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