mkdep.c revision 1.41
1/* $NetBSD: mkdep.c,v 1.41 2012/08/26 22:37:19 jmmv 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.41 2012/08/26 22:37:19 jmmv 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 <getopt.h>
50#include <locale.h>
51#include <paths.h>
52#include <stdio.h>
53#include <stdlib.h>
54#include <string.h>
55#include <unistd.h>
56
57#include "findcc.h"
58
59typedef struct opt opt_t;
60struct opt {
61	opt_t	*left;
62	opt_t	*right;
63	int	len;
64	int	count;
65	char	name[4];
66};
67
68typedef struct suff_list {
69	size_t	len;
70	char	*suff;
71	struct suff_list *next;
72} suff_list_t;
73
74/* tree of includes for -o processing */
75static opt_t *opt;
76static int width;
77
78#define DEFAULT_PATH		_PATH_DEFPATH
79#define DEFAULT_FILENAME	".depend"
80
81static void save_for_optional(const char *, const char *);
82static int write_optional(int, opt_t *, int);
83
84static inline void *
85deconst(const void *p)
86{
87	return (const char *)p - (const char *)0 + (char *)0;
88}
89
90__dead static void
91usage(void)
92{
93	(void)fprintf(stderr,
94	    "usage: %s [-aDdopq] [-f file] [-P prefix] [-s suffixes] "
95	    "-- [flags] file ...\n",
96	    getprogname());
97	exit(EXIT_FAILURE);
98}
99
100static int
101run_cc(int argc, char **argv, const char **fname)
102{
103	const char *CC, *tmpdir;
104	char * volatile pathname;
105	static char tmpfilename[MAXPATHLEN];
106	char **args;
107	int tmpfd;
108	pid_t pid, cpid;
109	int status;
110
111	if ((CC = getenv("CC")) == NULL)
112		CC = DEFAULT_CC;
113	if ((pathname = findcc(CC)) == NULL)
114		if (!setenv("PATH", DEFAULT_PATH, 1))
115			pathname = findcc(CC);
116	if (pathname == NULL)
117		err(EXIT_FAILURE, "%s: not found", CC);
118	if ((args = malloc((argc + 3) * sizeof(char *))) == NULL)
119		err(EXIT_FAILURE, "malloc");
120
121	args[0] = deconst(CC);
122	args[1] = deconst("-M");
123	(void)memcpy(&args[2], argv, (argc + 1) * sizeof(char *));
124
125	if ((tmpdir = getenv("TMPDIR")) == NULL)
126		tmpdir = _PATH_TMP;
127	(void)snprintf(tmpfilename, sizeof (tmpfilename), "%s/%s", tmpdir,
128	    "mkdepXXXXXX");
129	if ((tmpfd = mkstemp(tmpfilename)) < 0)
130		err(EXIT_FAILURE,  "Unable to create temporary file %s",
131		    tmpfilename);
132	(void)unlink(tmpfilename);
133	*fname = tmpfilename;
134
135	switch (cpid = vfork()) {
136	case 0:
137		(void)dup2(tmpfd, STDOUT_FILENO);
138		(void)close(tmpfd);
139
140		(void)execv(pathname, args);
141		_exit(EXIT_FAILURE);
142		/* NOTREACHED */
143
144	case -1:
145		err(EXIT_FAILURE, "unable to fork");
146	}
147
148	free(pathname);
149	free(args);
150
151	while (((pid = wait(&status)) != cpid) && (pid >= 0))
152		continue;
153
154	if (status)
155		errx(EXIT_FAILURE, "compile failed.");
156
157	return tmpfd;
158}
159
160static const char *
161read_fname(void)
162{
163	static char *fbuf;
164	static int fbuflen;
165	int len, ch;
166
167	for (len = 0; (ch = getchar()) != EOF; len++) {
168		if (isspace(ch)) {
169			if (len != 0)
170				break;
171			len--;
172			continue;
173		}
174		if (len >= fbuflen - 1) {
175			fbuf = realloc(fbuf, fbuflen += 32);
176			if (fbuf == NULL)
177				err(EXIT_FAILURE, "no memory");
178		}
179		fbuf[len] = ch;
180	}
181	if (len == 0)
182		return NULL;
183	fbuf[len] = 0;
184	return fbuf;
185}
186
187static struct option longopt[] = {
188	{ "sysroot", 1, NULL, 'R' },
189	{ NULL, 0, NULL, '\0' },
190};
191
192static void
193addsuff(suff_list_t **l, const char *s, size_t len)
194{
195	suff_list_t *p = calloc(1, sizeof(*p));
196	if (p == NULL)
197		err(1, "calloc");
198	p->suff = malloc(len + 1);
199	if (p->suff == NULL)
200		err(1, "malloc");
201	memcpy(p->suff, s, len);
202	p->suff[len] = '\0';
203	p->len = len;
204	p->next = *l;
205	*l = p;
206}
207
208int
209main(int argc, char **argv)
210{
211	int 	aflag, dflag, oflag, qflag;
212	const char *filename;
213	int	dependfile;
214	char	*buf, *lim, *ptr, *line, *suf, *colon, *eol;
215	int	ok_ind, ch;
216	size_t	sz;
217	int	fd;
218	size_t  slen;
219	const char *fname;
220	const char *prefix = NULL;
221	const char *suffixes = NULL, *s;
222	suff_list_t *suff_list = NULL, *sl;
223
224	suf = NULL;		/* XXXGCC -Wuninitialized [sun2] */
225	sl = NULL;		/* XXXGCC -Wuninitialized [sun2] */
226
227	setlocale(LC_ALL, "");
228	setprogname(argv[0]);
229
230	aflag = O_WRONLY | O_APPEND | O_CREAT | O_TRUNC;
231	dflag = 0;
232	oflag = 0;
233	qflag = 0;
234	filename = DEFAULT_FILENAME;
235	dependfile = -1;
236
237	opterr = 0;	/* stop getopt() bleating about errors. */
238	for (;;) {
239		ok_ind = optind;
240		ch = getopt_long(argc, argv, "aDdf:oP:pqRs:", longopt, NULL);
241		switch (ch) {
242		case -1:
243			ok_ind = optind;
244			break;
245		case 'a':	/* Append to output file */
246			aflag &= ~O_TRUNC;
247			continue;
248		case 'D':	/* Process *.d files (don't run cc -M) */
249			dflag = 2;	/* Read names from stdin */
250			opterr = 1;
251			continue;
252		case 'd':	/* Process *.d files (don't run cc -M) */
253			dflag = 1;
254			opterr = 1;
255			continue;
256		case 'f':	/* Name of output file */
257			filename = optarg;
258			continue;
259		case 'o':	/* Mark dependent files .OPTIONAL */
260			oflag = 1;
261			continue;
262		case 'P':	/* Prefix for each target filename */
263			prefix = optarg;
264			continue;
265		case 'p':	/* Program mode (x.o: -> x:) */
266			suffixes = "";
267			continue;
268		case 'q':	/* Quiet */
269			qflag = 1;
270			continue;
271		case 'R':
272			/* sysroot = optarg */
273			continue;
274		case 's':	/* Suffix list */
275			suffixes = optarg;
276			continue;
277		default:
278			if (dflag)
279				usage();
280			/* Unknown arguments are passed to "${CC} -M" */
281			break;
282		}
283		break;
284	}
285
286	argc -= ok_ind;
287	argv += ok_ind;
288	if ((argc == 0 && !dflag) || (argc != 0 && dflag == 2))
289		usage();
290
291	if (suffixes != NULL) {
292		if (*suffixes) {
293			for (s = suffixes; (sz = strcspn(s, ", ")) != 0;) {
294				addsuff(&suff_list, s, sz);
295				s += sz;
296				while (*s && strchr(", ", *s))
297					s++;
298			}
299		} else
300			addsuff(&suff_list, "", 0);
301	}
302
303	dependfile = open(filename, aflag, 0666);
304	if (dependfile == -1)
305		err(EXIT_FAILURE, "unable to %s to file %s\n",
306		    aflag & O_TRUNC ? "write" : "append", filename);
307
308	while (dflag == 2 || *argv != NULL) {
309		if (dflag) {
310			if (dflag == 2) {
311				fname = read_fname();
312				if (fname == NULL)
313					break;
314			} else
315				fname = *argv++;
316			fd = open(fname, O_RDONLY, 0);
317			if (fd == -1) {
318				if (!qflag)
319					warn("ignoring %s", fname);
320				continue;
321			}
322		} else {
323			fd = run_cc(argc, argv, &fname);
324			/* consume all args... */
325			argv += argc;
326		}
327
328		sz = lseek(fd, 0, SEEK_END);
329		if (sz == 0) {
330			close(fd);
331			continue;
332		}
333		buf = mmap(NULL, sz, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
334		close(fd);
335
336		if (buf == MAP_FAILED)
337			err(EXIT_FAILURE, "unable to mmap file %s", fname);
338		lim = buf + sz - 1;
339
340		/* Remove leading "./" from filenames */
341		for (ptr = buf; ptr < lim; ptr++) {
342			if (ptr[1] != '.' || ptr[2] != '/'
343			    || !isspace((unsigned char)ptr[0]))
344				continue;
345			ptr[1] = ' ';
346			ptr[2] = ' ';
347		}
348
349		for (line = eol = buf; eol <= lim;) {
350			while (eol <= lim && *eol++ != '\n')
351				/* Find end of this line */
352				continue;
353			if (line == eol - 1) {
354				/* empty line - ignore */
355				line = eol;
356				continue;
357			}
358			if (eol[-2] == '\\')
359				/* Assemble continuation lines */
360				continue;
361			for (colon = line; *colon != ':'; colon++) {
362				if (colon >= eol) {
363					colon = NULL;
364					break;
365				}
366			}
367			if (isspace((unsigned char)*line) || colon == NULL) {
368				/* No dependency - just transcribe line */
369				write(dependfile, line, eol - line);
370				line = eol;
371				continue;
372			}
373			if (suff_list != NULL) {
374				/* Find the .o: */
375				/* First allow for any whitespace */
376				for (suf = colon; suf > buf; suf--) {
377					if (!isspace((unsigned char)suf[-1]))
378						break;
379				}
380				if (suf == buf)
381					errx(EXIT_FAILURE,
382					    "Corrupted file `%s'", fname);
383				/* Then look for any valid suffix */
384				for (sl = suff_list; sl != NULL;
385				    sl = sl->next) {
386					if (sl->len && buf <= suf - sl->len &&
387					    !memcmp(suf - sl->len, sl->suff,
388						    sl->len))
389						break;
390				}
391				/*
392				 * Not found, check for .o, since the
393				 * original file will have it.
394				 */
395				if (sl == NULL) {
396					if (memcmp(suf - 2, ".o", 2) == 0)
397						slen = 2;
398					else
399						slen = 0;
400				} else
401					slen = sl->len;
402			}
403			if (suff_list != NULL && slen != 0) {
404				suf -= slen;
405				for (sl = suff_list; sl != NULL; sl = sl->next)
406				{
407					if (sl != suff_list)
408						write(dependfile, " ", 1);
409					if (prefix != NULL)
410						write(dependfile, prefix,
411						    strlen(prefix));
412					write(dependfile, line, suf - line);
413					write(dependfile, sl->suff, sl->len);
414				}
415				write(dependfile, colon, eol - colon);
416			} else {
417				if (prefix != NULL)
418					write(dependfile, prefix, strlen(prefix));
419				write(dependfile, line, eol - line);
420			}
421
422			if (oflag)
423				save_for_optional(colon + 1, eol);
424			line = eol;
425		}
426		munmap(buf, sz);
427	}
428
429	if (oflag && opt != NULL) {
430		write(dependfile, ".OPTIONAL:", 10);
431		width = 9;
432		sz = write_optional(dependfile, opt, 0);
433		/* 'depth' is about 39 for an i386 kernel */
434		/* fprintf(stderr, "Recursion depth %d\n", sz); */
435	}
436	close(dependfile);
437
438	exit(EXIT_SUCCESS);
439}
440
441
442/*
443 * Only save each file once - the kernel .depend is 3MB and there is
444 * no point doubling its size.
445 * The data seems to be 'random enough' so the simple binary tree
446 * only has a reasonable depth.
447 */
448static void
449save_for_optional(const char *start, const char *limit)
450{
451	opt_t **l, *n;
452	const char *name, *end;
453	int c;
454
455	while (start < limit && strchr(" \t\n\\", *start))
456		start++;
457	for (name = start; ; name = end) {
458		while (name < limit && strchr(" \t\n\\", *name))
459			name++;
460		for (end = name; end < limit && !strchr(" \t\n\\", *end);)
461			end++;
462		if (name >= limit)
463			break;
464		if (end[-1] == 'c' && end[-2] == '.' && name == start)
465			/* ignore dependency on the files own .c */
466			continue;
467		for (l = &opt;;) {
468			n = *l;
469			if (n == NULL) {
470				n = malloc(sizeof *n + (end - name));
471				n->left = n->right = 0;
472				n->len = end - name;
473				n->count = 1;
474				n->name[0] = ' ';
475				memcpy(n->name + 1, name, end - name);
476				*l = n;
477				break;
478			}
479			c = (end - name) - n->len;
480			if (c == 0)
481				c = memcmp(n->name + 1, name, (end - name));
482			if (c == 0) {
483				/* Duplicate */
484				n->count++;
485				break;
486			}
487			if (c < 0)
488				l = &n->left;
489			else
490				l = &n->right;
491		}
492	}
493}
494
495static int
496write_optional(int fd, opt_t *node, int depth)
497{
498	int d1 = ++depth;
499
500	if (node->left)
501		d1 = write_optional(fd, node->left, d1);
502	if (width > 76 - node->len) {
503		write(fd, " \\\n ", 4);
504		width = 1;
505	}
506	width += 1 + node->len;
507	write(fd, node->name, 1 + node->len);
508	if (node->right)
509		depth = write_optional(fd, node->right, depth);
510	return d1 > depth ? d1 : depth;
511}
512