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 * 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 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 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 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 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 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 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 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