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$";
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 <stdio.h>
48#include <string.h>
49#include <sys/param.h>
50#include "y.tab.h"
51#include "config.h"
52#include "configvers.h"
53
54static char *tail(char *);
55static void do_clean(FILE *);
56static void do_rules(FILE *);
57static void do_xxfiles(char *, FILE *);
58static void do_objs(FILE *);
59static void do_before_depend(FILE *);
60static int opteq(const char *, const char *);
61static void read_files(void);
62
63static void errout(const char *fmt, ...)
64{
65	va_list ap;
66
67	va_start(ap, fmt);
68	vfprintf(stderr, fmt, ap);
69	va_end(ap);
70	exit(1);
71}
72
73/*
74 * Lookup a file, by name.
75 */
76static struct file_list *
77fl_lookup(char *file)
78{
79	struct file_list *fp;
80
81	STAILQ_FOREACH(fp, &ftab, f_next) {
82		if (eq(fp->f_fn, file))
83			return (fp);
84	}
85	return (0);
86}
87
88/*
89 * Make a new file list entry
90 */
91static struct file_list *
92new_fent(void)
93{
94	struct file_list *fp;
95
96	fp = (struct file_list *) calloc(1, sizeof *fp);
97	if (fp == NULL)
98		err(EXIT_FAILURE, "calloc");
99	STAILQ_INSERT_TAIL(&ftab, fp, f_next);
100	return (fp);
101}
102
103/*
104 * Open the correct Makefile and return it, or error out.
105 */
106FILE *
107open_makefile_template(void)
108{
109	FILE *ifp;
110	char line[BUFSIZ];
111
112	snprintf(line, sizeof(line), "../../conf/Makefile.%s", machinename);
113	ifp = fopen(line, "r");
114	if (ifp == 0) {
115		snprintf(line, sizeof(line), "Makefile.%s", machinename);
116		ifp = fopen(line, "r");
117	}
118	if (ifp == 0)
119		err(1, "%s", line);
120	return (ifp);
121}
122
123/*
124 * Build the makefile from the skeleton
125 */
126void
127makefile(void)
128{
129	FILE *ifp, *ofp;
130	char line[BUFSIZ];
131	struct opt *op, *t;
132
133	read_files();
134	ifp = open_makefile_template();
135	ofp = fopen(path("Makefile.new"), "w");
136	if (ofp == 0)
137		err(1, "%s", path("Makefile.new"));
138	fprintf(ofp, "KERN_IDENT=%s\n", ident);
139	fprintf(ofp, "MACHINE=%s\n", machinename);
140	fprintf(ofp, "MACHINE_ARCH=%s\n", machinearch);
141	SLIST_FOREACH_SAFE(op, &mkopt, op_next, t) {
142		fprintf(ofp, "%s=%s", op->op_name, op->op_value);
143		while ((op = SLIST_NEXT(op, op_append)) != NULL)
144			fprintf(ofp, " %s", op->op_value);
145		fprintf(ofp, "\n");
146	}
147	if (debugging)
148		fprintf(ofp, "DEBUG=-g\n");
149	if (profiling)
150		fprintf(ofp, "PROFLEVEL=%d\n", profiling);
151	if (*srcdir != '\0')
152		fprintf(ofp,"S=%s\n", srcdir);
153	while (fgets(line, BUFSIZ, ifp) != NULL) {
154		if (*line != '%') {
155			fprintf(ofp, "%s", line);
156			continue;
157		}
158		if (eq(line, "%BEFORE_DEPEND\n"))
159			do_before_depend(ofp);
160		else if (eq(line, "%OBJS\n"))
161			do_objs(ofp);
162		else if (strncmp(line, "%FILES.", 7) == 0)
163			do_xxfiles(line, ofp);
164		else if (eq(line, "%RULES\n"))
165			do_rules(ofp);
166		else if (eq(line, "%CLEAN\n"))
167			do_clean(ofp);
168		else if (strncmp(line, "%VERSREQ=", 9) == 0)
169			line[0] = '\0'; /* handled elsewhere */
170		else
171			fprintf(stderr,
172			    "Unknown %% construct in generic makefile: %s",
173			    line);
174	}
175	(void) fclose(ifp);
176	(void) fclose(ofp);
177	moveifchanged(path("Makefile.new"), path("Makefile"));
178}
179
180/*
181 * Build hints.c from the skeleton
182 */
183void
184makehints(void)
185{
186	FILE *ifp, *ofp;
187	char line[BUFSIZ];
188	char *s;
189	struct hint *hint;
190
191	ofp = fopen(path("hints.c.new"), "w");
192	if (ofp == NULL)
193		err(1, "%s", path("hints.c.new"));
194	fprintf(ofp, "#include <sys/types.h>\n");
195	fprintf(ofp, "#include <sys/systm.h>\n");
196	fprintf(ofp, "\n");
197	fprintf(ofp, "int hintmode = %d;\n", hintmode);
198	fprintf(ofp, "char static_hints[] = {\n");
199	STAILQ_FOREACH(hint, &hints, hint_next) {
200		ifp = fopen(hint->hint_name, "r");
201		if (ifp == NULL)
202			err(1, "%s", hint->hint_name);
203		while (fgets(line, BUFSIZ, ifp) != NULL) {
204			/* zap trailing CR and/or LF */
205			while ((s = strrchr(line, '\n')) != NULL)
206				*s = '\0';
207			while ((s = strrchr(line, '\r')) != NULL)
208				*s = '\0';
209			/* remove # comments */
210			s = strchr(line, '#');
211			if (s)
212				*s = '\0';
213			/* remove any whitespace and " characters */
214			s = line;
215			while (*s) {
216				if (*s == ' ' || *s == '\t' || *s == '"') {
217					while (*s) {
218						s[0] = s[1];
219						s++;
220					}
221					/* start over */
222					s = line;
223					continue;
224				}
225				s++;
226			}
227			/* anything left? */
228			if (*line == '\0')
229				continue;
230			fprintf(ofp, "\"%s\\0\"\n", line);
231		}
232		fclose(ifp);
233	}
234	fprintf(ofp, "\"\\0\"\n};\n");
235	fclose(ofp);
236	moveifchanged(path("hints.c.new"), path("hints.c"));
237}
238
239/*
240 * Build env.c from the skeleton
241 */
242void
243makeenv(void)
244{
245	FILE *ifp, *ofp;
246	char line[BUFSIZ];
247	char *s;
248
249	if (env) {
250		ifp = fopen(env, "r");
251		if (ifp == NULL)
252			err(1, "%s", env);
253	} else {
254		ifp = NULL;
255	}
256	ofp = fopen(path("env.c.new"), "w");
257	if (ofp == NULL)
258		err(1, "%s", path("env.c.new"));
259	fprintf(ofp, "#include <sys/types.h>\n");
260	fprintf(ofp, "#include <sys/systm.h>\n");
261	fprintf(ofp, "\n");
262	fprintf(ofp, "int envmode = %d;\n", envmode);
263	fprintf(ofp, "char static_env[] = {\n");
264	if (ifp) {
265		while (fgets(line, BUFSIZ, ifp) != NULL) {
266			/* zap trailing CR and/or LF */
267			while ((s = strrchr(line, '\n')) != NULL)
268				*s = '\0';
269			while ((s = strrchr(line, '\r')) != NULL)
270				*s = '\0';
271			/* remove # comments */
272			s = strchr(line, '#');
273			if (s)
274				*s = '\0';
275			/* remove any whitespace and " characters */
276			s = line;
277			while (*s) {
278				if (*s == ' ' || *s == '\t' || *s == '"') {
279					while (*s) {
280						s[0] = s[1];
281						s++;
282					}
283					/* start over */
284					s = line;
285					continue;
286				}
287				s++;
288			}
289			/* anything left? */
290			if (*line == '\0')
291				continue;
292			fprintf(ofp, "\"%s\\0\"\n", line);
293		}
294	}
295	fprintf(ofp, "\"\\0\"\n};\n");
296	if (ifp)
297		fclose(ifp);
298	fclose(ofp);
299	moveifchanged(path("env.c.new"), path("env.c"));
300}
301
302static void
303read_file(char *fname)
304{
305	char ifname[MAXPATHLEN];
306	FILE *fp;
307	struct file_list *tp;
308	struct device *dp;
309	struct opt *op;
310	char *wd, *this, *compilewith, *depends, *clean, *warning;
311	const char *objprefix;
312	int compile, match, nreqs, std, filetype, not,
313	    imp_rule, no_obj, before_depend, nowerror;
314
315	fp = fopen(fname, "r");
316	if (fp == 0)
317		err(1, "%s", fname);
318next:
319	/*
320	 * include "filename"
321	 * filename    [ standard | optional ]
322	 *	[ dev* [ | dev* ... ] | profiling-routine ] [ no-obj ]
323	 *	[ compile-with "compile rule" [no-implicit-rule] ]
324	 *      [ dependency "dependency-list"] [ before-depend ]
325	 *	[ clean "file-list"] [ warning "text warning" ]
326	 *	[ obj-prefix "file prefix"]
327	 */
328	wd = get_word(fp);
329	if (wd == (char *)EOF) {
330		(void) fclose(fp);
331		return;
332	}
333	if (wd == 0)
334		goto next;
335	if (wd[0] == '#')
336	{
337		while (((wd = get_word(fp)) != (char *)EOF) && wd)
338			;
339		goto next;
340	}
341	if (eq(wd, "include")) {
342		wd = get_quoted_word(fp);
343		if (wd == (char *)EOF || wd == 0)
344			errout("%s: missing include filename.\n", fname);
345		(void) snprintf(ifname, sizeof(ifname), "../../%s", wd);
346		read_file(ifname);
347		while (((wd = get_word(fp)) != (char *)EOF) && wd)
348			;
349		goto next;
350	}
351	this = ns(wd);
352	wd = get_word(fp);
353	if (wd == (char *)EOF)
354		return;
355	if (wd == 0)
356		errout("%s: No type for %s.\n", fname, this);
357	tp = fl_lookup(this);
358	compile = 0;
359	match = 1;
360	nreqs = 0;
361	compilewith = 0;
362	depends = 0;
363	clean = 0;
364	warning = 0;
365	std = 0;
366	imp_rule = 0;
367	no_obj = 0;
368	before_depend = 0;
369	nowerror = 0;
370	not = 0;
371	filetype = NORMAL;
372	objprefix = "";
373	if (eq(wd, "standard"))
374		std = 1;
375	else if (!eq(wd, "optional"))
376		errout("%s: \"%s\" %s must be optional or standard\n",
377		    fname, wd, this);
378	for (wd = get_word(fp); wd; wd = get_word(fp)) {
379		if (wd == (char *)EOF)
380			return;
381		if (eq(wd, "!")) {
382			not = 1;
383			continue;
384		}
385		if (eq(wd, "|")) {
386			if (nreqs == 0)
387				errout("%s: syntax error describing %s\n",
388				       fname, this);
389			if (not)
390				compile += !match;
391			else
392				compile += match;
393			match = 1;
394			nreqs = 0;
395			not = 0;
396			continue;
397		}
398		if (eq(wd, "no-obj")) {
399			no_obj++;
400			continue;
401		}
402		if (eq(wd, "no-implicit-rule")) {
403			if (compilewith == 0)
404				errout("%s: alternate rule required when "
405				       "\"no-implicit-rule\" is specified for"
406				       " %s.\n",
407				       fname, this);
408			imp_rule++;
409			continue;
410		}
411		if (eq(wd, "before-depend")) {
412			before_depend++;
413			continue;
414		}
415		if (eq(wd, "dependency")) {
416			wd = get_quoted_word(fp);
417			if (wd == (char *)EOF || wd == 0)
418				errout("%s: %s missing dependency string.\n",
419				       fname, this);
420			depends = ns(wd);
421			continue;
422		}
423		if (eq(wd, "clean")) {
424			wd = get_quoted_word(fp);
425			if (wd == (char *)EOF || wd == 0)
426				errout("%s: %s missing clean file list.\n",
427				       fname, this);
428			clean = ns(wd);
429			continue;
430		}
431		if (eq(wd, "compile-with")) {
432			wd = get_quoted_word(fp);
433			if (wd == (char *)EOF || wd == 0)
434				errout("%s: %s missing compile command string.\n",
435				       fname, this);
436			compilewith = ns(wd);
437			continue;
438		}
439		if (eq(wd, "warning")) {
440			wd = get_quoted_word(fp);
441			if (wd == (char *)EOF || wd == 0)
442				errout("%s: %s missing warning text string.\n",
443				       fname, this);
444			warning = ns(wd);
445			continue;
446		}
447		if (eq(wd, "obj-prefix")) {
448			wd = get_quoted_word(fp);
449			if (wd == (char *)EOF || wd == 0)
450				errout("%s: %s missing object prefix string.\n",
451				       fname, this);
452			objprefix = ns(wd);
453			continue;
454		}
455		if (eq(wd, "nowerror")) {
456			nowerror = 1;
457			continue;
458		}
459		if (eq(wd, "local")) {
460			filetype = LOCAL;
461			continue;
462		}
463		if (eq(wd, "no-depend")) {
464			filetype = NODEPEND;
465			continue;
466		}
467		nreqs++;
468		if (eq(wd, "profiling-routine")) {
469			filetype = PROFILING;
470			continue;
471		}
472		if (std)
473			errout("standard entry %s has optional inclusion specifier %s!\n",
474			       this, wd);
475		STAILQ_FOREACH(dp, &dtab, d_next)
476			if (eq(dp->d_name, wd)) {
477				dp->d_done |= DEVDONE;
478				goto nextparam;
479			}
480		SLIST_FOREACH(op, &opt, op_next)
481			if (op->op_value == 0 && opteq(op->op_name, wd))
482				goto nextparam;
483		match = 0;
484nextparam:;
485	}
486	if (not)
487		compile += !match;
488	else
489		compile += match;
490	if (compile && tp == NULL) {
491		if (std == 0 && nreqs == 0)
492			errout("%s: what is %s optional on?\n",
493			       fname, this);
494		if (filetype == PROFILING && profiling == 0)
495			goto next;
496		tp = new_fent();
497		tp->f_fn = this;
498		tp->f_type = filetype;
499		if (imp_rule)
500			tp->f_flags |= NO_IMPLCT_RULE;
501		if (no_obj)
502			tp->f_flags |= NO_OBJ;
503		if (before_depend)
504			tp->f_flags |= BEFORE_DEPEND;
505		if (nowerror)
506			tp->f_flags |= NOWERROR;
507		tp->f_compilewith = compilewith;
508		tp->f_depends = depends;
509		tp->f_clean = clean;
510		tp->f_warn = warning;
511		tp->f_objprefix = objprefix;
512	}
513	goto next;
514}
515
516/*
517 * Read in the information about files used in making the system.
518 * Store it in the ftab linked list.
519 */
520static void
521read_files(void)
522{
523	char fname[MAXPATHLEN];
524	struct files_name *nl, *tnl;
525
526	(void) snprintf(fname, sizeof(fname), "../../conf/files");
527	read_file(fname);
528	(void) snprintf(fname, sizeof(fname),
529		       	"../../conf/files.%s", machinename);
530	read_file(fname);
531	for (nl = STAILQ_FIRST(&fntab); nl != NULL; nl = tnl) {
532		read_file(nl->f_name);
533		tnl = STAILQ_NEXT(nl, f_next);
534		free(nl->f_name);
535		free(nl);
536	}
537}
538
539static int
540opteq(const char *cp, const char *dp)
541{
542	char c, d;
543
544	for (; ; cp++, dp++) {
545		if (*cp != *dp) {
546			c = isupper(*cp) ? tolower(*cp) : *cp;
547			d = isupper(*dp) ? tolower(*dp) : *dp;
548			if (c != d)
549				return (0);
550		}
551		if (*cp == 0)
552			return (1);
553	}
554}
555
556static void
557do_before_depend(FILE *fp)
558{
559	struct file_list *tp;
560	int lpos, len;
561
562	fputs("BEFORE_DEPEND=", fp);
563	lpos = 15;
564	STAILQ_FOREACH(tp, &ftab, f_next)
565		if (tp->f_flags & BEFORE_DEPEND) {
566			len = strlen(tp->f_fn);
567			if ((len = 3 + len) + lpos > 72) {
568				lpos = 8;
569				fputs("\\\n\t", fp);
570			}
571			if (tp->f_flags & NO_IMPLCT_RULE)
572				fprintf(fp, "%s ", tp->f_fn);
573			else
574				fprintf(fp, "$S/%s ", tp->f_fn);
575			lpos += len + 1;
576		}
577	if (lpos != 8)
578		putc('\n', fp);
579}
580
581static void
582do_objs(FILE *fp)
583{
584	struct file_list *tp;
585	int lpos, len;
586	char *cp, och, *sp;
587
588	fprintf(fp, "OBJS=");
589	lpos = 6;
590	STAILQ_FOREACH(tp, &ftab, f_next) {
591		if (tp->f_flags & NO_OBJ)
592			continue;
593		sp = tail(tp->f_fn);
594		cp = sp + (len = strlen(sp)) - 1;
595		och = *cp;
596		*cp = 'o';
597		len += strlen(tp->f_objprefix);
598		if (len + lpos > 72) {
599			lpos = 8;
600			fprintf(fp, "\\\n\t");
601		}
602		fprintf(fp, "%s%s ", tp->f_objprefix, sp);
603		lpos += len + 1;
604		*cp = och;
605	}
606	if (lpos != 8)
607		putc('\n', fp);
608}
609
610static void
611do_xxfiles(char *tag, FILE *fp)
612{
613	struct file_list *tp;
614	int lpos, len, slen;
615	char *suff, *SUFF;
616
617	if (tag[strlen(tag) - 1] == '\n')
618		tag[strlen(tag) - 1] = '\0';
619
620	suff = ns(tag + 7);
621	SUFF = ns(suff);
622	raisestr(SUFF);
623	slen = strlen(suff);
624
625	fprintf(fp, "%sFILES=", SUFF);
626	lpos = 8;
627	STAILQ_FOREACH(tp, &ftab, f_next)
628		if (tp->f_type != NODEPEND) {
629			len = strlen(tp->f_fn);
630			if (tp->f_fn[len - slen - 1] != '.')
631				continue;
632			if (strcasecmp(&tp->f_fn[len - slen], suff) != 0)
633				continue;
634			if ((len = 3 + len) + lpos > 72) {
635				lpos = 8;
636				fputs("\\\n\t", fp);
637			}
638			if (tp->f_type != LOCAL)
639				fprintf(fp, "$S/%s ", tp->f_fn);
640			else
641				fprintf(fp, "%s ", tp->f_fn);
642			lpos += len + 1;
643		}
644	if (lpos != 8)
645		putc('\n', fp);
646}
647
648static char *
649tail(char *fn)
650{
651	char *cp;
652
653	cp = strrchr(fn, '/');
654	if (cp == 0)
655		return (fn);
656	return (cp+1);
657}
658
659/*
660 * Create the makerules for each file
661 * which is part of the system.
662 */
663static void
664do_rules(FILE *f)
665{
666	char *cp, *np, och;
667	struct file_list *ftp;
668	char *compilewith;
669	char cmd[128];
670
671	STAILQ_FOREACH(ftp, &ftab, f_next) {
672		if (ftp->f_warn)
673			fprintf(stderr, "WARNING: %s\n", ftp->f_warn);
674		cp = (np = ftp->f_fn) + strlen(ftp->f_fn) - 1;
675		och = *cp;
676		if (ftp->f_flags & NO_IMPLCT_RULE) {
677			if (ftp->f_depends)
678				fprintf(f, "%s%s: %s\n",
679					ftp->f_objprefix, np, ftp->f_depends);
680			else
681				fprintf(f, "%s%s: \n", ftp->f_objprefix, np);
682		}
683		else {
684			*cp = '\0';
685			if (och == 'o') {
686				fprintf(f, "%s%so:\n\t-cp $S/%so .\n\n",
687					ftp->f_objprefix, tail(np), np);
688				continue;
689			}
690			if (ftp->f_depends) {
691				fprintf(f, "%s%sln: $S/%s%c %s\n",
692					ftp->f_objprefix, tail(np), np, och,
693					ftp->f_depends);
694				fprintf(f, "\t${NORMAL_LINT}\n\n");
695				fprintf(f, "%s%so: $S/%s%c %s\n",
696					ftp->f_objprefix, tail(np), np, och,
697					ftp->f_depends);
698			}
699			else {
700				fprintf(f, "%s%sln: $S/%s%c\n",
701					ftp->f_objprefix, tail(np), np, och);
702				fprintf(f, "\t${NORMAL_LINT}\n\n");
703				fprintf(f, "%s%so: $S/%s%c\n",
704					ftp->f_objprefix, tail(np), np, och);
705			}
706		}
707		compilewith = ftp->f_compilewith;
708		if (compilewith == 0) {
709			const char *ftype = NULL;
710
711			switch (ftp->f_type) {
712			case NORMAL:
713				ftype = "NORMAL";
714				break;
715			case PROFILING:
716				if (!profiling)
717					continue;
718				ftype = "PROFILE";
719				break;
720			default:
721				fprintf(stderr,
722				    "config: don't know rules for %s\n", np);
723				break;
724			}
725			snprintf(cmd, sizeof(cmd),
726			    "${%s_%c%s}", ftype,
727			    toupper(och),
728			    ftp->f_flags & NOWERROR ? "_NOWERROR" : "");
729			compilewith = cmd;
730		}
731		*cp = och;
732		if (strlen(ftp->f_objprefix))
733			fprintf(f, "\t%s $S/%s\n", compilewith, np);
734		else
735			fprintf(f, "\t%s\n", compilewith);
736
737		if (!(ftp->f_flags & NO_OBJ))
738			fprintf(f, "\t${NORMAL_CTFCONVERT}\n\n");
739		else
740			fprintf(f, "\n");
741	}
742}
743
744static void
745do_clean(FILE *fp)
746{
747	struct file_list *tp;
748	int lpos, len;
749
750	fputs("CLEAN=", fp);
751	lpos = 7;
752	STAILQ_FOREACH(tp, &ftab, f_next)
753		if (tp->f_clean) {
754			len = strlen(tp->f_clean);
755			if (len + lpos > 72) {
756				lpos = 8;
757				fputs("\\\n\t", fp);
758			}
759			fprintf(fp, "%s ", tp->f_clean);
760			lpos += len + 1;
761		}
762	if (lpos != 8)
763		putc('\n', fp);
764}
765
766char *
767raisestr(char *str)
768{
769	char *cp = str;
770
771	while (*str) {
772		if (islower(*str))
773			*str = toupper(*str);
774		str++;
775	}
776	return (cp);
777}
778