xref: /netbsd-src/external/gpl2/xcvs/dist/src/myndbm.c (revision 5a6c14c844c4c665da5632061aebde7bb2cb5766)
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 
34 static 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 */
39 DBM *
mydbm_open(char * file,int flags,int mode)40 mydbm_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 
67 static int
write_item(Node * node,void * data)68 write_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 
80 void
mydbm_close(DBM * db)81 mydbm_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 
100 datum
mydbm_fetch(DBM * db,datum key)101 mydbm_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 
129 datum
mydbm_firstkey(DBM * db)130 mydbm_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 
153 datum
mydbm_nextkey(DBM * db)154 mydbm_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.  */
181 int
mydbm_store(DBM * db,datum key,datum value,int flags)182 mydbm_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  */
214 static void
mydbm_load_file(FILE * fp,List * list,char * filename)215 mydbm_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