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