1 /*
2  * Copyright (c) 1992 Eric P. Allman.
3  * Copyright (c) 1992, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 #ifndef lint
10 static char sccsid[] = "@(#)makemap.c	8.12 (Berkeley) 05/12/95";
11 #endif /* not lint */
12 
13 #include <stdio.h>
14 #include <sysexits.h>
15 #include <sys/types.h>
16 #include <ctype.h>
17 #include <string.h>
18 #include <sys/errno.h>
19 #ifndef ISC_UNIX
20 # include <sys/file.h>
21 #endif
22 #include "useful.h"
23 #include "conf.h"
24 
25 #ifdef NDBM
26 #include <ndbm.h>
27 #endif
28 
29 #ifdef NEWDB
30 #include <db.h>
31 #endif
32 
33 enum type { T_DBM, T_BTREE, T_HASH, T_ERR, T_UNKNOWN };
34 
35 union dbent
36 {
37 #ifdef NDBM
38 	datum	dbm;
39 #endif
40 #ifdef NEWDB
41 	DBT	db;
42 #endif
43 	struct
44 	{
45 		char	*data;
46 		size_t	size;
47 	} xx;
48 };
49 
50 #define BUFSIZE		1024
51 
52 main(argc, argv)
53 	int argc;
54 	char **argv;
55 {
56 	char *progname;
57 	bool inclnull = FALSE;
58 	bool notrunc = FALSE;
59 	bool allowreplace = FALSE;
60 	bool allowdups = FALSE;
61 	bool verbose = FALSE;
62 	bool foldcase = TRUE;
63 	int exitstat;
64 	int opt;
65 	char *typename;
66 	char *mapname;
67 	char *ext;
68 	int lineno;
69 	int st;
70 	int mode;
71 	enum type type;
72 	int fd;
73 	union
74 	{
75 #ifdef NDBM
76 		DBM	*dbm;
77 #endif
78 #ifdef NEWDB
79 		DB	*db;
80 #endif
81 		void	*dbx;
82 	} dbp;
83 	union dbent key, val;
84 #ifdef NEWDB
85 	BTREEINFO bti;
86 #endif
87 	char ibuf[BUFSIZE];
88 	char fbuf[MAXNAME];
89 	extern char *optarg;
90 	extern int optind;
91 	extern bool lockfile();
92 
93 	progname = argv[0];
94 
95 	while ((opt = getopt(argc, argv, "Ndforv")) != EOF)
96 	{
97 		switch (opt)
98 		{
99 		  case 'N':
100 			inclnull = TRUE;
101 			break;
102 
103 		  case 'd':
104 			allowdups = TRUE;
105 			break;
106 
107 		  case 'f':
108 			foldcase = FALSE;
109 			break;
110 
111 		  case 'o':
112 			notrunc = TRUE;
113 			break;
114 
115 		  case 'r':
116 			allowreplace = TRUE;
117 			break;
118 
119 		  case 'v':
120 			verbose = TRUE;
121 			break;
122 
123 		  default:
124 			type = T_ERR;
125 			break;
126 		}
127 	}
128 
129 	argc -= optind;
130 	argv += optind;
131 	if (argc != 2)
132 		type = T_ERR;
133 	else
134 	{
135 		typename = argv[0];
136 		mapname = argv[1];
137 		ext = NULL;
138 
139 		if (strcmp(typename, "dbm") == 0)
140 		{
141 			type = T_DBM;
142 		}
143 		else if (strcmp(typename, "btree") == 0)
144 		{
145 			type = T_BTREE;
146 			ext = ".db";
147 		}
148 		else if (strcmp(typename, "hash") == 0)
149 		{
150 			type = T_HASH;
151 			ext = ".db";
152 		}
153 		else
154 			type = T_UNKNOWN;
155 	}
156 
157 	switch (type)
158 	{
159 	  case T_ERR:
160 		fprintf(stderr, "Usage: %s [-N] [-d] [-f] [-o] [-r] [-v] type mapname\n", progname);
161 		exit(EX_USAGE);
162 
163 	  case T_UNKNOWN:
164 		fprintf(stderr, "%s: Unknown database type %s\n",
165 			progname, typename);
166 		exit(EX_USAGE);
167 
168 #ifndef NDBM
169 	  case T_DBM:
170 #endif
171 #ifndef NEWDB
172 	  case T_BTREE:
173 	  case T_HASH:
174 #endif
175 		fprintf(stderr, "%s: Type %s not supported in this version\n",
176 			progname, typename);
177 		exit(EX_UNAVAILABLE);
178 
179 #ifdef NEWDB
180 	  case T_BTREE:
181 		bzero(&bti, sizeof bti);
182 		if (allowdups)
183 			bti.flags |= R_DUP;
184 		break;
185 
186 	  case T_HASH:
187 #endif
188 #ifdef NDBM
189 	  case T_DBM:
190 #endif
191 		if (allowdups)
192 		{
193 			fprintf(stderr, "%s: Type %s does not support -d (allow dups)\n",
194 				progname, typename);
195 			exit(EX_UNAVAILABLE);
196 		}
197 		break;
198 	}
199 
200 	/*
201 	**  Adjust file names.
202 	*/
203 
204 	if (ext != NULL)
205 	{
206 		int el, fl;
207 
208 		el = strlen(ext);
209 		fl = strlen(mapname);
210 		if (fl < el || strcmp(&mapname[fl - el], ext) != 0)
211 		{
212 			strcpy(fbuf, mapname);
213 			strcat(fbuf, ext);
214 			mapname = fbuf;
215 		}
216 	}
217 
218 	/*
219 	**  Create the database.
220 	*/
221 
222 	mode = O_RDWR;
223 #ifdef O_EXLOCK
224 	mode |= O_EXLOCK;
225 #endif
226 	if (!notrunc)
227 		mode |= O_CREAT|O_TRUNC;
228 	switch (type)
229 	{
230 #ifdef NDBM
231 	  case T_DBM:
232 		dbp.dbm = dbm_open(mapname, mode, 0644);
233 		break;
234 #endif
235 
236 #ifdef NEWDB
237 	  case T_HASH:
238 		dbp.db = dbopen(mapname, mode, 0644, DB_HASH, NULL);
239 		if (dbp.db != NULL)
240 		{
241 # ifdef OLD_NEWDB
242 			(void) (*dbp.db->sync)(dbp.db);
243 # else
244 			(void) (*dbp.db->sync)(dbp.db, 0);
245 # endif
246 		}
247 		break;
248 
249 	  case T_BTREE:
250 		dbp.db = dbopen(mapname, mode, 0644, DB_BTREE, &bti);
251 		if (dbp.db != NULL)
252 		{
253 # ifdef OLD_NEWDB
254 			(void) (*dbp.db->sync)(dbp.db);
255 # else
256 			(void) (*dbp.db->sync)(dbp.db, 0);
257 # endif
258 		}
259 		break;
260 #endif
261 
262 	  default:
263 		fprintf(stderr, "%s: internal error: type %d\n", progname, type);
264 		exit(EX_SOFTWARE);
265 	}
266 
267 	if (dbp.dbx == NULL)
268 	{
269 		fprintf(stderr, "%s: cannot create type %s map %s\n",
270 			progname, typename, mapname);
271 		exit(EX_CANTCREAT);
272 	}
273 
274 #ifndef O_EXLOCK
275 	switch (type)
276 	{
277 # ifdef NDBM
278 	  case T_DBM:
279 		fd = dbm_dirfno(dbp.dbm);
280 		if (fd >= 0)
281 			lockfile(fd);
282 		break;
283 # endif
284 # ifdef NEWDB
285 	  case T_HASH:
286 	  case T_BTREE:
287 		fd = dbp.db->fd(dbp.db);
288 		if (fd >= 0)
289 			lockfile(fd);
290 		break;
291 # endif
292 	}
293 #endif
294 
295 	/*
296 	**  Copy the data
297 	*/
298 
299 	lineno = 0;
300 	exitstat = EX_OK;
301 	while (fgets(ibuf, sizeof ibuf, stdin) != NULL)
302 	{
303 		register char *p;
304 
305 		lineno++;
306 
307 		/*
308 		**  Parse the line.
309 		*/
310 
311 		p = strchr(ibuf, '\n');
312 		if (p != NULL)
313 			*p = '\0';
314 		else if (!feof(stdin))
315 		{
316 			fprintf(stderr, "%s: %s: line %d: line too long (%d bytes max)\n",
317 				progname, mapname, lineno, sizeof ibuf);
318 			continue;
319 		}
320 
321 		if (ibuf[0] == '\0' || ibuf[0] == '#')
322 			continue;
323 		if (isspace(ibuf[0]))
324 		{
325 			fprintf(stderr, "%s: %s: line %d: syntax error (leading space)\n",
326 				progname, mapname, lineno);
327 			continue;
328 		}
329 		key.xx.data = ibuf;
330 		for (p = ibuf; *p != '\0' && !isspace(*p); p++)
331 		{
332 			if (foldcase && isupper(*p))
333 				*p = tolower(*p);
334 		}
335 		key.xx.size = p - key.xx.data;
336 		if (inclnull)
337 			key.xx.size++;
338 		if (*p != '\0')
339 			*p++ = '\0';
340 		while (isspace(*p))
341 			p++;
342 		if (*p == '\0')
343 		{
344 			fprintf(stderr, "%s: %s: line %d: no RHS for LHS %s\n",
345 				progname, mapname, lineno, key.xx.data);
346 			continue;
347 		}
348 		val.xx.data = p;
349 		val.xx.size = strlen(p);
350 		if (inclnull)
351 			val.xx.size++;
352 
353 		/*
354 		**  Do the database insert.
355 		*/
356 
357 		if (verbose)
358 		{
359 			printf("key=`%s', val=`%s'\n", key.xx.data, val.xx.data);
360 		}
361 
362 		switch (type)
363 		{
364 #ifdef NDBM
365 		  case T_DBM:
366 			st = dbm_store(dbp.dbm, key.dbm, val.dbm,
367 					allowreplace ? DBM_REPLACE : DBM_INSERT);
368 			break;
369 #endif
370 
371 #ifdef NEWDB
372 		  case T_BTREE:
373 		  case T_HASH:
374 			st = (*dbp.db->put)(dbp.db, &key.db, &val.db,
375 					allowreplace ? 0 : R_NOOVERWRITE);
376 			break;
377 #endif
378 		}
379 
380 		if (st < 0)
381 		{
382 			fprintf(stderr, "%s: %s: line %d: key %s: put error\n",
383 				progname, mapname, lineno, key.xx.data);
384 			perror(mapname);
385 			exitstat = EX_IOERR;
386 		}
387 		else if (st > 0)
388 		{
389 			fprintf(stderr, "%s: %s: line %d: key %s: duplicate key\n",
390 				progname, mapname, lineno, key.xx.data);
391 		}
392 	}
393 
394 	/*
395 	**  Now close the database.
396 	*/
397 
398 	switch (type)
399 	{
400 #ifdef NDBM
401 	  case T_DBM:
402 		dbm_close(dbp.dbm);
403 		break;
404 #endif
405 
406 #ifdef NEWDB
407 	  case T_HASH:
408 	  case T_BTREE:
409 		if ((*dbp.db->close)(dbp.db) < 0)
410 		{
411 			fprintf(stderr, "%s: %s: error on close\n",
412 				progname, mapname);
413 			perror(mapname);
414 			exitstat = EX_IOERR;
415 		}
416 #endif
417 	}
418 
419 	exit (exitstat);
420 }
421 /*
422 **  LOCKFILE -- lock a file using flock or (shudder) fcntl locking
423 **
424 **	Parameters:
425 **		fd -- the file descriptor of the file.
426 **
427 **	Returns:
428 **		TRUE if the lock was acquired.
429 **		FALSE otherwise.
430 */
431 
432 bool
433 lockfile(fd)
434 	int fd;
435 {
436 # if !HASFLOCK
437 	int action;
438 	struct flock lfd;
439 	extern int errno;
440 
441 	bzero(&lfd, sizeof lfd);
442 	lfd.l_type = F_WRLCK;
443 	action = F_SETLKW;
444 
445 	if (fcntl(fd, action, &lfd) >= 0)
446 		return TRUE;
447 
448 	/*
449 	**  On SunOS, if you are testing using -oQ/tmp/mqueue or
450 	**  -oA/tmp/aliases or anything like that, and /tmp is mounted
451 	**  as type "tmp" (that is, served from swap space), the
452 	**  previous fcntl will fail with "Invalid argument" errors.
453 	**  Since this is fairly common during testing, we will assume
454 	**  that this indicates that the lock is successfully grabbed.
455 	*/
456 
457 	if (errno == EINVAL)
458 		return TRUE;
459 
460 # else	/* HASFLOCK */
461 
462 	if (flock(fd, LOCK_EX) >= 0)
463 		return TRUE;
464 
465 # endif
466 
467 	return FALSE;
468 }
469