update changelog
[alioth/cvs.git] / src / myndbm.c
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 */