xref: /freebsd/usr.sbin/config/mkmakefile.cc (revision ee97f198b42d50437f87aa4111d478eca2a5be16)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1980, 1990, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #ifndef lint
33 #if 0
34 static char sccsid[] = "@(#)mkmakefile.c	8.1 (Berkeley) 6/6/93";
35 #endif
36 static const char rcsid[] =
37   "$FreeBSD$";
38 #endif /* not lint */
39 
40 /*
41  * Build the makefile for the system, from
42  * the information in the files files and the
43  * additional files for the machine being compiled to.
44  */
45 #include <sys/param.h>
46 
47 #include <ctype.h>
48 #include <err.h>
49 #include <stdarg.h>
50 #include <stdbool.h>
51 #include <stdio.h>
52 #include <string.h>
53 #include <string>
54 #include <unordered_map>
55 
56 #include "y.tab.h"
57 #include "config.h"
58 #include "configvers.h"
59 
60 typedef std::unordered_map<std::string, std::string>	env_map;
61 
62 static char *tail(char *);
63 static void do_clean(FILE *);
64 static void do_rules(FILE *);
65 static void do_xxfiles(char *, FILE *);
66 static void do_objs(FILE *);
67 static void do_before_depend(FILE *);
68 static void read_files(void);
69 static void sanitize_envline(char *result, const char *src);
70 static bool preprocess(char *line, char *result);
71 static void process_into_file(char *line, FILE *ofp);
72 static int process_into_map(char *line, env_map &emap);
73 static void dump_map(env_map &emap, FILE *ofp);
74 
75 static void __printflike(1, 2)
76 errout(const char *fmt, ...)
77 {
78 	va_list ap;
79 
80 	va_start(ap, fmt);
81 	vfprintf(stderr, fmt, ap);
82 	va_end(ap);
83 	exit(1);
84 }
85 
86 /*
87  * Lookup a file, by name.
88  */
89 static struct file_list *
90 fl_lookup(char *file)
91 {
92 	struct file_list *fp;
93 
94 	STAILQ_FOREACH(fp, &ftab, f_next) {
95 		if (eq(fp->f_fn, file))
96 			return (fp);
97 	}
98 	return (0);
99 }
100 
101 /*
102  * Make a new file list entry
103  */
104 static struct file_list *
105 new_fent(void)
106 {
107 	struct file_list *fp;
108 
109 	fp = (struct file_list *) calloc(1, sizeof *fp);
110 	if (fp == NULL)
111 		err(EXIT_FAILURE, "calloc");
112 	STAILQ_INSERT_TAIL(&ftab, fp, f_next);
113 	return (fp);
114 }
115 
116 /*
117  * Open the correct Makefile and return it, or error out.
118  */
119 FILE *
120 open_makefile_template(void)
121 {
122 	FILE *ifp;
123 	char line[BUFSIZ];
124 
125 	snprintf(line, sizeof(line), "../../conf/Makefile.%s", machinename);
126 	ifp = fopen(line, "r");
127 	if (ifp == NULL) {
128 		snprintf(line, sizeof(line), "Makefile.%s", machinename);
129 		ifp = fopen(line, "r");
130 	}
131 	if (ifp == NULL)
132 		err(1, "%s", line);
133 	return (ifp);
134 }
135 
136 /*
137  * Build the makefile from the skeleton
138  */
139 void
140 makefile(void)
141 {
142 	FILE *ifp, *ofp;
143 	char line[BUFSIZ];
144 	struct opt *op, *t;
145 
146 	read_files();
147 	ifp = open_makefile_template();
148 	ofp = fopen(path("Makefile.new"), "w");
149 	if (ofp == NULL)
150 		err(1, "%s", path("Makefile.new"));
151 	fprintf(ofp, "KERN_IDENT=%s\n", ident);
152 	fprintf(ofp, "MACHINE=%s\n", machinename);
153 	fprintf(ofp, "MACHINE_ARCH=%s\n", machinearch);
154 	SLIST_FOREACH_SAFE(op, &mkopt, op_next, t) {
155 		fprintf(ofp, "%s=%s", op->op_name, op->op_value);
156 		while ((op = SLIST_NEXT(op, op_append)) != NULL)
157 			fprintf(ofp, " %s", op->op_value);
158 		fprintf(ofp, "\n");
159 	}
160 	if (debugging)
161 		fprintf(ofp, "DEBUG=-g\n");
162 	if (*srcdir != '\0')
163 		fprintf(ofp,"S=%s\n", srcdir);
164 	while (fgets(line, BUFSIZ, ifp) != NULL) {
165 		if (*line != '%') {
166 			fprintf(ofp, "%s", line);
167 			continue;
168 		}
169 		if (eq(line, "%BEFORE_DEPEND\n"))
170 			do_before_depend(ofp);
171 		else if (eq(line, "%OBJS\n"))
172 			do_objs(ofp);
173 		else if (strncmp(line, "%FILES.", 7) == 0)
174 			do_xxfiles(line, ofp);
175 		else if (eq(line, "%RULES\n"))
176 			do_rules(ofp);
177 		else if (eq(line, "%CLEAN\n"))
178 			do_clean(ofp);
179 		else if (strncmp(line, "%VERSREQ=", 9) == 0)
180 			line[0] = '\0'; /* handled elsewhere */
181 		else
182 			fprintf(stderr,
183 			    "Unknown %% construct in generic makefile: %s",
184 			    line);
185 	}
186 	(void) fclose(ifp);
187 	(void) fclose(ofp);
188 	moveifchanged("Makefile.new", "Makefile");
189 }
190 
191 static void
192 sanitize_envline(char *result, const char *src)
193 {
194 	const char *eq;
195 	char c, *dst;
196 	bool leading;
197 
198 	/* If there is no '=' it's not a well-formed name=value line. */
199 	if ((eq = strchr(src, '=')) == NULL) {
200 		*result = '\0';
201 		return;
202 	}
203 	dst = result;
204 
205 	/* Copy chars before the '=', skipping any leading spaces/quotes. */
206 	leading = true;
207 	while (src < eq) {
208 		c = *src++;
209 		if (leading && (isspace(c) || c == '"'))
210 			continue;
211 		*dst++ = c;
212 		leading = false;
213 	}
214 
215 	/* If it was all leading space, we don't have a well-formed line. */
216 	if (leading) {
217 		*result = '\0';
218 		return;
219 	}
220 
221 	/* Trim spaces/quotes immediately before the '=', then copy the '='. */
222 	while (isspace(dst[-1]) || dst[-1] == '"')
223 		--dst;
224 	*dst++ = *src++;
225 
226 	/* Copy chars after the '=', skipping any leading whitespace. */
227 	leading = true;
228 	while ((c = *src++) != '\0') {
229 		if (leading && (isspace(c) || c == '"'))
230 			continue;
231 		*dst++ = c;
232 		leading = false;
233 	}
234 
235 	/* If it was all leading space, it's a valid 'var=' (nil value). */
236 	if (leading) {
237 		*dst = '\0';
238 		return;
239 	}
240 
241 	/* Trim trailing whitespace and quotes. */
242 	while (isspace(dst[-1]) || dst[-1] == '"')
243 		--dst;
244 
245 	*dst = '\0';
246 }
247 
248 /*
249  * Returns true if the caller may use the string.
250  */
251 static bool
252 preprocess(char *line, char *result)
253 {
254 	char *s;
255 
256 	/* Strip any comments */
257 	if ((s = strchr(line, '#')) != NULL)
258 		*s = '\0';
259 	sanitize_envline(result, line);
260 	/* Return true if it's non-empty */
261 	return (*result != '\0');
262 }
263 
264 static void
265 process_into_file(char *line, FILE *ofp)
266 {
267 	char result[BUFSIZ];
268 
269 	if (preprocess(line, result))
270 		fprintf(ofp, "\"%s\\0\"\n", result);
271 }
272 
273 static int
274 process_into_map(char *line, env_map &emap)
275 {
276 	char result[BUFSIZ], *s;
277 
278 	if (preprocess(line, result)) {
279 		s = strchr(result, '=');
280 		if (s == NULL)
281 			return (EINVAL);
282 		*s = '\0';
283 		emap[result] = s + 1;
284 	}
285 
286 	return (0);
287 }
288 
289 static void
290 dump_map(env_map &emap, FILE *ofp)
291 {
292 
293 	for (auto iter : emap) {
294 		fprintf(ofp, "\"%s=%s\\0\"\n", iter.first.c_str(),
295 		    iter.second.c_str());
296 	}
297 }
298 
299 /*
300  * Build hints.c from the skeleton
301  */
302 void
303 makehints(void)
304 {
305 	FILE *ifp, *ofp;
306 	env_map emap;
307 	char line[BUFSIZ];
308 	struct hint *hint;
309 
310 	ofp = fopen(path("hints.c.new"), "w");
311 	if (ofp == NULL)
312 		err(1, "%s", path("hints.c.new"));
313 	fprintf(ofp, "#include <sys/types.h>\n");
314 	fprintf(ofp, "#include <sys/systm.h>\n");
315 	fprintf(ofp, "\n");
316 	/*
317 	 * Write out hintmode for older kernels. Remove when config(8) major
318 	 * version rolls over.
319 	 */
320 	if (versreq <= CONFIGVERS_ENVMODE_REQ)
321 		fprintf(ofp, "int hintmode = %d;\n",
322 			!STAILQ_EMPTY(&hints) ? 1 : 0);
323 	fprintf(ofp, "char static_hints[] = {\n");
324 	STAILQ_FOREACH(hint, &hints, hint_next) {
325 		ifp = fopen(hint->hint_name, "r");
326 		if (ifp == NULL)
327 			err(1, "%s", hint->hint_name);
328 		while (fgets(line, BUFSIZ, ifp) != NULL) {
329 			if (process_into_map(line, emap) != 0)
330 				errout("%s: malformed line: %s\n",
331 				    hint->hint_name, line);
332 		}
333 		dump_map(emap, ofp);
334 		fclose(ifp);
335 	}
336 	fprintf(ofp, "\"\\0\"\n};\n");
337 	fclose(ofp);
338 	moveifchanged("hints.c.new", "hints.c");
339 }
340 
341 /*
342  * Build env.c from the skeleton
343  */
344 void
345 makeenv(void)
346 {
347 	FILE *ifp, *ofp;
348 	env_map emap;
349 	char line[BUFSIZ];
350 	struct envvar *envvar;
351 
352 	ofp = fopen(path("env.c.new"), "w");
353 	if (ofp == NULL)
354 		err(1, "%s", path("env.c.new"));
355 	fprintf(ofp, "#include <sys/types.h>\n");
356 	fprintf(ofp, "#include <sys/systm.h>\n");
357 	fprintf(ofp, "\n");
358 	/*
359 	 * Write out envmode for older kernels. Remove when config(8) major
360 	 * version rolls over.
361 	 */
362 	if (versreq <= CONFIGVERS_ENVMODE_REQ)
363 		fprintf(ofp, "int envmode = %d;\n",
364 			!STAILQ_EMPTY(&envvars) ? 1 : 0);
365 	fprintf(ofp, "char static_env[] = {\n");
366 	STAILQ_FOREACH(envvar, &envvars, envvar_next) {
367 		if (envvar->env_is_file) {
368 			ifp = fopen(envvar->env_str, "r");
369 			if (ifp == NULL)
370 				err(1, "%s", envvar->env_str);
371 			while (fgets(line, BUFSIZ, ifp) != NULL) {
372 				if (process_into_map(line, emap) != 0)
373 					errout("%s: malformed line: %s\n",
374 					    envvar->env_str, line);
375 			}
376 			dump_map(emap, ofp);
377 			fclose(ifp);
378 		} else
379 			process_into_file(envvar->env_str, ofp);
380 	}
381 	fprintf(ofp, "\"\\0\"\n};\n");
382 	fclose(ofp);
383 	moveifchanged("env.c.new", "env.c");
384 }
385 
386 static void
387 read_file(char *fname)
388 {
389 	char ifname[MAXPATHLEN];
390 	FILE *fp;
391 	struct file_list *tp;
392 	struct device *dp;
393 	struct opt *op;
394 	configword wd;
395 	char *rfile, *compilewith, *depends, *clean, *warning;
396 	const char *objprefix;
397 	int compile, match, nreqs, std, filetype, negate,
398 	    imp_rule, no_ctfconvert, no_obj, before_depend, nowerror;
399 
400 	fp = fopen(fname, "r");
401 	if (fp == NULL)
402 		err(1, "%s", fname);
403 next:
404 	/*
405 	 * include "filename"
406 	 * filename    [ standard | optional ]
407 	 *	[ dev* [ | dev* ... ] | [ no-obj ]
408 	 *	[ compile-with "compile rule" [no-implicit-rule] ]
409 	 *      [ dependency "dependency-list"] [ before-depend ]
410 	 *	[ clean "file-list"] [ warning "text warning" ]
411 	 *	[ obj-prefix "file prefix"]
412 	 *	[ nowerror ] [ local ]
413 	 */
414 	wd = get_word(fp);
415 	if (wd.eof()) {
416 		(void) fclose(fp);
417 		return;
418 	}
419 	if (wd.eol())
420 		goto next;
421 	if (wd[0] == '#')
422 	{
423 		while (!(wd = get_word(fp)).eof() && !wd.eol())
424 			;
425 		goto next;
426 	}
427 	if (eq(wd, "include")) {
428 		wd = get_quoted_word(fp);
429 		if (wd.eof() || wd.eol())
430 			errout("%s: missing include filename.\n", fname);
431 		(void) snprintf(ifname, sizeof(ifname), "../../%s",
432 		    wd->c_str());
433 		read_file(ifname);
434 		while (!(wd = get_word(fp)).eof() && !wd.eol())
435 			;
436 		goto next;
437 	}
438 	rfile = ns(wd);
439 	wd = get_word(fp);
440 	if (wd.eof())
441 		return;
442 	if (wd.eol())
443 		errout("%s: No type for %s.\n", fname, rfile);
444 	tp = fl_lookup(rfile);
445 	compile = 0;
446 	match = 1;
447 	nreqs = 0;
448 	compilewith = NULL;
449 	depends = NULL;
450 	clean = NULL;
451 	warning = NULL;
452 	std = 0;
453 	imp_rule = 0;
454 	no_ctfconvert = 0;
455 	no_obj = 0;
456 	before_depend = 0;
457 	nowerror = 0;
458 	negate = 0;
459 	filetype = NORMAL;
460 	objprefix = "";
461 	if (eq(wd, "standard"))
462 		std = 1;
463 	else if (!eq(wd, "optional"))
464 		errout("%s: \"%s\" %s must be optional or standard\n",
465 		    fname, wd->c_str(), rfile);
466 	for (wd = get_word(fp); !wd.eol(); wd = get_word(fp)) {
467 		if (wd.eof())
468 			return;
469 		if (eq(wd, "!")) {
470 			negate = 1;
471 			continue;
472 		}
473 		if (eq(wd, "|")) {
474 			if (nreqs == 0)
475 				errout("%s: syntax error describing %s\n",
476 				       fname, rfile);
477 			compile += match;
478 			match = 1;
479 			nreqs = 0;
480 			continue;
481 		}
482 		if (eq(wd, "no-ctfconvert")) {
483 			no_ctfconvert++;
484 			continue;
485 		}
486 		if (eq(wd, "no-obj")) {
487 			no_obj++;
488 			continue;
489 		}
490 		if (eq(wd, "no-implicit-rule")) {
491 			if (compilewith == NULL)
492 				errout("%s: alternate rule required when "
493 				       "\"no-implicit-rule\" is specified for"
494 				       " %s.\n",
495 				       fname, rfile);
496 			imp_rule++;
497 			continue;
498 		}
499 		if (eq(wd, "before-depend")) {
500 			before_depend++;
501 			continue;
502 		}
503 		if (eq(wd, "dependency")) {
504 			wd = get_quoted_word(fp);
505 			if (wd.eof() || wd.eol())
506 				errout("%s: %s missing dependency string.\n",
507 				       fname, rfile);
508 			depends = ns(wd);
509 			continue;
510 		}
511 		if (eq(wd, "clean")) {
512 			wd = get_quoted_word(fp);
513 			if (wd.eof() || wd.eol())
514 				errout("%s: %s missing clean file list.\n",
515 				       fname, rfile);
516 			clean = ns(wd);
517 			continue;
518 		}
519 		if (eq(wd, "compile-with")) {
520 			wd = get_quoted_word(fp);
521 			if (wd.eof() || wd.eol())
522 				errout("%s: %s missing compile command string.\n",
523 				       fname, rfile);
524 			compilewith = ns(wd);
525 			continue;
526 		}
527 		if (eq(wd, "warning")) {
528 			wd = get_quoted_word(fp);
529 			if (wd.eof() || wd.eol())
530 				errout("%s: %s missing warning text string.\n",
531 				       fname, rfile);
532 			warning = ns(wd);
533 			continue;
534 		}
535 		if (eq(wd, "obj-prefix")) {
536 			wd = get_quoted_word(fp);
537 			if (wd.eof() || wd.eol())
538 				errout("%s: %s missing object prefix string.\n",
539 				       fname, rfile);
540 			objprefix = ns(wd);
541 			continue;
542 		}
543 		if (eq(wd, "nowerror")) {
544 			nowerror = 1;
545 			continue;
546 		}
547 		if (eq(wd, "local")) {
548 			filetype = LOCAL;
549 			continue;
550 		}
551 		if (eq(wd, "no-depend")) {
552 			filetype = NODEPEND;
553 			continue;
554 		}
555 		nreqs++;
556 		if (std)
557 			errout("standard entry %s has optional inclusion specifier %s!\n",
558 			       rfile, wd->c_str());
559 		STAILQ_FOREACH(dp, &dtab, d_next)
560 			if (eq(dp->d_name, wd)) {
561 				if (negate)
562 					match = 0;
563 				else
564 					dp->d_done |= DEVDONE;
565 				goto nextparam;
566 			}
567 		SLIST_FOREACH(op, &opt, op_next)
568 			if (op->op_value == 0 &&
569 			    strcasecmp(op->op_name, wd) == 0) {
570 				if (negate)
571 					match = 0;
572 				goto nextparam;
573 			}
574 		match &= negate;
575 nextparam:;
576 		negate = 0;
577 	}
578 	compile += match;
579 	if (compile && tp == NULL) {
580 		if (std == 0 && nreqs == 0)
581 			errout("%s: what is %s optional on?\n",
582 			       fname, rfile);
583 		tp = new_fent();
584 		tp->f_fn = rfile;
585 		tp->f_type = filetype;
586 		if (filetype == LOCAL)
587 			tp->f_srcprefix = "";
588 		else
589 			tp->f_srcprefix = "$S/";
590 		if (imp_rule)
591 			tp->f_flags |= NO_IMPLCT_RULE;
592 		if (no_ctfconvert)
593 			tp->f_flags |= NO_CTFCONVERT;
594 		if (no_obj)
595 			tp->f_flags |= NO_OBJ | NO_CTFCONVERT;
596 		if (before_depend)
597 			tp->f_flags |= BEFORE_DEPEND;
598 		if (nowerror)
599 			tp->f_flags |= NOWERROR;
600 		tp->f_compilewith = compilewith;
601 		tp->f_depends = depends;
602 		tp->f_clean = clean;
603 		tp->f_warn = warning;
604 		tp->f_objprefix = objprefix;
605 	}
606 	goto next;
607 }
608 
609 /*
610  * Read in the information about files used in making the system.
611  * Store it in the ftab linked list.
612  */
613 static void
614 read_files(void)
615 {
616 	char fname[MAXPATHLEN];
617 	struct files_name *nl, *tnl;
618 
619 	(void) snprintf(fname, sizeof(fname), "../../conf/files");
620 	read_file(fname);
621 	(void) snprintf(fname, sizeof(fname),
622 		       	"../../conf/files.%s", machinename);
623 	read_file(fname);
624 	for (nl = STAILQ_FIRST(&fntab); nl != NULL; nl = tnl) {
625 		read_file(nl->f_name);
626 		tnl = STAILQ_NEXT(nl, f_next);
627 		free(nl->f_name);
628 		free(nl);
629 	}
630 }
631 
632 static void
633 do_before_depend(FILE *fp)
634 {
635 	struct file_list *tp;
636 	int lpos, len;
637 
638 	fputs("BEFORE_DEPEND=", fp);
639 	lpos = 15;
640 	STAILQ_FOREACH(tp, &ftab, f_next)
641 		if (tp->f_flags & BEFORE_DEPEND) {
642 			len = strlen(tp->f_fn) + strlen(tp->f_srcprefix);
643 			if (len + lpos > 72) {
644 				lpos = 8;
645 				fputs("\\\n\t", fp);
646 			}
647 			if (tp->f_flags & NO_IMPLCT_RULE)
648 				lpos += fprintf(fp, "%s ", tp->f_fn);
649 			else
650 				lpos += fprintf(fp, "%s%s ", tp->f_srcprefix,
651 				    tp->f_fn);
652 		}
653 	if (lpos != 8)
654 		putc('\n', fp);
655 }
656 
657 static void
658 do_objs(FILE *fp)
659 {
660 	struct file_list *tp;
661 	int lpos, len;
662 	char *cp, och, *sp;
663 
664 	fprintf(fp, "OBJS=");
665 	lpos = 6;
666 	STAILQ_FOREACH(tp, &ftab, f_next) {
667 		if (tp->f_flags & NO_OBJ)
668 			continue;
669 		sp = tail(tp->f_fn);
670 		cp = sp + (len = strlen(sp)) - 1;
671 		och = *cp;
672 		*cp = 'o';
673 		len += strlen(tp->f_objprefix);
674 		if (len + lpos > 72) {
675 			lpos = 8;
676 			fprintf(fp, "\\\n\t");
677 		}
678 		fprintf(fp, "%s%s ", tp->f_objprefix, sp);
679 		lpos += len + 1;
680 		*cp = och;
681 	}
682 	if (lpos != 8)
683 		putc('\n', fp);
684 }
685 
686 static void
687 do_xxfiles(char *tag, FILE *fp)
688 {
689 	struct file_list *tp;
690 	int lpos, len, slen;
691 	char *suff, *SUFF;
692 
693 	if (tag[strlen(tag) - 1] == '\n')
694 		tag[strlen(tag) - 1] = '\0';
695 
696 	suff = ns(tag + 7);
697 	SUFF = ns(suff);
698 	raisestr(SUFF);
699 	slen = strlen(suff);
700 
701 	fprintf(fp, "%sFILES=", SUFF);
702 	free(SUFF);
703 	lpos = 8;
704 	STAILQ_FOREACH(tp, &ftab, f_next)
705 		if (tp->f_type != NODEPEND) {
706 			len = strlen(tp->f_fn);
707 			if (tp->f_fn[len - slen - 1] != '.')
708 				continue;
709 			if (strcasecmp(&tp->f_fn[len - slen], suff) != 0)
710 				continue;
711 			if (len + strlen(tp->f_srcprefix) + lpos > 72) {
712 				lpos = 8;
713 				fputs("\\\n\t", fp);
714 			}
715 			lpos += fprintf(fp, "%s%s ", tp->f_srcprefix, tp->f_fn);
716 		}
717 	free(suff);
718 	if (lpos != 8)
719 		putc('\n', fp);
720 }
721 
722 static char *
723 tail(char *fn)
724 {
725 	char *cp;
726 
727 	cp = strrchr(fn, '/');
728 	if (cp == NULL)
729 		return (fn);
730 	return (cp+1);
731 }
732 
733 /*
734  * Create the makerules for each file
735  * which is part of the system.
736  */
737 static void
738 do_rules(FILE *f)
739 {
740 	char *cp, *np, och;
741 	struct file_list *ftp;
742 	char *compilewith;
743 	char cmd[128];
744 
745 	STAILQ_FOREACH(ftp, &ftab, f_next) {
746 		if (ftp->f_warn)
747 			fprintf(stderr, "WARNING: %s\n", ftp->f_warn);
748 		cp = (np = ftp->f_fn) + strlen(ftp->f_fn) - 1;
749 		och = *cp;
750 		if (ftp->f_flags & NO_IMPLCT_RULE) {
751 			if (ftp->f_depends)
752 				fprintf(f, "%s%s: %s\n",
753 					ftp->f_objprefix, np, ftp->f_depends);
754 			else
755 				fprintf(f, "%s%s: \n", ftp->f_objprefix, np);
756 		}
757 		else {
758 			*cp = '\0';
759 			if (och == 'o') {
760 				fprintf(f, "%s%so:\n\t-cp %s%so .\n\n",
761 					ftp->f_objprefix, tail(np),
762 					ftp->f_srcprefix, np);
763 				continue;
764 			}
765 			if (ftp->f_depends) {
766 				fprintf(f, "%s%so: %s%s%c %s\n",
767 					ftp->f_objprefix, tail(np),
768 					ftp->f_srcprefix, np, och,
769 					ftp->f_depends);
770 			}
771 			else {
772 				fprintf(f, "%s%so: %s%s%c\n",
773 					ftp->f_objprefix, tail(np),
774 					ftp->f_srcprefix, np, och);
775 			}
776 		}
777 		compilewith = ftp->f_compilewith;
778 		if (compilewith == NULL) {
779 			const char *ftype = NULL;
780 
781 			switch (ftp->f_type) {
782 			case NORMAL:
783 				ftype = "NORMAL";
784 				break;
785 			default:
786 				fprintf(stderr,
787 				    "config: don't know rules for %s\n", np);
788 				break;
789 			}
790 			snprintf(cmd, sizeof(cmd),
791 			    "${%s_%c%s}", ftype,
792 			    toupper(och),
793 			    ftp->f_flags & NOWERROR ? "_NOWERROR" : "");
794 			compilewith = cmd;
795 		}
796 		*cp = och;
797 		if (strlen(ftp->f_objprefix))
798 			fprintf(f, "\t%s %s%s\n", compilewith,
799 			    ftp->f_srcprefix, np);
800 		else
801 			fprintf(f, "\t%s\n", compilewith);
802 
803 		if (!(ftp->f_flags & NO_CTFCONVERT))
804 			fprintf(f, "\t${NORMAL_CTFCONVERT}\n\n");
805 		else
806 			fprintf(f, "\n");
807 	}
808 }
809 
810 static void
811 do_clean(FILE *fp)
812 {
813 	struct file_list *tp;
814 	int lpos, len;
815 
816 	fputs("CLEAN=", fp);
817 	lpos = 7;
818 	STAILQ_FOREACH(tp, &ftab, f_next)
819 		if (tp->f_clean) {
820 			len = strlen(tp->f_clean);
821 			if (len + lpos > 72) {
822 				lpos = 8;
823 				fputs("\\\n\t", fp);
824 			}
825 			fprintf(fp, "%s ", tp->f_clean);
826 			lpos += len + 1;
827 		}
828 	if (lpos != 8)
829 		putc('\n', fp);
830 }
831 
832 char *
833 raisestr(char *str)
834 {
835 	char *cp = str;
836 
837 	while (*str) {
838 		if (islower(*str))
839 			*str = toupper(*str);
840 		str++;
841 	}
842 	return (cp);
843 }
844