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