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