1/*	$NetBSD$	*/
2
3/*
4 * Copyright (c) 1997-2014 Erez Zadok
5 * Copyright (c) 1990 Jan-Simon Pendry
6 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
7 * Copyright (c) 1990 The Regents of the University of California.
8 * All rights reserved.
9 *
10 * This code is derived from software contributed to Berkeley by
11 * Jan-Simon Pendry at Imperial College, London.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 *    notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 *    notice, this list of conditions and the following disclaimer in the
20 *    documentation and/or other materials provided with the distribution.
21 * 3. Neither the name of the University nor the names of its contributors
22 *    may be used to endorse or promote products derived from this software
23 *    without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 *
37 *
38 * File: am-utils/mk-amd-map/mk-amd-map.c
39 */
40
41/*
42 * Convert a file map into an ndbm map
43 */
44
45#ifdef HAVE_CONFIG_H
46# include <config.h>
47#endif /* HAVE_CONFIG_H */
48#include <am_defs.h>
49
50/* (libdb version 2) uses .db extensions but an old dbm API */
51/* check for libgdbm to distinguish it from linux systems */
52#if defined(DBM_SUFFIX) && !defined(HAVE_LIBGDBM)
53# define HAVE_DB_SUFFIX
54#endif /* not defined(DBM_SUFFIX) && !defined(HAVE_LIBGDBM) */
55
56#ifdef HAVE_MAP_NDBM
57
58static int
59store_data(voidp db, char *k, char *v)
60{
61  datum key, val;
62
63  key.dptr = k;
64  val.dptr = v;
65  key.dsize = strlen(k) + 1;
66  val.dsize = strlen(v) + 1;
67  return dbm_store((DBM *) db, key, val, DBM_INSERT);
68}
69
70
71/*
72 * Read one line from file.
73 */
74static int
75read_line(char *buf, int size, FILE *fp)
76{
77  int done = 0;
78
79  do {
80    while (fgets(buf, size, fp)) {
81      int len = strlen(buf);
82
83      done += len;
84      if (len > 1 && buf[len - 2] == '\\' && buf[len - 1] == '\n') {
85	int ch;
86	buf += len - 2;
87	size -= len - 2;
88	*buf = '\n';
89	buf[1] = '\0';
90
91	/*
92	 * Skip leading white space on next line
93	 */
94	while ((ch = getc(fp)) != EOF && isascii((unsigned char)ch) && isspace((unsigned char)ch)) ;
95	(void) ungetc(ch, fp);
96      } else {
97	return done;
98      }
99    }
100  } while (size > 0 && !feof(fp));
101
102  return done;
103}
104
105
106/*
107 * Read through a map.
108 */
109static int
110read_file(FILE *fp, char *map, voidp db)
111{
112  char key_val[2048];
113  int chuck = 0;
114  int line_no = 0;
115  int errs = 0;
116
117  while (read_line(key_val, 2048, fp)) {
118    char *kp;
119    char *cp;
120    char *hash;
121    int len = strlen(key_val);
122
123    line_no++;
124
125    /*
126     * Make sure we got the whole line
127     */
128    if (key_val[len - 1] != '\n') {
129      fprintf(stderr, "line %d in \"%s\" is too long", line_no, map);
130      chuck = 1;
131    } else {
132      key_val[len - 1] = '\0';
133    }
134
135    /*
136     * Strip comments
137     */
138    hash = strchr(key_val, '#');
139    if (hash)
140      *hash = '\0';
141
142    /*
143     * Find start of key
144     */
145    for (kp = key_val; *kp && isascii((unsigned char)*kp) && isspace((unsigned char)*kp); kp++) ;
146
147    /*
148     * Ignore blank lines
149     */
150    if (!*kp)
151      goto again;
152
153    /*
154     * Find end of key
155     */
156    for (cp = kp; *cp && (!isascii((unsigned char)*cp) || !isspace((unsigned char)*cp)); cp++) ;
157
158    /*
159     * Check whether key matches, or whether
160     * the entry is a wildcard entry.
161     */
162    if (*cp)
163      *cp++ = '\0';
164    while (*cp && isascii((unsigned char)*cp) && isspace((unsigned char)*cp))
165      cp++;
166    if (*kp == '+') {
167      fprintf(stderr, "Can't interpolate %s\n", kp);
168      errs++;
169    } else if (*cp) {
170      if (db) {
171	if (store_data(db, kp, cp) < 0) {
172	  fprintf(stderr, "Could store %s -> %s\n", kp, cp);
173	  errs++;
174	}
175      } else {
176	printf("%s\t%s\n", kp, cp);
177      }
178    } else {
179      fprintf(stderr, "%s: line %d has no value field", map, line_no);
180      errs++;
181    }
182
183  again:
184    /*
185     * If the last read didn't get a whole line then
186     * throw away the remainder before continuing...
187     */
188    if (chuck) {
189      while (fgets(key_val, sizeof(key_val), fp) &&
190	     !strchr(key_val, '\n')) ;
191      chuck = 0;
192    }
193  }
194  return errs;
195}
196
197
198static int
199remove_file(char *f)
200{
201  if (unlink(f) < 0 && errno != ENOENT)
202    return -1;
203
204  return 0;
205}
206
207
208int
209main(int argc, char *argv[])
210{
211  FILE *mapf;			/* the input file to read from */
212  int error;
213  char *mapsrc;
214  DBM *db = NULL;
215  static char maptmp[] = "dbmXXXXXX";
216#ifdef HAVE_DB_SUFFIX
217  char maptdb[16];
218  char *map_name_db = (char *) NULL;
219#else /* not HAVE_DB_SUFFIX */
220  char maptpag[16], maptdir[16];
221  char *map_name_pag = (char *) NULL, *map_name_dir = (char *) NULL;
222#endif /* not HAVE_DB_SUFFIX */
223  size_t l = 0;
224  char *sl;
225  int printit = 0;
226  int usage = 0;
227  int ch;
228  extern int optind;
229
230  /* test options */
231  while ((ch = getopt(argc, argv, "p")) != -1)
232    switch (ch) {
233    case 'p':
234      printit = 1;
235      break;
236    default:
237      usage++;
238      break;
239    }
240
241  if (usage || optind != (argc - 1)) {
242    fputs("Usage: mk-amd-map [-p] file-map\n", stderr);
243    exit(1);
244  }
245  mapsrc = argv[optind];
246
247  /* test if can get to the map directory */
248  sl = strrchr(mapsrc, '/');
249  if (sl) {
250    *sl = '\0';
251    if (chdir(mapsrc) < 0) {
252      fputs("Can't chdir to ", stderr);
253      perror(mapsrc);
254      exit(1);
255    }
256    mapsrc = sl + 1;
257  }
258
259  /* open source file */
260  mapf = fopen(mapsrc, "r");
261  if (!mapf) {
262    fprintf(stderr, "cannot open source file ");
263    perror(mapsrc);
264    exit(1);
265  }
266
267#ifndef DEBUG
268  signal(SIGINT, SIG_IGN);
269#endif /* DEBUG */
270
271  if (!printit) {
272    /* enough space for ".db" or ".pag" or ".dir" appended */
273    l = strlen(mapsrc) + 5;
274#ifdef HAVE_DB_SUFFIX
275    map_name_db = (char *) malloc(l);
276    error = (map_name_db == NULL);
277#else /* not HAVE_DB_SUFFIX */
278    map_name_pag = (char *) malloc(l);
279    map_name_dir = (char *) malloc(l);
280    error = (map_name_pag == NULL || map_name_dir == NULL);
281#endif /* not HAVE_DB_SUFFIX */
282    if (error) {
283      perror("mk-amd-map: malloc");
284      exit(1);
285    }
286
287#ifdef HAVE_MKSTEMP
288    {
289      /*
290       * XXX: hack to avoid compiler complaints about mktemp not being
291       * secure, since we have to do a dbm_open on this anyway.  So use
292       * mkstemp if you can, and then close the fd, but we get a safe
293       * and unique file name.
294       */
295      int dummyfd;
296      dummyfd = mkstemp(maptmp);
297      if (dummyfd >= 0)
298	close(dummyfd);
299    }
300#else /* not HAVE_MKSTEMP */
301    mktemp(maptmp);
302#endif /* not HAVE_MKSTEMP */
303
304    /* remove existing temps (if any) */
305#ifdef HAVE_DB_SUFFIX
306    xsnprintf(maptdb, sizeof(maptdb), "%s.db", maptmp);
307    if (remove_file(maptdb) < 0) {
308      fprintf(stderr, "Can't remove existing temporary file; ");
309      perror(maptdb);
310      exit(1);
311    }
312#else /* not HAVE_DB_SUFFIX */
313    xsnprintf(maptpag, sizeof(maptpag), "%s.pag", maptmp);
314    xsnprintf(maptdir, sizeof(maptdir), "%s.dir", maptmp);
315    if (remove_file(maptpag) < 0 || remove_file(maptdir) < 0) {
316      fprintf(stderr, "Can't remove existing temporary files; %s and ", maptpag);
317      perror(maptdir);
318      exit(1);
319    }
320#endif /* not HAVE_DB_SUFFIX */
321
322    db = dbm_open(maptmp, O_RDWR|O_CREAT|O_EXCL, 0444);
323    if (!db) {
324      fprintf(stderr, "cannot initialize temporary database: %s", maptmp);
325      exit(1);
326    }
327  }
328
329  /* print db to stdout or to temp database */
330  error = read_file(mapf, mapsrc, db);
331  fclose(mapf);
332  if (error) {
333    if (printit)
334      fprintf(stderr, "Error reading source file  %s\n", mapsrc);
335    else
336      fprintf(stderr, "Error creating database map for %s\n", mapsrc);
337    exit(1);
338  }
339
340  if (printit)
341    exit(0);			/* nothing more to do */
342
343  /* if gets here, we wrote to a database */
344
345  dbm_close(db);
346  /* all went well */
347
348#ifdef HAVE_DB_SUFFIX
349  /* sizeof(map_name_db) is malloc'ed above */
350  xsnprintf(map_name_db, l, "%s.db", mapsrc);
351  if (rename(maptdb, map_name_db) < 0) {
352    fprintf(stderr, "Couldn't rename %s to ", maptdb);
353    perror(map_name_db);
354    /* Throw away the temporary map */
355    unlink(maptdb);
356    exit(1);
357  }
358#else /* not HAVE_DB_SUFFIX */
359  /* sizeof(map_name_{pag,dir}) are malloc'ed above */
360  xsnprintf(map_name_pag, l, "%s.pag", mapsrc);
361  xsnprintf(map_name_dir, l, "%s.dir", mapsrc);
362  if (rename(maptpag, map_name_pag) < 0) {
363    fprintf(stderr, "Couldn't rename %s to ", maptpag);
364    perror(map_name_pag);
365    /* Throw away the temporary map */
366    unlink(maptpag);
367    unlink(maptdir);
368    exit(1);
369  }
370  if (rename(maptdir, map_name_dir) < 0) {
371    fprintf(stderr, "Couldn't rename %s to ", maptdir);
372    perror(map_name_dir);
373    /* remove the (presumably bad) .pag file */
374    unlink(map_name_pag);
375    /* throw away remaining part of original map */
376    unlink(map_name_dir);
377    /* throw away the temporary map */
378    unlink(maptdir);
379    fprintf(stderr, "WARNING: existing map \"%s.{dir,pag}\" destroyed\n",
380	    mapsrc);
381    exit(1);
382  }
383#endif /* not HAVE_DB_SUFFIX */
384
385  exit(0);
386}
387
388#else /* not HAVE_MAP_NDBM */
389
390int
391main()
392{
393  fputs("mk-amd-map: This system does not support hashed database files\n", stderr);
394  exit(1);
395}
396
397#endif /* not HAVE_MAP_NDBM */
398