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