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