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