186d7f5d3SJohn Marino /*
286d7f5d3SJohn Marino * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
386d7f5d3SJohn Marino *
486d7f5d3SJohn Marino * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
586d7f5d3SJohn Marino * and others.
686d7f5d3SJohn Marino *
786d7f5d3SJohn Marino * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
886d7f5d3SJohn Marino * Portions Copyright (C) 1989-1992, Brian Berliner
986d7f5d3SJohn Marino *
1086d7f5d3SJohn Marino * You may distribute under the terms of the GNU General Public License as
1186d7f5d3SJohn Marino * specified in the README file that comes with the CVS source distribution.
1286d7f5d3SJohn Marino *
1386d7f5d3SJohn Marino * A simple ndbm-emulator for CVS. It parses a text file of the format:
1486d7f5d3SJohn Marino *
1586d7f5d3SJohn Marino * key value
1686d7f5d3SJohn Marino *
1786d7f5d3SJohn Marino * at dbm_open time, and loads the entire file into memory. As such, it is
1886d7f5d3SJohn Marino * probably only good for fairly small modules files. Ours is about 30K in
1986d7f5d3SJohn Marino * size, and this code works fine.
2086d7f5d3SJohn Marino */
2186d7f5d3SJohn Marino
2286d7f5d3SJohn Marino #include "cvs.h"
2386d7f5d3SJohn Marino
2486d7f5d3SJohn Marino #include "getdelim.h"
2586d7f5d3SJohn Marino #include "getline.h"
2686d7f5d3SJohn Marino
2786d7f5d3SJohn Marino #ifdef MY_NDBM
2886d7f5d3SJohn Marino # ifndef O_ACCMODE
2986d7f5d3SJohn Marino # define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR)
3086d7f5d3SJohn Marino # endif /* defined O_ACCMODE */
3186d7f5d3SJohn Marino
3286d7f5d3SJohn Marino static void mydbm_load_file (FILE *, List *, char *);
3386d7f5d3SJohn Marino
3486d7f5d3SJohn Marino /* Returns NULL on error in which case errno has been set to indicate
3586d7f5d3SJohn Marino the error. Can also call error() itself. */
3686d7f5d3SJohn Marino /* ARGSUSED */
3786d7f5d3SJohn Marino DBM *
mydbm_open(char * file,int flags,int mode)3886d7f5d3SJohn Marino mydbm_open (char *file, int flags, int mode)
3986d7f5d3SJohn Marino {
4086d7f5d3SJohn Marino FILE *fp;
4186d7f5d3SJohn Marino DBM *db;
4286d7f5d3SJohn Marino
4386d7f5d3SJohn Marino fp = CVS_FOPEN (file, (flags & O_ACCMODE) != O_RDONLY
4486d7f5d3SJohn Marino ? FOPEN_BINARY_READWRITE : FOPEN_BINARY_READ);
4586d7f5d3SJohn Marino if (fp == NULL && !(existence_error (errno) && (flags & O_CREAT)))
4686d7f5d3SJohn Marino return NULL;
4786d7f5d3SJohn Marino
4886d7f5d3SJohn Marino db = xmalloc (sizeof (*db));
4986d7f5d3SJohn Marino db->dbm_list = getlist ();
5086d7f5d3SJohn Marino db->modified = 0;
5186d7f5d3SJohn Marino db->name = xstrdup (file);
5286d7f5d3SJohn Marino
5386d7f5d3SJohn Marino if (fp != NULL)
5486d7f5d3SJohn Marino {
5586d7f5d3SJohn Marino mydbm_load_file (fp, db->dbm_list, file);
5686d7f5d3SJohn Marino if (fclose (fp) < 0)
5786d7f5d3SJohn Marino error (0, errno, "cannot close %s",
5886d7f5d3SJohn Marino primary_root_inverse_translate (file));
5986d7f5d3SJohn Marino }
6086d7f5d3SJohn Marino return db;
6186d7f5d3SJohn Marino }
6286d7f5d3SJohn Marino
6386d7f5d3SJohn Marino
6486d7f5d3SJohn Marino
6586d7f5d3SJohn Marino static int
write_item(Node * node,void * data)6686d7f5d3SJohn Marino write_item (Node *node, void *data)
6786d7f5d3SJohn Marino {
6886d7f5d3SJohn Marino FILE *fp = data;
6986d7f5d3SJohn Marino fputs (node->key, fp);
7086d7f5d3SJohn Marino fputs (" ", fp);
7186d7f5d3SJohn Marino fputs (node->data, fp);
7286d7f5d3SJohn Marino fputs ("\012", fp);
7386d7f5d3SJohn Marino return 0;
7486d7f5d3SJohn Marino }
7586d7f5d3SJohn Marino
7686d7f5d3SJohn Marino
7786d7f5d3SJohn Marino
7886d7f5d3SJohn Marino void
mydbm_close(DBM * db)7986d7f5d3SJohn Marino mydbm_close (DBM *db)
8086d7f5d3SJohn Marino {
8186d7f5d3SJohn Marino if (db->modified)
8286d7f5d3SJohn Marino {
8386d7f5d3SJohn Marino FILE *fp;
8486d7f5d3SJohn Marino fp = CVS_FOPEN (db->name, FOPEN_BINARY_WRITE);
8586d7f5d3SJohn Marino if (fp == NULL)
8686d7f5d3SJohn Marino error (1, errno, "cannot write %s", db->name);
8786d7f5d3SJohn Marino walklist (db->dbm_list, write_item, fp);
8886d7f5d3SJohn Marino if (fclose (fp) < 0)
8986d7f5d3SJohn Marino error (0, errno, "cannot close %s", db->name);
9086d7f5d3SJohn Marino }
9186d7f5d3SJohn Marino free (db->name);
9286d7f5d3SJohn Marino dellist (&db->dbm_list);
9386d7f5d3SJohn Marino free (db);
9486d7f5d3SJohn Marino }
9586d7f5d3SJohn Marino
9686d7f5d3SJohn Marino
9786d7f5d3SJohn Marino
9886d7f5d3SJohn Marino datum
mydbm_fetch(DBM * db,datum key)9986d7f5d3SJohn Marino mydbm_fetch (DBM *db, datum key)
10086d7f5d3SJohn Marino {
10186d7f5d3SJohn Marino Node *p;
10286d7f5d3SJohn Marino char *s;
10386d7f5d3SJohn Marino datum val;
10486d7f5d3SJohn Marino
10586d7f5d3SJohn Marino /* make sure it's null-terminated */
10686d7f5d3SJohn Marino s = xmalloc (key.dsize + 1);
10786d7f5d3SJohn Marino (void) strncpy (s, key.dptr, key.dsize);
10886d7f5d3SJohn Marino s[key.dsize] = '\0';
10986d7f5d3SJohn Marino
11086d7f5d3SJohn Marino p = findnode (db->dbm_list, s);
11186d7f5d3SJohn Marino if (p)
11286d7f5d3SJohn Marino {
11386d7f5d3SJohn Marino val.dptr = p->data;
11486d7f5d3SJohn Marino val.dsize = strlen (p->data);
11586d7f5d3SJohn Marino }
11686d7f5d3SJohn Marino else
11786d7f5d3SJohn Marino {
11886d7f5d3SJohn Marino val.dptr = NULL;
11986d7f5d3SJohn Marino val.dsize = 0;
12086d7f5d3SJohn Marino }
12186d7f5d3SJohn Marino free (s);
12286d7f5d3SJohn Marino return val;
12386d7f5d3SJohn Marino }
12486d7f5d3SJohn Marino
12586d7f5d3SJohn Marino
12686d7f5d3SJohn Marino
12786d7f5d3SJohn Marino datum
mydbm_firstkey(DBM * db)12886d7f5d3SJohn Marino mydbm_firstkey (DBM *db)
12986d7f5d3SJohn Marino {
13086d7f5d3SJohn Marino Node *head, *p;
13186d7f5d3SJohn Marino datum key;
13286d7f5d3SJohn Marino
13386d7f5d3SJohn Marino head = db->dbm_list->list;
13486d7f5d3SJohn Marino p = head->next;
13586d7f5d3SJohn Marino if (p != head)
13686d7f5d3SJohn Marino {
13786d7f5d3SJohn Marino key.dptr = p->key;
13886d7f5d3SJohn Marino key.dsize = strlen (p->key);
13986d7f5d3SJohn Marino }
14086d7f5d3SJohn Marino else
14186d7f5d3SJohn Marino {
14286d7f5d3SJohn Marino key.dptr = NULL;
14386d7f5d3SJohn Marino key.dsize = 0;
14486d7f5d3SJohn Marino }
14586d7f5d3SJohn Marino db->dbm_next = p->next;
14686d7f5d3SJohn Marino return key;
14786d7f5d3SJohn Marino }
14886d7f5d3SJohn Marino
14986d7f5d3SJohn Marino
15086d7f5d3SJohn Marino
15186d7f5d3SJohn Marino datum
mydbm_nextkey(DBM * db)15286d7f5d3SJohn Marino mydbm_nextkey (DBM *db)
15386d7f5d3SJohn Marino {
15486d7f5d3SJohn Marino Node *head, *p;
15586d7f5d3SJohn Marino datum key;
15686d7f5d3SJohn Marino
15786d7f5d3SJohn Marino head = db->dbm_list->list;
15886d7f5d3SJohn Marino p = db->dbm_next;
15986d7f5d3SJohn Marino if (p != head)
16086d7f5d3SJohn Marino {
16186d7f5d3SJohn Marino key.dptr = p->key;
16286d7f5d3SJohn Marino key.dsize = strlen (p->key);
16386d7f5d3SJohn Marino }
16486d7f5d3SJohn Marino else
16586d7f5d3SJohn Marino {
16686d7f5d3SJohn Marino key.dptr = NULL;
16786d7f5d3SJohn Marino key.dsize = 0;
16886d7f5d3SJohn Marino }
16986d7f5d3SJohn Marino db->dbm_next = p->next;
17086d7f5d3SJohn Marino return key;
17186d7f5d3SJohn Marino }
17286d7f5d3SJohn Marino
17386d7f5d3SJohn Marino
17486d7f5d3SJohn Marino
17586d7f5d3SJohn Marino /* Note: only updates the in-memory copy, which is written out at
17686d7f5d3SJohn Marino mydbm_close time. Note: Also differs from DBM in that on duplication,
17786d7f5d3SJohn Marino it gives a warning, rather than either DBM_INSERT or DBM_REPLACE
17886d7f5d3SJohn Marino behavior. */
17986d7f5d3SJohn Marino int
mydbm_store(DBM * db,datum key,datum value,int flags)18086d7f5d3SJohn Marino mydbm_store (DBM *db, datum key, datum value, int flags)
18186d7f5d3SJohn Marino {
18286d7f5d3SJohn Marino Node *node;
18386d7f5d3SJohn Marino
18486d7f5d3SJohn Marino node = getnode ();
18586d7f5d3SJohn Marino node->type = NDBMNODE;
18686d7f5d3SJohn Marino
18786d7f5d3SJohn Marino node->key = xmalloc (key.dsize + 1);
18886d7f5d3SJohn Marino *node->key = '\0';
18986d7f5d3SJohn Marino strncat (node->key, key.dptr, key.dsize);
19086d7f5d3SJohn Marino
19186d7f5d3SJohn Marino node->data = xmalloc (value.dsize + 1);
19286d7f5d3SJohn Marino *(char *)node->data = '\0';
19386d7f5d3SJohn Marino strncat (node->data, value.dptr, value.dsize);
19486d7f5d3SJohn Marino
19586d7f5d3SJohn Marino db->modified = 1;
19686d7f5d3SJohn Marino if (addnode (db->dbm_list, node) == -1)
19786d7f5d3SJohn Marino {
19886d7f5d3SJohn Marino error (0, 0, "attempt to insert duplicate key `%s'", node->key);
19986d7f5d3SJohn Marino freenode (node);
20086d7f5d3SJohn Marino return 0;
20186d7f5d3SJohn Marino }
20286d7f5d3SJohn Marino return 0;
20386d7f5d3SJohn Marino }
20486d7f5d3SJohn Marino
20586d7f5d3SJohn Marino
20686d7f5d3SJohn Marino
20786d7f5d3SJohn Marino /* Load a DBM file.
20886d7f5d3SJohn Marino *
20986d7f5d3SJohn Marino * INPUTS
21086d7f5d3SJohn Marino * filename Used in error messages.
21186d7f5d3SJohn Marino */
21286d7f5d3SJohn Marino static void
mydbm_load_file(FILE * fp,List * list,char * filename)21386d7f5d3SJohn Marino mydbm_load_file (FILE *fp, List *list, char *filename)
21486d7f5d3SJohn Marino {
21586d7f5d3SJohn Marino char *line = NULL;
21686d7f5d3SJohn Marino size_t line_size;
21786d7f5d3SJohn Marino char *value;
21886d7f5d3SJohn Marino size_t value_allocated;
21986d7f5d3SJohn Marino char *cp, *vp;
22086d7f5d3SJohn Marino int cont;
22186d7f5d3SJohn Marino int line_length;
22286d7f5d3SJohn Marino int line_num;
22386d7f5d3SJohn Marino
22486d7f5d3SJohn Marino value_allocated = 1;
22586d7f5d3SJohn Marino value = xmalloc (value_allocated);
22686d7f5d3SJohn Marino
22786d7f5d3SJohn Marino cont = 0;
22886d7f5d3SJohn Marino line_num=0;
22986d7f5d3SJohn Marino while ((line_length = getdelim (&line, &line_size, '\012', fp)) >= 0)
23086d7f5d3SJohn Marino {
23186d7f5d3SJohn Marino line_num++;
23286d7f5d3SJohn Marino if (line_length > 0 && line[line_length - 1] == '\012')
23386d7f5d3SJohn Marino {
23486d7f5d3SJohn Marino /* Strip the newline. */
23586d7f5d3SJohn Marino --line_length;
23686d7f5d3SJohn Marino line[line_length] = '\0';
23786d7f5d3SJohn Marino }
23886d7f5d3SJohn Marino if (line_length > 0 && line[line_length - 1] == '\015')
23986d7f5d3SJohn Marino {
24086d7f5d3SJohn Marino /* If the file (e.g. modules) was written on an NT box, it will
24186d7f5d3SJohn Marino contain CRLF at the ends of lines. Strip them (we can't do
24286d7f5d3SJohn Marino this by opening the file in text mode because we might be
24386d7f5d3SJohn Marino running on unix). */
24486d7f5d3SJohn Marino --line_length;
24586d7f5d3SJohn Marino line[line_length] = '\0';
24686d7f5d3SJohn Marino }
24786d7f5d3SJohn Marino
24886d7f5d3SJohn Marino /*
24986d7f5d3SJohn Marino * Add the line to the value, at the end if this is a continuation
25086d7f5d3SJohn Marino * line; otherwise at the beginning, but only after any trailing
25186d7f5d3SJohn Marino * backslash is removed.
25286d7f5d3SJohn Marino */
25386d7f5d3SJohn Marino if (!cont)
25486d7f5d3SJohn Marino value[0] = '\0';
25586d7f5d3SJohn Marino
25686d7f5d3SJohn Marino /*
25786d7f5d3SJohn Marino * See if the line we read is a continuation line, and strip the
25886d7f5d3SJohn Marino * backslash if so.
25986d7f5d3SJohn Marino */
26086d7f5d3SJohn Marino if (line_length > 0)
26186d7f5d3SJohn Marino cp = &line[line_length - 1];
26286d7f5d3SJohn Marino else
26386d7f5d3SJohn Marino cp = line;
26486d7f5d3SJohn Marino if (*cp == '\\')
26586d7f5d3SJohn Marino {
26686d7f5d3SJohn Marino cont = 1;
26786d7f5d3SJohn Marino *cp = '\0';
26886d7f5d3SJohn Marino --line_length;
26986d7f5d3SJohn Marino }
27086d7f5d3SJohn Marino else
27186d7f5d3SJohn Marino {
27286d7f5d3SJohn Marino cont = 0;
27386d7f5d3SJohn Marino }
27486d7f5d3SJohn Marino expand_string (&value,
27586d7f5d3SJohn Marino &value_allocated,
27686d7f5d3SJohn Marino strlen (value) + line_length + 5);
27786d7f5d3SJohn Marino strcat (value, line);
27886d7f5d3SJohn Marino
27986d7f5d3SJohn Marino if (value[0] == '#')
28086d7f5d3SJohn Marino continue; /* comment line */
28186d7f5d3SJohn Marino vp = value;
28286d7f5d3SJohn Marino while (*vp && isspace ((unsigned char) *vp))
28386d7f5d3SJohn Marino vp++;
28486d7f5d3SJohn Marino if (*vp == '\0')
28586d7f5d3SJohn Marino continue; /* empty line */
28686d7f5d3SJohn Marino
28786d7f5d3SJohn Marino /*
28886d7f5d3SJohn Marino * If this was not a continuation line, add the entry to the database
28986d7f5d3SJohn Marino */
29086d7f5d3SJohn Marino if (!cont)
29186d7f5d3SJohn Marino {
29286d7f5d3SJohn Marino Node *p = getnode ();
29386d7f5d3SJohn Marino char *kp;
29486d7f5d3SJohn Marino
29586d7f5d3SJohn Marino kp = vp;
29686d7f5d3SJohn Marino while (*vp && !isspace ((unsigned char) *vp))
29786d7f5d3SJohn Marino vp++;
29886d7f5d3SJohn Marino if (*vp)
29986d7f5d3SJohn Marino *vp++ = '\0'; /* NULL terminate the key */
30086d7f5d3SJohn Marino p->type = NDBMNODE;
30186d7f5d3SJohn Marino p->key = xstrdup (kp);
30286d7f5d3SJohn Marino while (*vp && isspace ((unsigned char) *vp))
30386d7f5d3SJohn Marino vp++; /* skip whitespace to value */
30486d7f5d3SJohn Marino if (*vp == '\0')
30586d7f5d3SJohn Marino {
30686d7f5d3SJohn Marino if (!really_quiet)
30786d7f5d3SJohn Marino error (0, 0,
30886d7f5d3SJohn Marino "warning: NULL value for key `%s' at line %d of `%s'",
30986d7f5d3SJohn Marino p->key, line_num,
31086d7f5d3SJohn Marino primary_root_inverse_translate (filename));
31186d7f5d3SJohn Marino freenode (p);
31286d7f5d3SJohn Marino continue;
31386d7f5d3SJohn Marino }
31486d7f5d3SJohn Marino p->data = xstrdup (vp);
31586d7f5d3SJohn Marino if (addnode (list, p) == -1)
31686d7f5d3SJohn Marino {
31786d7f5d3SJohn Marino if (!really_quiet)
31886d7f5d3SJohn Marino error (0, 0,
31986d7f5d3SJohn Marino "duplicate key found for `%s' at line %d of `%s'",
32086d7f5d3SJohn Marino p->key, line_num,
32186d7f5d3SJohn Marino primary_root_inverse_translate (filename));
32286d7f5d3SJohn Marino freenode (p);
32386d7f5d3SJohn Marino }
32486d7f5d3SJohn Marino }
32586d7f5d3SJohn Marino }
32686d7f5d3SJohn Marino if (line_length < 0 && !feof (fp))
32786d7f5d3SJohn Marino error (0, errno, "cannot read file `%s' in mydbm_load_file",
32886d7f5d3SJohn Marino primary_root_inverse_translate (filename));
32986d7f5d3SJohn Marino
33086d7f5d3SJohn Marino free (line);
33186d7f5d3SJohn Marino free (value);
33286d7f5d3SJohn Marino }
33386d7f5d3SJohn Marino
33486d7f5d3SJohn Marino #endif /* MY_NDBM */
335