1/*
2 * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
3 *
4 * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
5 *                                  and others.
6 *
7 * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
8 * Portions Copyright (C) 1989-1992, Brian Berliner
9 *
10 * You may distribute under the terms of the GNU General Public License as
11 * specified in the README file that comes with the CVS source distribution.
12 *
13 * A simple ndbm-emulator for CVS.  It parses a text file of the format:
14 *
15 * key	value
16 *
17 * at dbm_open time, and loads the entire file into memory.  As such, it is
18 * probably only good for fairly small modules files.  Ours is about 30K in
19 * size, and this code works fine.
20 */
21#include <sys/cdefs.h>
22__RCSID("$NetBSD: myndbm.c,v 1.2 2016/05/17 14:00:09 christos Exp $");
23
24#include "cvs.h"
25
26#include "getdelim.h"
27#include "getline.h"
28
29#ifdef MY_NDBM
30# ifndef O_ACCMODE
31#   define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR)
32# endif /* defined O_ACCMODE */
33
34static void mydbm_load_file (FILE *, List *, char *);
35
36/* Returns NULL on error in which case errno has been set to indicate
37   the error.  Can also call error() itself.  */
38/* ARGSUSED */
39DBM *
40mydbm_open (char *file, int flags, int mode)
41{
42    FILE *fp;
43    DBM *db;
44
45    fp = CVS_FOPEN (file, (flags & O_ACCMODE) != O_RDONLY
46			  ?  FOPEN_BINARY_READWRITE : FOPEN_BINARY_READ);
47    if (fp == NULL && !(existence_error (errno) && (flags & O_CREAT)))
48	return NULL;
49
50    db = xmalloc (sizeof (*db));
51    db->dbm_list = getlist ();
52    db->modified = 0;
53    db->name = xstrdup (file);
54
55    if (fp != NULL)
56    {
57	mydbm_load_file (fp, db->dbm_list, file);
58	if (fclose (fp) < 0)
59	    error (0, errno, "cannot close %s",
60		   primary_root_inverse_translate (file));
61    }
62    return db;
63}
64
65
66
67static int
68write_item (Node *node, void *data)
69{
70    FILE *fp = data;
71    fputs (node->key, fp);
72    fputs (" ", fp);
73    fputs (node->data, fp);
74    fputs ("\012", fp);
75    return 0;
76}
77
78
79
80void
81mydbm_close (DBM *db)
82{
83    if (db->modified)
84    {
85	FILE *fp;
86	fp = CVS_FOPEN (db->name, FOPEN_BINARY_WRITE);
87	if (fp == NULL)
88	    error (1, errno, "cannot write %s", db->name);
89	walklist (db->dbm_list, write_item, fp);
90	if (fclose (fp) < 0)
91	    error (0, errno, "cannot close %s", db->name);
92    }
93    free (db->name);
94    dellist (&db->dbm_list);
95    free (db);
96}
97
98
99
100datum
101mydbm_fetch (DBM *db, datum key)
102{
103    Node *p;
104    char *s;
105    datum val;
106
107    /* make sure it's null-terminated */
108    s = xmalloc (key.dsize + 1);
109    (void) strncpy (s, key.dptr, key.dsize);
110    s[key.dsize] = '\0';
111
112    p = findnode (db->dbm_list, s);
113    if (p)
114    {
115	val.dptr = p->data;
116	val.dsize = strlen (p->data);
117    }
118    else
119    {
120	val.dptr = NULL;
121	val.dsize = 0;
122    }
123    free (s);
124    return val;
125}
126
127
128
129datum
130mydbm_firstkey (DBM *db)
131{
132    Node *head, *p;
133    datum key;
134
135    head = db->dbm_list->list;
136    p = head->next;
137    if (p != head)
138    {
139	key.dptr = p->key;
140	key.dsize = strlen (p->key);
141    }
142    else
143    {
144	key.dptr = NULL;
145	key.dsize = 0;
146    }
147    db->dbm_next = p->next;
148    return key;
149}
150
151
152
153datum
154mydbm_nextkey (DBM *db)
155{
156    Node *head, *p;
157    datum key;
158
159    head = db->dbm_list->list;
160    p = db->dbm_next;
161    if (p != head)
162    {
163	key.dptr = p->key;
164	key.dsize = strlen (p->key);
165    }
166    else
167    {
168	key.dptr = NULL;
169	key.dsize = 0;
170    }
171    db->dbm_next = p->next;
172    return key;
173}
174
175
176
177/* Note: only updates the in-memory copy, which is written out at
178   mydbm_close time.  Note: Also differs from DBM in that on duplication,
179   it gives a warning, rather than either DBM_INSERT or DBM_REPLACE
180   behavior.  */
181int
182mydbm_store (DBM *db, datum key, datum value, int flags)
183{
184    Node *node;
185
186    node = getnode ();
187    node->type = NDBMNODE;
188
189    node->key = xmalloc (key.dsize + 1);
190    *node->key = '\0';
191    strncat (node->key, key.dptr, key.dsize);
192
193    node->data = xmalloc (value.dsize + 1);
194    *(char *)node->data = '\0';
195    strncat (node->data, value.dptr, value.dsize);
196
197    db->modified = 1;
198    if (addnode (db->dbm_list, node) == -1)
199    {
200	error (0, 0, "attempt to insert duplicate key `%s'", node->key);
201	freenode (node);
202	return 0;
203    }
204    return 0;
205}
206
207
208
209/* Load a DBM file.
210 *
211 * INPUTS
212 *   filename		Used in error messages.
213 */
214static void
215mydbm_load_file (FILE *fp, List *list, char *filename)
216{
217    char *line = NULL;
218    size_t line_size;
219    char *value;
220    size_t value_allocated;
221    char *cp, *vp;
222    int cont;
223    int line_length;
224    int line_num;
225
226    value_allocated = 1;
227    value = xmalloc (value_allocated);
228
229    cont = 0;
230    line_num=0;
231    while ((line_length = getdelim (&line, &line_size, '\012', fp)) >= 0)
232    {
233	line_num++;
234	if (line_length > 0 && line[line_length - 1] == '\012')
235	{
236	    /* Strip the newline.  */
237	    --line_length;
238	    line[line_length] = '\0';
239	}
240	if (line_length > 0 && line[line_length - 1] == '\015')
241	{
242	    /* If the file (e.g. modules) was written on an NT box, it will
243	       contain CRLF at the ends of lines.  Strip them (we can't do
244	       this by opening the file in text mode because we might be
245	       running on unix).  */
246	    --line_length;
247	    line[line_length] = '\0';
248	}
249
250	/*
251	 * Add the line to the value, at the end if this is a continuation
252	 * line; otherwise at the beginning, but only after any trailing
253	 * backslash is removed.
254	 */
255	if (!cont)
256	    value[0] = '\0';
257
258	/*
259	 * See if the line we read is a continuation line, and strip the
260	 * backslash if so.
261	 */
262	if (line_length > 0)
263	    cp = &line[line_length - 1];
264	else
265	    cp = line;
266	if (*cp == '\\')
267	{
268	    cont = 1;
269	    *cp = '\0';
270	    --line_length;
271	}
272	else
273	{
274	    cont = 0;
275	}
276	expand_string (&value,
277		       &value_allocated,
278		       strlen (value) + line_length + 5);
279	strcat (value, line);
280
281	if (value[0] == '#')
282	    continue;			/* comment line */
283	vp = value;
284	while (*vp && isspace ((unsigned char) *vp))
285	    vp++;
286	if (*vp == '\0')
287	    continue;			/* empty line */
288
289	/*
290	 * If this was not a continuation line, add the entry to the database
291	 */
292	if (!cont)
293	{
294	    Node *p = getnode ();
295	    char *kp;
296
297	    kp = vp;
298	    while (*vp && !isspace ((unsigned char) *vp))
299		vp++;
300	    if (*vp)
301		*vp++ = '\0';		/* NULL terminate the key */
302	    p->type = NDBMNODE;
303	    p->key = xstrdup (kp);
304	    while (*vp && isspace ((unsigned char) *vp))
305		vp++;			/* skip whitespace to value */
306	    if (*vp == '\0')
307	    {
308		if (!really_quiet)
309		    error (0, 0,
310			"warning: NULL value for key `%s' at line %d of `%s'",
311			p->key, line_num,
312			primary_root_inverse_translate (filename));
313		freenode (p);
314		continue;
315	    }
316	    p->data = xstrdup (vp);
317	    if (addnode (list, p) == -1)
318	    {
319		if (!really_quiet)
320		    error (0, 0,
321			"duplicate key found for `%s' at line %d of `%s'",
322			p->key, line_num,
323			primary_root_inverse_translate (filename));
324		freenode (p);
325	    }
326	}
327    }
328    if (line_length < 0 && !feof (fp))
329	error (0, errno, "cannot read file `%s' in mydbm_load_file",
330	       primary_root_inverse_translate (filename));
331
332    free (line);
333    free (value);
334}
335
336#endif				/* MY_NDBM */
337