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 *
mydbm_open(file,flags,mode)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
write_item(node,data)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
mydbm_close(db)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
mydbm_fetch(db,key)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
mydbm_firstkey(db)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
mydbm_nextkey(db)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
mydbm_store(db,key,value,flags)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
mydbm_load_file(fp,list)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