mkdep.c revision 1.34
1/* $NetBSD: mkdep.c,v 1.34 2010/05/26 15:04:40 christos Exp $ */
2
3/*-
4 * Copyright (c) 1999 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Matthias Scheler.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#if HAVE_NBTOOL_CONFIG_H
33#include "nbtool_config.h"
34#endif
35
36#include <sys/cdefs.h>
37#if !defined(lint)
38__COPYRIGHT("@(#) Copyright (c) 1999 The NetBSD Foundation, Inc.\
39 All rights reserved.");
40__RCSID("$NetBSD: mkdep.c,v 1.34 2010/05/26 15:04:40 christos Exp $");
41#endif /* not lint */
42
43#include <sys/mman.h>
44#include <sys/param.h>
45#include <sys/wait.h>
46#include <ctype.h>
47#include <err.h>
48#include <fcntl.h>
49#include <locale.h>
50#include <paths.h>
51#include <stdio.h>
52#include <stdlib.h>
53#include <string.h>
54#include <unistd.h>
55
56#include "findcc.h"
57
58typedef struct opt opt_t;
59struct opt {
60	opt_t	*left;
61	opt_t	*right;
62	int	len;
63	int	count;
64	char	name[4];
65};
66
67typedef struct {
68	size_t	len;
69	char	suff[12];
70} suff_list_t;
71
72/* tree of includes for -o processing */
73opt_t *opt;
74int width;
75
76#define DEFAULT_PATH		_PATH_DEFPATH
77#define DEFAULT_FILENAME	".depend"
78
79static void save_for_optional(const char *, const char *);
80static int write_optional(int, opt_t *, int);
81
82
83static inline void *
84deconst(const void *p)
85{
86	return (const char *)p - (const char *)0 + (char *)0;
87}
88
89static void
90usage(void)
91{
92	(void)fprintf(stderr,
93	    "usage: %s [-aDdopq] [-f file] [-s suffixes] -- [flags] file ...\n",
94	    getprogname());
95	exit(EXIT_FAILURE);
96}
97
98static int
99run_cc(int argc, char **argv, const char **fname)
100{
101	const char *CC, *tmpdir;
102	char * volatile pathname;
103	static char tmpfilename[MAXPATHLEN];
104	char **args;
105	int tmpfd;
106	pid_t pid, cpid;
107	int status;
108
109	if ((CC = getenv("CC")) == NULL)
110		CC = DEFAULT_CC;
111	if ((pathname = findcc(CC)) == NULL)
112		if (!setenv("PATH", DEFAULT_PATH, 1))
113			pathname = findcc(CC);
114	if (pathname == NULL)
115		err(EXIT_FAILURE, "%s: not found", CC);
116	if ((args = malloc((argc + 3) * sizeof(char *))) == NULL)
117		err(EXIT_FAILURE, "malloc");
118
119	args[0] = deconst(CC);
120	args[1] = deconst("-M");
121	(void)memcpy(&args[2], argv, (argc + 1) * sizeof(char *));
122
123	if ((tmpdir = getenv("TMPDIR")) == NULL)
124		tmpdir = _PATH_TMP;
125	(void)snprintf(tmpfilename, sizeof (tmpfilename), "%s/%s", tmpdir,
126	    "mkdepXXXXXX");
127	if ((tmpfd = mkstemp(tmpfilename)) < 0) {
128		warn("unable to create temporary file %s", tmpfilename);
129		exit(EXIT_FAILURE);
130	}
131	(void)unlink(tmpfilename);
132	*fname = tmpfilename;
133
134	switch (cpid = vfork()) {
135	case 0:
136		(void)dup2(tmpfd, STDOUT_FILENO);
137		(void)close(tmpfd);
138
139		(void)execv(pathname, args);
140		_exit(EXIT_FAILURE);
141		/* NOTREACHED */
142
143	case -1:
144		err(EXIT_FAILURE, "unable to fork");
145	}
146
147	free(pathname);
148	free(args);
149
150	while (((pid = wait(&status)) != cpid) && (pid >= 0))
151		continue;
152
153	if (status)
154		errx(EXIT_FAILURE, "compile failed.");
155
156	return tmpfd;
157}
158
159static const char *
160read_fname(void)
161{
162	static char *fbuf;
163	static int fbuflen;
164	int len, ch;
165
166	for (len = 0; (ch = getchar()) != EOF; len++) {
167		if (isspace(ch)) {
168			if (len != 0)
169				break;
170			len--;
171			continue;
172		}
173		if (len >= fbuflen - 1) {
174			fbuf = realloc(fbuf, fbuflen += 32);
175			if (fbuf == NULL)
176				err(EXIT_FAILURE, "no memory");
177		}
178		fbuf[len] = ch;
179	}
180	if (len == 0)
181		return NULL;
182	fbuf[len] = 0;
183	return fbuf;
184}
185
186int
187main(int argc, char **argv)
188{
189	int 	aflag, dflag, oflag, qflag;
190	const char *filename;
191	int	dependfile;
192	char	*buf, *lim, *ptr, *line, *suf, *colon, *eol;
193	int	ok_ind, ch;
194	size_t	sz;
195	int	fd;
196	size_t  slen;
197	const char *fname;
198	const char *suffixes = NULL, *s;
199	suff_list_t *suff_list = NULL, *sl;
200
201	suf = NULL;		/* XXXGCC -Wuninitialized [sun2] */
202	sl = NULL;		/* XXXGCC -Wuninitialized [sun2] */
203
204	setlocale(LC_ALL, "");
205	setprogname(argv[0]);
206
207	aflag = O_WRONLY | O_APPEND | O_CREAT | O_TRUNC;
208	dflag = 0;
209	oflag = 0;
210	qflag = 0;
211	filename = DEFAULT_FILENAME;
212	dependfile = -1;
213
214	opterr = 0;	/* stop getopt() bleating about errors. */
215	for (;;) {
216		ok_ind = optind;
217		ch = getopt(argc, argv, "aDdf:opqs:");
218		switch (ch) {
219		case -1:
220			ok_ind = optind;
221			break;
222		case 'a':	/* Append to output file */
223			aflag &= ~O_TRUNC;
224			continue;
225		case 'D':	/* Process *.d files (don't run cc -M) */
226			dflag = 2;	/* Read names from stdin */
227			opterr = 1;
228			continue;
229		case 'd':	/* Process *.d files (don't run cc -M) */
230			dflag = 1;
231			opterr = 1;
232			continue;
233		case 'f':	/* Name of output file */
234			filename = optarg;
235			continue;
236		case 'o':	/* Mark dependant files .OPTIONAL */
237			oflag = 1;
238			continue;
239		case 'p':	/* Program mode (x.o: -> x:) */
240			suffixes = "";
241			continue;
242		case 'q':	/* Quiet */
243			qflag = 1;
244			continue;
245		case 's':	/* Suffix list */
246			suffixes = optarg;
247			continue;
248		default:
249			if (dflag)
250				usage();
251			/* Unknown arguments are passed to "${CC} -M" */
252			break;
253		}
254		break;
255	}
256
257	argc -= ok_ind;
258	argv += ok_ind;
259	if ((argc == 0 && !dflag) || (argc != 0 && dflag == 2))
260		usage();
261
262	if (suffixes != NULL) {
263		/* parse list once and save names and lengths */
264		/* allocate an extra entry to mark end of list */
265		for (sz = 1, s = suffixes; *s != 0; s++)
266			if (*s == '.')
267			    sz++;
268		suff_list = calloc(sz, sizeof *suff_list);
269		if (suff_list == NULL)
270			err(2, "malloc");
271		sl = suff_list;
272		for (s = suffixes; (s = strchr(s, '.')); s += sz, sl++ ) {
273			sz = strcspn(s, ", ");
274			if (sz > sizeof sl->suff)
275				errx(2, "suffix too long");
276			sl->len = sz;
277			memcpy(sl->suff, s, sz);
278		}
279	}
280
281	dependfile = open(filename, aflag, 0666);
282	if (dependfile == -1)
283		err(EXIT_FAILURE, "unable to %s to file %s\n",
284		    aflag & O_TRUNC ? "write" : "append", filename);
285
286	while (dflag == 2 || *argv != NULL) {
287		if (dflag) {
288			if (dflag == 2) {
289				fname = read_fname();
290				if (fname == NULL)
291					break;
292			} else
293				fname = *argv++;
294			fd = open(fname, O_RDONLY, 0);
295			if (fd == -1) {
296				if (!qflag)
297					warn("ignoring %s", fname);
298				continue;
299			}
300		} else {
301			fd = run_cc(argc, argv, &fname);
302			/* consume all args... */
303			argv += argc;
304		}
305
306		sz = lseek(fd, 0, SEEK_END);
307		if (sz == 0) {
308			close(fd);
309			continue;
310		}
311		buf = mmap(NULL, sz, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
312		close(fd);
313
314		if (buf == MAP_FAILED)
315			err(EXIT_FAILURE, "unable to mmap file %s", fname);
316		lim = buf + sz - 1;
317
318		/* Remove leading "./" from filenames */
319		for (ptr = buf; ptr < lim; ptr++) {
320			if (ptr[1] != '.' || ptr[2] != '/'
321			    || !isspace((unsigned char)ptr[0]))
322				continue;
323			ptr[1] = ' ';
324			ptr[2] = ' ';
325		}
326
327		for (line = eol = buf; eol <= lim;) {
328			while (eol <= lim && *eol++ != '\n')
329				/* Find end of this line */
330				continue;
331			if (line == eol - 1) {
332				/* empty line - ignore */
333				line = eol;
334				continue;
335			}
336			if (eol[-2] == '\\')
337				/* Assemble continuation lines */
338				continue;
339			for (colon = line; *colon != ':'; colon++) {
340				if (colon >= eol) {
341					colon = NULL;
342					break;
343				}
344			}
345			if (isspace((unsigned char)*line) || colon == NULL) {
346				/* No dependency - just transcribe line */
347				write(dependfile, line, eol - line);
348				line = eol;
349				continue;
350			}
351			if (suff_list != NULL) {
352				/* Find the .o: */
353				/* First allow for any whitespace */
354				for (suf = colon; suf > buf; suf--) {
355					if (!isspace((unsigned char)suf[-1]))
356						break;
357				}
358				if (suf == buf)
359					errx(EXIT_FAILURE,
360					    "Corrupted file `%s'", fname);
361				/* Then look for any valid suffix */
362				for (sl = suff_list; sl->len != 0; sl++) {
363					if (!memcmp(suf - sl->len, sl->suff,
364						    sl->len))
365						break;
366				}
367				/*
368				 * Not found, check for .o, since the
369				 * original file will have it.
370				 */
371				if (sl->len == 0) {
372					if (memcmp(suf - 2, ".o", 2) == 0)
373						slen = 2;
374					else
375						slen = 0;
376				} else
377					slen = sl->len;
378			}
379			if (suff_list != NULL && slen != 0) {
380				suf -= slen;
381				for (sl = suff_list; sl->len != 0; sl++) {
382					if (sl != suff_list)
383						write(dependfile, " ", 1);
384					write(dependfile, line, suf - line);
385					write(dependfile, sl->suff, sl->len);
386				}
387				write(dependfile, colon, eol - colon);
388			} else
389				write(dependfile, line, eol - line);
390
391			if (oflag)
392				save_for_optional(colon + 1, eol);
393			line = eol;
394		}
395		munmap(buf, sz);
396	}
397
398	if (oflag && opt != NULL) {
399		write(dependfile, ".OPTIONAL:", 10);
400		width = 9;
401		sz = write_optional(dependfile, opt, 0);
402		/* 'depth' is about 39 for an i386 kernel */
403		/* fprintf(stderr, "Recursion depth %d\n", sz); */
404	}
405	close(dependfile);
406
407	exit(EXIT_SUCCESS);
408}
409
410
411/*
412 * Only save each file once - the kernel .depend is 3MB and there is
413 * no point doubling its size.
414 * The data seems to be 'random enough' so the simple binary tree
415 * only has a reasonable depth.
416 */
417static void
418save_for_optional(const char *start, const char *limit)
419{
420	opt_t **l, *n;
421	const char *name, *end;
422	int c;
423
424	while (start < limit && strchr(" \t\n\\", *start))
425		start++;
426	for (name = start; ; name = end) {
427		while (name < limit && strchr(" \t\n\\", *name))
428			name++;
429		for (end = name; end < limit && !strchr(" \t\n\\", *end);)
430			end++;
431		if (name >= limit)
432			break;
433		if (end[-1] == 'c' && end[-2] == '.' && name == start)
434			/* ignore dependency on the files own .c */
435			continue;
436		for (l = &opt;;) {
437			n = *l;
438			if (n == NULL) {
439				n = malloc(sizeof *n + (end - name));
440				n->left = n->right = 0;
441				n->len = end - name;
442				n->count = 1;
443				n->name[0] = ' ';
444				memcpy(n->name + 1, name, end - name);
445				*l = n;
446				break;
447			}
448			c = (end - name) - n->len;
449			if (c == 0)
450				c = memcmp(n->name + 1, name, (end - name));
451			if (c == 0) {
452				/* Duplicate */
453				n->count++;
454				break;
455			}
456			if (c < 0)
457				l = &n->left;
458			else
459				l = &n->right;
460		}
461	}
462}
463
464static int
465write_optional(int fd, opt_t *node, int depth)
466{
467	int d1 = ++depth;
468
469	if (node->left)
470		d1 = write_optional(fd, node->left, d1);
471	if (width > 76 - node->len) {
472		write(fd, " \\\n ", 4);
473		width = 1;
474	}
475	width += 1 + node->len;
476	write(fd, node->name, 1 + node->len);
477	if (node->right)
478		depth = write_optional(fd, node->right, depth);
479	return d1 > depth ? d1 : depth;
480}
481