1 /*-
2  * Copyright (c) 2011-2021 Hans Petter Selasky <hselasky@freebsd.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <ctype.h>
28 #include <stdio.h>
29 #include <stdint.h>
30 #include <stdlib.h>
31 #include <sysexits.h>
32 #include <err.h>
33 #include <string.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36 
37 #include <sys/queue.h>
38 #include <sys/stat.h>
39 #include <errno.h>
40 
41 struct node;
42 typedef TAILQ_HEAD(, node) node_head_t;
43 typedef TAILQ_ENTRY(node) node_entry_t;
44 
45 struct config;
46 typedef TAILQ_HEAD(, config) config_head_t;
47 typedef TAILQ_ENTRY(config) config_entry_t;
48 
49 struct directory;
50 typedef TAILQ_HEAD(, directory) directory_head_t;
51 typedef TAILQ_ENTRY(directory) directory_entry_t;
52 
53 struct makefile;
54 typedef TAILQ_HEAD(, makefile) makefile_head_t;
55 typedef TAILQ_ENTRY(makefile) makefile_entry_t;
56 
57 struct exclude;
58 typedef TAILQ_HEAD(, exclude) exclude_head_t;
59 typedef TAILQ_ENTRY(exclude) exclude_entry_t;
60 
61 #if 0
62 void
63 style_fix(void)
64 {
65 }
66 
67 #endif
68 
69 struct node {
70 	node_entry_t entry;
71 	node_head_t children;
72 	char   *name;			/* obj-y/obj-m/obj-n/xxx.o */
73 	char   *path;			/* relative path to file */
74 	char   *objprefix;		/* object prefix */
75 	uint8_t	has_children;		/* set if object has children */
76 };
77 
78 struct directory {
79 	directory_entry_t entry;
80 	char   *name;
81 };
82 
83 struct makefile {
84 	makefile_entry_t entry;
85 	char   *name;
86 };
87 
88 struct exclude {
89 	exclude_entry_t entry;
90 	char   *name;
91 };
92 
93 struct config {
94 	config_entry_t entry;
95 	char   *name;
96 	char   *orig;
97 	char	value;			/* m/y/n */
98 };
99 
100 static node_head_t rootNode = TAILQ_HEAD_INITIALIZER(rootNode);
101 static config_head_t rootConfig = TAILQ_HEAD_INITIALIZER(rootConfig);
102 static directory_head_t rootDirectory = TAILQ_HEAD_INITIALIZER(rootDirectory);
103 static makefile_head_t rootMakefile = TAILQ_HEAD_INITIALIZER(rootMakefile);
104 static exclude_head_t rootExclude = TAILQ_HEAD_INITIALIZER(rootExclude);
105 static int opt_verbose;
106 
107 static char *opt_config = "config";
108 static char *opt_input;
109 static char *opt_output;
110 
111 static char *
strcatdup(const char * a,const char * b)112 strcatdup(const char *a, const char *b)
113 {
114 	int al = strlen(a);
115 	int bl = strlen(b);
116 	char *ptr;
117 
118 	ptr = malloc(al + bl + 1);
119 	if (ptr == NULL)
120 		errx(EX_SOFTWARE, "Out of memory.");
121 
122 	memcpy(ptr, a, al);
123 	memcpy(ptr + al, b, bl + 1);
124 	return (ptr);
125 }
126 
127 static struct node *
new_node(void)128 new_node(void)
129 {
130 	struct node *temp;
131 
132 	temp = malloc(sizeof(*temp));
133 	if (temp == NULL)
134 		errx(EX_SOFTWARE, "Out of memory.");
135 	memset(temp, 0, sizeof(*temp));
136 	TAILQ_INIT(&temp->children);
137 	return (temp);
138 }
139 
140 static struct directory *
new_directory(void)141 new_directory(void)
142 {
143 	struct directory *temp;
144 
145 	temp = malloc(sizeof(*temp));
146 	if (temp == NULL)
147 		errx(EX_SOFTWARE, "Out of memory.");
148 	memset(temp, 0, sizeof(*temp));
149 	return (temp);
150 }
151 
152 static void
new_makefile(char * name)153 new_makefile(char *name)
154 {
155 	struct makefile *temp;
156 
157 	temp = malloc(sizeof(*temp));
158 	if (temp == NULL)
159 		errx(EX_SOFTWARE, "Out of memory.");
160 	memset(temp, 0, sizeof(*temp));
161 	temp->name = name;
162 	TAILQ_INSERT_TAIL(&rootMakefile, temp, entry);
163 }
164 
165 static void
new_exclude(char * name)166 new_exclude(char *name)
167 {
168 	struct exclude *temp;
169 
170 	temp = malloc(sizeof(*temp));
171 	if (temp == NULL)
172 		errx(EX_SOFTWARE, "Out of memory.");
173 	memset(temp, 0, sizeof(*temp));
174 	temp->name = name;
175 	TAILQ_INSERT_TAIL(&rootExclude, temp, entry);
176 }
177 
178 static uint8_t
match_exclude(const char * name)179 match_exclude(const char *name)
180 {
181 	struct exclude *temp;
182 
183 	TAILQ_FOREACH(temp, &rootExclude, entry) {
184 		if (strcmp(temp->name, name) == 0)
185 			return (1);
186 	}
187 	return (0);
188 }
189 
190 static int
sort_config_compare(const void * pa,const void * pb)191 sort_config_compare(const void *pa, const void *pb)
192 {
193 	const struct config *ca = *(const void **)pa;
194 	const struct config *cb = *(const void **)pb;
195 
196 	return (strcmp(ca->name, cb->name));
197 }
198 
199 static void
sort_config(void)200 sort_config(void)
201 {
202 	uint32_t x;
203 	uint32_t count = 0;
204 	struct config *c0;
205 	struct config **ppc;
206 
207 	TAILQ_FOREACH(c0, &rootConfig, entry)
208 	    count++;
209 
210 	ppc = malloc(sizeof(void *) * count);
211 	if (ppc == NULL)
212 		return;
213 
214 	count = 0;
215 	TAILQ_FOREACH(c0, &rootConfig, entry)
216 	    ppc[count++] = c0;
217 
218 	mergesort(ppc, count, sizeof(void *), sort_config_compare);
219 
220 	TAILQ_INIT(&rootConfig);
221 
222 	for (x = 0; x != count; x++)
223 		TAILQ_INSERT_TAIL(&rootConfig, ppc[x], entry);
224 
225 	free(ppc);
226 }
227 
228 static void
new_config(char * name,char * orig,char what)229 new_config(char *name, char *orig, char what)
230 {
231 	struct config *c0;
232 
233 	TAILQ_FOREACH(c0, &rootConfig, entry) {
234 		if (strcmp(c0->name, name) == 0) {
235 			free(c0->orig);
236 			free(name);
237 			c0->value = what;
238 			c0->orig = orig;
239 			return;
240 		}
241 	}
242 
243 	c0 = malloc(sizeof(*c0));
244 	if (c0 == NULL)
245 		errx(EX_SOFTWARE, "Out of memory.");
246 	memset(c0, 0, sizeof(*c0));
247 	c0->name = name;
248 	c0->value = what;
249 	c0->orig = orig;
250 
251 	TAILQ_INSERT_TAIL(&rootConfig, c0, entry);
252 }
253 
254 static void
free_node(struct node * node)255 free_node(struct node *node)
256 {
257 	free(node->name);
258 	free(node->objprefix);
259 	free(node);
260 }
261 
262 static char *
load_file(char * path)263 load_file(char *path)
264 {
265 	int f;
266 	long off;
267 	char *ptr;
268 
269 	if (opt_verbose > 0)
270 		fprintf(stderr, "Loading %s\n", path);
271 
272 	f = open(path, O_RDONLY);
273 
274 	free(path);
275 
276 	if (f < 0)
277 		return (NULL);
278 
279 	off = lseek(f, 0, SEEK_END);
280 	lseek(f, 0, SEEK_SET);
281 
282 	if (off < 0) {
283 		close(f);
284 		return (NULL);
285 	}
286 	ptr = malloc(off + 1);
287 	if (ptr == NULL) {
288 		close(f);
289 		return (NULL);
290 	}
291 	if (read(f, ptr, off) != off) {
292 		close(f);
293 		return (NULL);
294 	}
295 	ptr[off] = 0;
296 
297 	while (off--) {
298 		if (ptr[off] == 0)
299 			ptr[off] = ' ';
300 	}
301 	return (ptr);
302 }
303 
304 static char *
read_line(char ** ptr,int * line)305 read_line(char **ptr, int *line)
306 {
307 	int c;
308 	char *old = *ptr;
309 	int ignore_nl = 0;
310 
311 	if (*old == 0)
312 		return (NULL);
313 
314 	(*line)++;
315 
316 	while (1) {
317 		c = **ptr;
318 		if (c == 0)
319 			break;
320 
321 		(*ptr)++;
322 
323 		switch (c) {
324 		case ' ':
325 		case '\t':
326 		case '\r':
327 			*((*ptr) - 1) = ' ';
328 			break;
329 		case '\\':
330 			*((*ptr) - 1) = ' ';
331 			ignore_nl = 1;
332 			break;
333 		case '\n':
334 			if (ignore_nl) {
335 				*((*ptr) - 1) = ' ';
336 				(*line)++;
337 				ignore_nl = 0;
338 				break;
339 			}
340 			*((*ptr) - 1) = 0;
341 			goto done;
342 		default:
343 			ignore_nl = 0;
344 			break;
345 		}
346 	}
347 done:
348 	return (old);
349 }
350 
351 static void
skip_while(char ** ptr,char * what)352 skip_while(char **ptr, char *what)
353 {
354 	int c;
355 
356 	while (1) {
357 		c = **ptr;
358 		if ((c != 0) && (strchr(what, c) != NULL)) {
359 			(*ptr)++;
360 			continue;
361 		}
362 		break;
363 	}
364 }
365 
366 static void
skip_until(char ** ptr,char * what)367 skip_until(char **ptr, char *what)
368 {
369 	int c;
370 
371 	while (1) {
372 		c = **ptr;
373 		if ((c != 0) && (strchr(what, c) == NULL)) {
374 			(*ptr)++;
375 			continue;
376 		}
377 		break;
378 	}
379 }
380 
381 static void
skip_until_tp(char ** ptr,char * what)382 skip_until_tp(char **ptr, char *what)
383 {
384 	int c;
385 	int tp = 0;
386 
387 	while (1) {
388 		c = **ptr;
389 		if (c == '(')
390 			tp++;
391 		if (c == ')')
392 			tp--;
393 		if (c != 0) {
394 			if (tp > 0) {
395 				(*ptr)++;
396 				continue;
397 			}
398 			if (strchr(what, c) == NULL) {
399 				(*ptr)++;
400 				continue;
401 			}
402 		}
403 		break;
404 	}
405 }
406 
407 static struct node *
add_node(node_head_t * head,char * name)408 add_node(node_head_t *head, char *name)
409 {
410 	struct node *node;
411 
412 	TAILQ_FOREACH(node, head, entry) {
413 		if (strcmp(node->name, name) == 0) {
414 			free(name);
415 			return (node);
416 		}
417 	}
418 	node = new_node();
419 	node->name = name;
420 	TAILQ_INSERT_TAIL(head, node, entry);
421 	return (node);
422 }
423 
424 static void
add_child(struct node * parent,char * path,char * name)425 add_child(struct node *parent, char *path, char *name)
426 {
427 	struct node *node;
428 
429 	node = new_node();
430 	node->name = name;
431 	node->path = path;
432 
433 	TAILQ_INSERT_TAIL(&parent->children, node, entry);
434 }
435 
436 static void
remove_duplicate(node_head_t * head)437 remove_duplicate(node_head_t *head)
438 {
439 	struct node *n0;
440 	struct node *n1;
441 	struct node *n2;
442 
443 	TAILQ_FOREACH(n0, head, entry) {
444 
445 		n1 = TAILQ_NEXT(n0, entry);
446 		if (n1 == NULL)
447 			continue;
448 		TAILQ_FOREACH_FROM_SAFE(n1, head, entry, n2) {
449 			if (strcmp(n0->name, n1->name) == 0) {
450 				TAILQ_REMOVE(head, n1, entry);
451 				free_node(n1);
452 			}
453 		}
454 	}
455 }
456 
457 static void
resolve_nodes(void)458 resolve_nodes(void)
459 {
460 	struct node *n0;
461 	struct node *n1;
462 	struct node *n2;
463 	struct node *n3;
464 
465 	TAILQ_FOREACH(n0, &rootNode, entry) {
466 
467 		TAILQ_FOREACH_SAFE(n1, &n0->children, entry, n2) {
468 
469 			if (n1->name[0] == '$') {
470 				char *ptr = n1->name;
471 				int len = strlen(n1->name);
472 
473 				if (ptr[1] == '(') {
474 					ptr += 2;
475 					len -= 2;
476 				}
477 				if ((len != 0) && (ptr[len - 1] == ')')) {
478 					ptr[len - 1] = 0;
479 					len--;
480 				}
481 				TAILQ_REMOVE(&n0->children, n1, entry);
482 
483 				TAILQ_FOREACH(n3, &rootNode, entry) {
484 
485 					if (n3 == n0)
486 						continue;
487 
488 					if (strcmp(n3->name, ptr) == 0) {
489 						TAILQ_REMOVE(&rootNode, n3, entry);
490 						TAILQ_CONCAT(&n0->children, &n3->children, entry);
491 						free_node(n3);
492 						break;
493 					}
494 				}
495 				free_node(n1);
496 			}
497 		}
498 	}
499 
500 	TAILQ_FOREACH(n0, &rootNode, entry)
501 	    remove_duplicate(&n0->children);
502 }
503 
504 static uint8_t
obj_has_children(const node_head_t * parent,const char * name,int len)505 obj_has_children(const node_head_t *parent, const char *name, int len)
506 {
507 	struct node *n0;
508 
509 	TAILQ_FOREACH(n0, parent, entry) {
510 		if (TAILQ_FIRST(&n0->children) == NULL)
511 			continue;
512 		if (strstr(n0->name, name) == n0->name &&
513 		    (strcmp(n0->name + len, "-n") == 0 ||
514 		     strcmp(n0->name + len, "-y") == 0 ||
515 		     strcmp(n0->name + len, "-objs") == 0 ||
516 		     strcmp(n0->name + len, "-m") == 0)) {
517 			return (1);
518 		} else {
519 			obj_has_children(&n0->children, name, len);
520 		}
521 	}
522 	return (0);
523 }
524 
525 static void
objs_exec(struct node * parent,void (* fn)(struct node *,const char *))526 objs_exec(struct node *parent, void (*fn) (struct node *, const char *))
527 {
528 	static int recurse;
529 
530 	struct node *n0;
531 	struct node *n1;
532 	char *temp;
533 	int len;
534 
535 	if (recurse > 64) {
536 		errx(EX_SOFTWARE, "Recursive object "
537 		    "execute limit of 64 exceeded for '%s'.", parent->name);
538 	}
539 	recurse++;
540 
541 	temp = strcatdup(parent->name, "");
542 	len = strlen(temp);
543 
544 	if ((len >= 2) && (temp[len - 2] == '.' && temp[len - 1] == 'o')) {
545 		temp[len - 2] = 0;
546 		len -= 2;
547 	}
548 	TAILQ_FOREACH(n0, &rootNode, entry) {
549 		if (strstr(n0->name, temp) == n0->name) {
550 
551 			if (opt_verbose > 1) {
552 				fprintf(stderr, "matching %s "
553 				    "= %s\n", temp, n0->name);
554 			}
555 			/* Expecting: <match>-<y/objs><null> */
556 			if (strcmp(n0->name + len, "-y") == 0 ||
557 			    strcmp(n0->name + len, "-objs") == 0) {
558 
559 				TAILQ_FOREACH(n1, &n0->children, entry)
560 				    objs_exec(n1, fn);
561 			}
562 		}
563 	}
564 
565 	fn(parent, temp);
566 
567 	free(temp);
568 
569 	recurse--;
570 }
571 
572 static void
dump_nodes(node_head_t * head)573 dump_nodes(node_head_t *head)
574 {
575 	static int level;
576 	struct node *n0;
577 	int x;
578 
579 	TAILQ_FOREACH(n0, head, entry) {
580 
581 		for (x = 0; x != level; x++)
582 			fprintf(stderr, "\t");
583 
584 		fprintf(stderr, "%s%s\n", n0->name,
585 		    TAILQ_FIRST(&n0->children) ? " =" : "");
586 
587 		level++;
588 
589 		dump_nodes(&n0->children);
590 
591 		level--;
592 	}
593 }
594 
595 static char
get_config_entry(const char * what)596 get_config_entry(const char *what)
597 {
598 	struct config *c0;
599 
600 	TAILQ_FOREACH(c0, &rootConfig, entry) {
601 		if (c0->value == 'd')
602 			continue;
603 		if (strcmp(c0->name, what) == 0)
604 			return (c0->value);
605 	}
606 
607 	new_config(strcatdup(what, ""), NULL, 'n');
608 
609 	return ('n');
610 }
611 
612 static void
variable_substitute(char * operator)613 variable_substitute(char *operator)
614 {
615 	char *pt;
616 	int len;
617 
618 	if (operator == NULL)
619 		return;
620 
621 	pt = operator;
622 	len = strlen(pt);
623 
624 	if (operator[1] == '(') {
625 		operator += 2;
626 		len -= 2;
627 	}
628 	if ((len != 0) && (operator[len - 1] == ')')) {
629 		len--;
630 		operator[len] = 0;
631 	}
632 	pt[0] = get_config_entry(operator);
633 	pt[1] = 0;
634 }
635 
636 static void
parse_config(char * path)637 parse_config(char *path)
638 {
639 	char *file;
640 	char *old_file;
641 	char *ptr;
642 	char *temp;
643 	char *keyword;
644 	int line;
645 	int c;
646 
647 	line = 0;
648 	old_file = file = load_file(strcatdup(path, ""));
649 	if (file == NULL)
650 		errx(EX_NOINPUT, "File '%sconfig' does not exist.", path);
651 
652 	while ((ptr = read_line(&file, &line))) {
653 
654 		skip_while(&ptr, "\t\r ");
655 
656 		/* discard comments */
657 		if (*ptr == '#')
658 			continue;
659 
660 		/* get next word */
661 		temp = ptr;
662 		skip_until(&temp, "\t\r =#");
663 		if (temp == ptr)
664 			continue;
665 
666 		/* duplicate word */
667 		c = *temp;
668 		*temp = 0;
669 		keyword = strcatdup(ptr, "");
670 		*temp = c;
671 
672 		/* skip operator */
673 		ptr = temp;
674 		skip_while(&temp, "\t\r =");
675 		if (temp == ptr) {
676 			free(keyword);
677 			continue;
678 		}
679 		/* figure out value */
680 		switch (temp[0]) {
681 		case 'y':
682 			new_config(keyword, NULL, 'y');
683 			break;
684 		case 'm':
685 			new_config(keyword, NULL, 'm');
686 			break;
687 		case 'n':
688 			new_config(keyword, NULL, 'n');
689 			break;
690 		case '\"':
691 		case '0':
692 		case '1':
693 		case '2':
694 		case '3':
695 		case '4':
696 		case '5':
697 		case '6':
698 		case '7':
699 		case '8':
700 		case '9':
701 			new_config(keyword, strcatdup(temp, ""), 'd');
702 			break;
703 		default:
704 			errx(EX_NOINPUT, "Invalid configuration "
705 			    "value '%c' at line %d.", temp[0], line);
706 			break;
707 		}
708 	}
709 	free(old_file);
710 }
711 
712 static void
parse_makefile(char * path)713 parse_makefile(char *path)
714 {
715 	static int recurse;
716 
717 	struct node *node;
718 	char *file;
719 	char *old_file;
720 	char *ptr;
721 	char *temp;
722 	char *parent;
723 	char *child;
724 	struct directory *dir;
725 	int line;
726 	int skip = 0;
727 	char c;
728 
729 	if (recurse > 64) {
730 		errx(EX_SOFTWARE, "Recursive parse Makefile "
731 		    "limit of 64 exceeded in '%s'.", path);
732 	}
733 	recurse++;
734 
735 	dir = new_directory();
736 	dir->name = strcatdup(path, "");
737 
738 	TAILQ_INSERT_TAIL(&rootDirectory, dir, entry);
739 
740 	line = 0;
741 	old_file = file = load_file(strcatdup(path, "Makefile"));
742 	if (file == NULL)
743 		errx(EX_NOINPUT, "File '%sMakefile' does not exist.", path);
744 
745 	while ((ptr = read_line(&file, &line))) {
746 
747 		if (opt_verbose > 1)
748 			fprintf(stderr, "line %d = %s\n", line, ptr);
749 
750 		skip_while(&ptr, "\t\r ");
751 
752 		/* discard comments */
753 		if (*ptr == '#')
754 			continue;
755 
756 		/* get next word */
757 		temp = ptr;
758 		skip_until(&temp, "\t\r :+=#");
759 		if (temp == ptr)
760 			continue;
761 
762 		/* duplicate word */
763 		c = *temp;
764 		*temp = 0;
765 		parent = strcatdup(ptr, "");
766 		*temp = c;
767 
768 		/* test for ifdef */
769 		if (strcmp(parent, "ifdef") == 0 ||
770 		    strcmp(parent, "ifndef") == 0) {
771 
772 			char *a;
773 			char b;
774 
775 			if (opt_verbose > 2) {
776 				fprintf(stderr, "Found %s at line "
777 				    "%d level %d\n", parent, line, skip);
778 			}
779 			if (skip) {
780 				skip++;
781 				free(parent);
782 				continue;
783 			}
784 			/* pass all ifdefs */
785 
786 			/* very simple expression matcher */
787 
788 			ptr = temp;
789 			skip_while(&ptr, "\t\r ");
790 
791 			a = ptr;
792 
793 			skip_until(&ptr, "\t\r #");
794 
795 			c = *ptr;
796 			*ptr = 0;
797 			a = strcatdup(a, "");
798 			*ptr = c;
799 
800 			b = get_config_entry(a);
801 
802 			if (opt_verbose > 2) {
803 				fprintf(stderr, "Testing %s %c\n",
804 				    parent, b);
805 			}
806 			if (strcmp(parent, "ifdef") == 0) {
807 				if (b == 'n')
808 					skip++;
809 			} else {
810 				if (b != 'n')
811 					skip++;
812 			}
813 
814 			free(a);
815 			free(parent);
816 			continue;
817 		}
818 		/* test for ifeq and ifneq */
819 		if (strcmp(parent, "ifeq") == 0 ||
820 		    strcmp(parent, "ifneq") == 0) {
821 
822 			char *a;
823 			char *b;
824 
825 			if (opt_verbose > 2) {
826 				fprintf(stderr, "Found %s at line "
827 				    "%d level %d\n", parent, line, skip);
828 			}
829 			if (skip) {
830 				skip++;
831 				free(parent);
832 				continue;
833 			}
834 			/* very simple expression matcher */
835 
836 			ptr = temp;
837 			skip_while(&ptr, "\t\r ");
838 			if (ptr[0] != '(') {
839 				if (opt_verbose > 2) {
840 					fprintf(stderr, "Syntax error %s "
841 					    "at line %d level %d\n",
842 					    parent, line, skip);
843 				}
844 				skip++;
845 				free(parent);
846 				continue;
847 			}
848 			ptr++;
849 
850 			a = ptr;
851 			skip_until_tp(&ptr, "\t\r ,");
852 
853 			c = *ptr;
854 			*ptr = 0;
855 			a = strcatdup(a, "");
856 			*ptr = c;
857 
858 			skip_while(&ptr, "\t\r ,");
859 			b = ptr;
860 
861 			skip_until_tp(&ptr, "\t\r )");
862 
863 			c = *ptr;
864 			*ptr = 0;
865 			b = strcatdup(b, "");
866 			*ptr = c;
867 
868 			variable_substitute(strchr(a, '$'));
869 			variable_substitute(strchr(b, '$'));
870 
871 			if (opt_verbose > 2) {
872 				fprintf(stderr, "Comparing %s(%s,%s)\n",
873 				    parent, a, b);
874 			}
875 			if (strcmp(parent, "ifeq") == 0) {
876 				if (strcmp(a, b) != 0)
877 					skip++;
878 			} else {
879 				if (strcmp(a, b) == 0)
880 					skip++;
881 			}
882 			free(parent);
883 			free(a);
884 			free(b);
885 			continue;
886 		}
887 		/* test for ifeq and ifneq */
888 		if (strcmp(parent, "endif") == 0) {
889 
890 			if (opt_verbose > 2) {
891 				fprintf(stderr, "Found endif at line "
892 				    "%d level %d\n", line, skip);
893 			}
894 			if (skip)
895 				skip--;
896 
897 			free(parent);
898 			continue;
899 		}
900 		/* check if inside conditional */
901 		if (skip) {
902 			free(parent);
903 			continue;
904 		}
905 		/* perform any variable substitution */
906 		variable_substitute(strchr(parent, '$'));
907 
908 		/* get next operator, if any */
909 		ptr = temp;
910 		skip_while(&ptr, "\t\r ");
911 		temp = ptr;
912 		skip_while(&temp, ":+=");
913 		if (temp == ptr) {
914 			free(parent);
915 			continue;
916 		}
917 		node = add_node(&rootNode, parent);
918 
919 		while (1) {
920 
921 			int len;
922 
923 			ptr = temp;
924 			skip_while(&ptr, "\t\r ");
925 			temp = ptr;
926 			skip_until(&temp, "\t\r #");
927 			if (temp == ptr)
928 				break;
929 
930 			c = *temp;
931 			*temp = 0;
932 			child = strcatdup(ptr, "");
933 			*temp = c;
934 
935 			len = strlen(child);
936 			if (child[len - 1] == '/') {
937 				if (strcmp(node->name, "obj-y") == 0 ||
938 				    strcmp(node->name, "obj-m") == 0)
939 					parse_makefile(strcatdup(path, child));
940 
941 			} else if (match_exclude(child) == 0) {
942 				add_child(node, strcatdup(path, ""), child);
943 			}
944 		}
945 	}
946 
947 	free(old_file);
948 
949 	free(path);
950 
951 	recurse--;
952 }
953 
954 static void
filter_objname(char * pch)955 filter_objname(char *pch)
956 {
957 	while (*pch) {
958 		if (*pch == '.' || *pch == '/' || *pch == '\\' || !isprint(*pch))
959 			*pch = '-';
960 		pch++;
961 	}
962 }
963 
964 static void
build_id(struct node * ptr,const char * name)965 build_id(struct node *ptr, const char *name)
966 {
967 	ptr->objprefix = strdup(ptr->path);
968 	if (ptr->objprefix == NULL)
969 		errx(EX_SOFTWARE, "Out of memory.");
970 
971 	filter_objname(ptr->objprefix);
972 
973 	if (obj_has_children(&rootNode, name, strlen(name))) {
974 		if (opt_verbose > 1) {
975 			fprintf(stderr, "object %s has children\n", name);
976 		}
977 		ptr->has_children = 1;
978 	} else {
979 		ptr->has_children = 0;
980 	}
981 }
982 
983 static void
build_source(struct node * ptr,const char * name)984 build_source(struct node *ptr, const char *name)
985 {
986 	char *objname;
987 
988 	if (ptr->has_children)
989 		return;
990 
991 	objname = strdup(name);
992 	filter_objname(objname);
993 
994 	printf("obj-%s%s.o: %s%s.c\n", ptr->objprefix, objname, ptr->path, name);
995 	printf("\t" "${CC} -c -DCURR_FILE_NAME=\\\"%s\\\" ${CFLAGS} -o obj-%s%s.o %s%s.c\n",
996 	    name, ptr->objprefix, objname, ptr->path, name);
997 
998 	free(objname);
999 }
1000 
1001 static void
build_objects(struct node * ptr,const char * name)1002 build_objects(struct node *ptr, const char *name)
1003 {
1004 	char *objname;
1005 
1006 	if (ptr->has_children)
1007 		return;
1008 
1009 	objname = strdup(name);
1010 	filter_objname(objname);
1011 
1012 	printf("\t" "obj-%s%s.o \\\n", ptr->objprefix, objname);
1013 
1014 	free(objname);
1015 }
1016 
1017 static void
output_makefile(char * name)1018 output_makefile(char *name)
1019 {
1020 	struct node *n0;
1021 	struct node *n1;
1022 	struct directory *dir;
1023 
1024 	printf("#\n"
1025 	    "# This makefile was automatically generated. Do not edit!\n"
1026 	    "#\n"
1027 	    "\n");
1028 
1029 	printf("PKGPATHS+= \\\n");
1030 
1031 	TAILQ_FOREACH(dir, &rootDirectory, entry) {
1032 		int c;
1033 		int len;
1034 
1035 		len = strlen(dir->name);
1036 		if (len > 0)
1037 			c = dir->name[len - 1];
1038 		else
1039 			c = 0;
1040 
1041 		if (c == '/')
1042 			dir->name[len - 1] = 0;
1043 
1044 		printf("%s \\\n", dir->name);
1045 
1046 		if (c == '/')
1047 			dir->name[len - 1] = c;
1048 	}
1049 
1050 	n0 = add_node(&rootNode, strcatdup(name, ""));
1051 
1052 	TAILQ_FOREACH(n1, &n0->children, entry) {
1053 		objs_exec(n1, &build_id);
1054 	}
1055 
1056 	printf("\n"
1057 	    "OBJS+= \\\n");
1058 
1059 	TAILQ_FOREACH(n1, &n0->children, entry) {
1060 		objs_exec(n1, &build_objects);
1061 	}
1062 
1063 	printf("\n");
1064 
1065 	TAILQ_FOREACH(n1, &n0->children, entry) {
1066 		objs_exec(n1, &build_source);
1067 	}
1068 
1069 	printf("\n");
1070 
1071 	printf(".if defined(LIB)\n"
1072 	    ".include \"${.CURDIR}/../../Makefile.lib\"\n"
1073 	    ".endif\n");
1074 }
1075 
1076 static void
set_stdout(char * name)1077 set_stdout(char *name)
1078 {
1079 	static int f = -1;
1080 
1081 	fflush(stdout);
1082 
1083 	if (f > -1)
1084 		close(f);
1085 
1086 	f = open(name, O_WRONLY | O_TRUNC | O_CREAT, 0660);
1087 
1088 	free(name);
1089 
1090 	if (f < 0)
1091 		err(EX_NOINPUT, "Could not set standard out");
1092 
1093 	if (dup2(f, STDOUT_FILENO) < 0)
1094 		err(EX_NOINPUT, "Could not duplicate file descriptor");
1095 }
1096 
1097 static char *
add_slashdup(char * ptr)1098 add_slashdup(char *ptr)
1099 {
1100 	int len = strlen(ptr);
1101 
1102 	if (len && (ptr[len - 1] != '/'))
1103 		return (strcatdup(ptr, "/"));
1104 	return (strcatdup(ptr, ""));
1105 }
1106 
1107 static char *
fname(char * ptr)1108 fname(char *ptr)
1109 {
1110 	char *old = ptr;
1111 
1112 	while (*ptr) {
1113 		if (*ptr == '/')
1114 			old = ptr + 1;
1115 		ptr++;
1116 	}
1117 	return (old);
1118 }
1119 
1120 static void
usage(void)1121 usage(void)
1122 {
1123 	fprintf(stderr,
1124 	    "usage: linux_make -c config -o . -i linuxA/ -i linuxB/\n"
1125 	    "	-c <config-file>\n"
1126 	    "	-i <input-directory>\n"
1127 	    "	-o <output-directory>\n"
1128 	    "	-x <exclude-object>\n"
1129 	    "	-h show help message\n"
1130 	    "	-v increase verbosity\n"
1131 	);
1132 	exit(EX_USAGE);
1133 }
1134 
1135 static const char *targets[] = {
1136 	"all",
1137 	"clean",
1138 	"cleandepend",
1139 	"depend",
1140 	NULL
1141 };
1142 
1143 int
main(int argc,char ** argv)1144 main(int argc, char **argv)
1145 {
1146 	const char *params = "c:i:o:hvx:";
1147 	struct node *n0;
1148 	struct node *n1;
1149 	struct makefile *m0;
1150 	struct config *c0;
1151 	int c;
1152 
1153 	while ((c = getopt(argc, argv, params)) != -1) {
1154 		switch (c) {
1155 		case 'c':
1156 			opt_config = optarg;
1157 			break;
1158 		case 'i':
1159 			opt_input = optarg;
1160 			break;
1161 		case 'o':
1162 			opt_output = optarg;
1163 			break;
1164 		case 'v':
1165 			opt_verbose++;
1166 			break;
1167 		case 'x':
1168 			new_exclude(optarg);
1169 			break;
1170 		default:
1171 			usage();
1172 			break;
1173 		}
1174 	}
1175 
1176 	if (opt_input == NULL || opt_output == NULL)
1177 		usage();
1178 
1179 	opt_output = add_slashdup(opt_output);
1180 
1181 	parse_config(opt_config);
1182 
1183 	optreset = 1;
1184 	optind = 1;
1185 
1186 	while ((c = getopt(argc, argv, params)) != -1) {
1187 		switch (c) {
1188 		case 'i':
1189 			opt_input = add_slashdup(optarg);
1190 			parse_makefile(opt_input);
1191 			opt_input = NULL;
1192 			break;
1193 		default:
1194 			break;
1195 		}
1196 	}
1197 
1198 	resolve_nodes();
1199 
1200 	if (opt_verbose > 1)
1201 		dump_nodes(&rootNode);
1202 
1203 	if (mkdir(strcatdup(opt_output, "obj-y"), 0755) != 0 &&
1204 	    errno != EEXIST)
1205 		err(EX_NOINPUT, "Could not create output directory.");
1206 
1207 	set_stdout(strcatdup(opt_output, "obj-y/Makefile"));
1208 
1209 	new_makefile(strcatdup(opt_output, "obj-y"));
1210 
1211 	output_makefile("obj-y");
1212 
1213 	n0 = add_node(&rootNode, strcatdup("obj-m", ""));
1214 
1215 	TAILQ_FOREACH(n1, &n0->children, entry) {
1216 		char *temp;
1217 		char *name;
1218 		int len;
1219 
1220 		temp = strcatdup(n1->name, "");
1221 		len = strlen(temp);
1222 
1223 		if ((len >= 2) && (temp[len - 2] == '.' &&
1224 		    temp[len - 1] == 'o')) {
1225 			temp[len - 2] = 0;
1226 			len -= 2;
1227 		}
1228 		name = strcatdup(temp, "-objs");
1229 		free(temp);
1230 
1231 		temp = strcatdup(opt_output, name);
1232 		free(name);
1233 
1234 		if (mkdir(temp, 0755) != 0 && errno != EEXIST)
1235 			err(EX_NOINPUT, "Could not create output directory.");
1236 
1237 		set_stdout(strcatdup(temp, "/Makefile"));
1238 
1239 		new_makefile(strcatdup(temp, ""));
1240 
1241 		output_makefile(temp + strlen(opt_output));
1242 
1243 		free(temp);
1244 	}
1245 
1246 	set_stdout(strcatdup(opt_output, "Makefile.top"));
1247 
1248 	printf("#\n"
1249 	    "# This makefile was automatically generated. Do not edit!\n"
1250 	    "#\n");
1251 
1252 	printf("\nMAKE?=make\n");
1253 	printf("\nMAKE_ARGS?=\n");
1254 
1255 	for (c = 0; targets[c]; c++) {
1256 		printf("\n%s:\n", targets[c]);
1257 		TAILQ_FOREACH(m0, &rootMakefile, entry) {
1258 			char *ptr;
1259 
1260 			ptr = fname(m0->name);
1261 			printf("\tcd %s; ${MAKE} LIB=%s%s ${MAKE_ARGS} %s\n",
1262 			    ptr, ptr, strcmp(ptr, "obj-y") ?
1263 			    " MODULE=YES" : "", targets[c]);
1264 		}
1265 	}
1266 
1267 	printf("\ninstall:\n");
1268 	printf("\t[ -d modules] || mkdir modules\n");
1269 	TAILQ_FOREACH(m0, &rootMakefile, entry) {
1270 		printf("\tcp -v %s/%s{.a,.so} modules/\n",
1271 		    m0->name, fname(m0->name));
1272 	}
1273 
1274 	set_stdout(strcatdup(opt_output, "config.h"));
1275 
1276 	sort_config();
1277 
1278 	printf("/*\n"
1279 	    " * This file was autogenerated. Please do not edit.\n"
1280 	    " */\n\n");
1281 
1282 	printf("#ifndef _ROOT_CONFIG_H_\n"
1283 	    "#define\t_ROOT_CONFIG_H_\n\n");
1284 
1285 	TAILQ_FOREACH(c0, &rootConfig, entry) {
1286 		switch (c0->value) {
1287 		case 'm':
1288 			printf("#define\t%s /* m */\n", c0->name);
1289 			printf("#define\t%s_IS_ENABLED 1\n", c0->name);
1290 			printf("#define\t%s_IS_MODULE 1\n", c0->name);
1291 			break;
1292 		case 'y':
1293 			printf("#define\t%s /* y */\n", c0->name);
1294 			printf("#define\t%s_IS_MODULE 0\n", c0->name);
1295 			printf("#define\t%s_IS_ENABLED 1\n", c0->name);
1296 			break;
1297 		case 'd':
1298 			printf("#define\t%s %s /* d */\n", c0->name, c0->orig);
1299 			break;
1300 		default:
1301 			printf("#undef\t%s /* %c */\n", c0->name, c0->value);
1302 			printf("#define\t%s_IS_MODULE 0\n", c0->name);
1303 			printf("#define\t%s_IS_ENABLED 0\n", c0->name);
1304 			break;
1305 		}
1306 	}
1307 
1308 	printf("\n#endif\t\t\t/* _ROOT_CONFIG_H_ */\n");
1309 
1310 	return (0);
1311 }
1312