]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/cvs/src/myndbm.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / contrib / cvs / 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 <assert.h>
23 #include "cvs.h"
24 #include "getline.h"
25
26 #ifdef MY_NDBM
27 # ifndef O_ACCMODE
28 #   define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR)
29 # endif /* defined O_ACCMODE */
30
31 static void mydbm_load_file PROTO ((FILE *, List *, char *));
32
33 /* Returns NULL on error in which case errno has been set to indicate
34    the error.  Can also call error() itself.  */
35 /* ARGSUSED */
36 DBM *
37 mydbm_open (file, flags, mode)
38     char *file;
39     int flags;
40     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 ((DBM *) 0);
49
50     db = (DBM *) 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", file);
60     }
61     return (db);
62 }
63
64 static int write_item PROTO ((Node *, void *));
65
66 static int
67 write_item (node, data)
68     Node *node;
69     void *data;
70 {
71     FILE *fp = (FILE *)data;
72     fputs (node->key, fp);
73     fputs (" ", fp);
74     fputs (node->data, fp);
75     fputs ("\012", fp);
76     return 0;
77 }
78
79 void
80 mydbm_close (db)
81     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, (void *)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 ((char *) db);
96 }
97
98 datum
99 mydbm_fetch (db, key)
100     DBM *db;
101     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 = (char *) NULL;
121         val.dsize = 0;
122     }
123     free (s);
124     return (val);
125 }
126
127 datum
128 mydbm_firstkey (db)
129     DBM *db;
130 {
131     Node *head, *p;
132     datum key;
133
134     head = db->dbm_list->list;
135     p = head->next;
136     if (p != head)
137     {
138         key.dptr = p->key;
139         key.dsize = strlen (p->key);
140     }
141     else
142     {
143         key.dptr = (char *) NULL;
144         key.dsize = 0;
145     }
146     db->dbm_next = p->next;
147     return (key);
148 }
149
150 datum
151 mydbm_nextkey (db)
152     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 = (char *) NULL;
167         key.dsize = 0;
168     }
169     db->dbm_next = p->next;
170     return (key);
171 }
172
173 /* Note: only updates the in-memory copy, which is written out at
174    mydbm_close time.  Note: Also differs from DBM in that on duplication,
175    it gives a warning, rather than either DBM_INSERT or DBM_REPLACE
176    behavior.  */
177 int
178 mydbm_store (db, key, value, flags)
179     DBM *db;
180     datum key;
181     datum value;
182     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 static void
208 mydbm_load_file (fp, list, filename)
209     FILE *fp;
210     List *list;
211     char *filename;     /* Used in error messages. */
212 {
213     char *line = NULL;
214     size_t line_size;
215     char *value;
216     size_t value_allocated;
217     char *cp, *vp;
218     int cont;
219     int line_length;
220     int line_num;
221
222     value_allocated = 1;
223     value = xmalloc (value_allocated);
224
225     cont = 0;
226     line_num=0;
227     while ((line_length = 
228             getstr (&line, &line_size, fp, '\012', 0, GETLINE_NO_LIMIT)) >= 0)
229     {
230         line_num++;
231         if (line_length > 0 && line[line_length - 1] == '\012')
232         {
233             /* Strip the newline.  */
234             --line_length;
235             line[line_length] = '\0';
236         }
237         if (line_length > 0 && line[line_length - 1] == '\015')
238         {
239             /* If the file (e.g. modules) was written on an NT box, it will
240                contain CRLF at the ends of lines.  Strip them (we can't do
241                this by opening the file in text mode because we might be
242                running on unix).  */
243             --line_length;
244             line[line_length] = '\0';
245         }
246
247         /*
248          * Add the line to the value, at the end if this is a continuation
249          * line; otherwise at the beginning, but only after any trailing
250          * backslash is removed.
251          */
252         if (!cont)
253             value[0] = '\0';
254
255         /*
256          * See if the line we read is a continuation line, and strip the
257          * backslash if so.
258          */
259         if (line_length > 0)
260             cp = &line[line_length - 1];
261         else
262             cp = line;
263         if (*cp == '\\')
264         {
265             cont = 1;
266             *cp = '\0';
267             --line_length;
268         }
269         else
270         {
271             cont = 0;
272         }
273         expand_string (&value,
274                        &value_allocated,
275                        strlen (value) + line_length + 5);
276         strcat (value, line);
277
278         if (value[0] == '#')
279             continue;                   /* comment line */
280         vp = value;
281         while (*vp && isspace ((unsigned char) *vp))
282             vp++;
283         if (*vp == '\0')
284             continue;                   /* empty line */
285
286         /*
287          * If this was not a continuation line, add the entry to the database
288          */
289         if (!cont)
290         {
291             Node *p = getnode ();
292             char *kp;
293
294             kp = vp;
295             while (*vp && !isspace ((unsigned char) *vp))
296                 vp++;
297             if (*vp)
298                 *vp++ = '\0';           /* NULL terminate the key */
299             p->type = NDBMNODE;
300             p->key = xstrdup (kp);
301             while (*vp && isspace ((unsigned char) *vp))
302                 vp++;                   /* skip whitespace to value */
303             if (*vp == '\0')
304             {
305                 if (!really_quiet)
306                     error (0, 0,
307                         "warning: NULL value for key `%s' at line %d of `%s'",
308                         p->key, line_num, filename);
309                 freenode (p);
310                 continue;
311             }
312             p->data = xstrdup (vp);
313             if (addnode (list, p) == -1)
314             {
315                 if (!really_quiet)
316                     error (0, 0,
317                         "duplicate key found for `%s' at line %d of `%s'",
318                         p->key, line_num, filename);
319                 freenode (p);
320             }
321         }
322     }
323     if (line_length < 0 && !feof (fp))
324         /* FIXME: should give the name of the file.  */
325         error (0, errno, "cannot read file in mydbm_load_file");
326
327     free (line);
328     free (value);
329 }
330
331 #endif                          /* MY_NDBM */