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