mkinit.c revision 8855
11556Srgrimes/*-
21556Srgrimes * Copyright (c) 1991, 1993
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * This code is derived from software contributed to Berkeley by
61556Srgrimes * Kenneth Almquist.
71556Srgrimes *
81556Srgrimes * Redistribution and use in source and binary forms, with or without
91556Srgrimes * modification, are permitted provided that the following conditions
101556Srgrimes * are met:
111556Srgrimes * 1. Redistributions of source code must retain the above copyright
121556Srgrimes *    notice, this list of conditions and the following disclaimer.
131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141556Srgrimes *    notice, this list of conditions and the following disclaimer in the
151556Srgrimes *    documentation and/or other materials provided with the distribution.
161556Srgrimes * 3. All advertising materials mentioning features or use of this software
171556Srgrimes *    must display the following acknowledgement:
181556Srgrimes *	This product includes software developed by the University of
191556Srgrimes *	California, Berkeley and its contributors.
201556Srgrimes * 4. Neither the name of the University nor the names of its contributors
211556Srgrimes *    may be used to endorse or promote products derived from this software
221556Srgrimes *    without specific prior written permission.
231556Srgrimes *
241556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341556Srgrimes * SUCH DAMAGE.
353044Sdg *
368855Srgrimes *	$Id: mkinit.c,v 1.3 1994/11/06 06:27:04 pst Exp $
371556Srgrimes */
381556Srgrimes
391556Srgrimes#ifndef lint
401556Srgrimesstatic char copyright[] =
411556Srgrimes"@(#) Copyright (c) 1991, 1993\n\
421556Srgrimes	The Regents of the University of California.  All rights reserved.\n";
431556Srgrimes#endif /* not lint */
441556Srgrimes
451556Srgrimes#ifndef lint
461556Srgrimesstatic char sccsid[] = "@(#)mkinit.c	8.1 (Berkeley) 5/31/93";
471556Srgrimes#endif /* not lint */
481556Srgrimes
491556Srgrimes/*
501556Srgrimes * This program scans all the source files for code to handle various
511556Srgrimes * special events and combines this code into one file.  This (allegedly)
521556Srgrimes * improves the structure of the program since there is no need for
531556Srgrimes * anyone outside of a module to know that that module performs special
541556Srgrimes * operations on particular events.  The command is executed iff init.c
551556Srgrimes * is actually changed.
561556Srgrimes *
571556Srgrimes * Usage:  mkinit command sourcefile...
581556Srgrimes */
591556Srgrimes
601556Srgrimes
611556Srgrimes#include <sys/cdefs.h>
621556Srgrimes#include <sys/types.h>
631556Srgrimes#include <stdio.h>
641556Srgrimes#include <fcntl.h>
651556Srgrimes#include <unistd.h>
661556Srgrimes
671556Srgrimes
681556Srgrimes/*
691556Srgrimes * OUTFILE is the name of the output file.  Output is initially written
701556Srgrimes * to the file OUTTEMP, which is then moved to OUTFILE if OUTTEMP and
711556Srgrimes * OUTFILE are different.
721556Srgrimes */
731556Srgrimes
741556Srgrimes#define OUTFILE "init.c"
751556Srgrimes#define OUTTEMP "init.c.new"
761556Srgrimes#define OUTOBJ "init.o"
771556Srgrimes
781556Srgrimes
791556Srgrimes/*
801556Srgrimes * A text structure is basicly just a string that grows as more characters
811556Srgrimes * are added onto the end of it.  It is implemented as a linked list of
821556Srgrimes * blocks of characters.  The routines addstr and addchar append a string
831556Srgrimes * or a single character, respectively, to a text structure.  Writetext
841556Srgrimes * writes the contents of a text structure to a file.
851556Srgrimes */
861556Srgrimes
871556Srgrimes#define BLOCKSIZE 512
881556Srgrimes
891556Srgrimesstruct text {
901556Srgrimes	char *nextc;
911556Srgrimes	int nleft;
921556Srgrimes	struct block *start;
931556Srgrimes	struct block *last;
948855Srgrimes};
951556Srgrimes
961556Srgrimesstruct block {
971556Srgrimes	struct block *next;
981556Srgrimes	char text[BLOCKSIZE];
991556Srgrimes};
1001556Srgrimes
1011556Srgrimes
1021556Srgrimes/*
1031556Srgrimes * There is one event structure for each event that mkinit handles.
1041556Srgrimes */
1051556Srgrimes
1061556Srgrimesstruct event {
1071556Srgrimes	char *name;		/* name of event (e.g. INIT) */
1081556Srgrimes	char *routine;		/* name of routine called on event */
1091556Srgrimes	char *comment;		/* comment describing routine */
1101556Srgrimes	struct text code;		/* code for handling event */
1111556Srgrimes};
1121556Srgrimes
1131556Srgrimes
1141556Srgrimeschar writer[] = "\
1151556Srgrimes/*\n\
1161556Srgrimes * This file was generated by the mkinit program.\n\
1171556Srgrimes */\n\
1181556Srgrimes\n";
1191556Srgrimes
1201556Srgrimeschar init[] = "\
1211556Srgrimes/*\n\
1221556Srgrimes * Initialization code.\n\
1231556Srgrimes */\n";
1241556Srgrimes
1251556Srgrimeschar reset[] = "\
1261556Srgrimes/*\n\
1271556Srgrimes * This routine is called when an error or an interrupt occurs in an\n\
1281556Srgrimes * interactive shell and control is returned to the main command loop.\n\
1291556Srgrimes */\n";
1301556Srgrimes
1311556Srgrimeschar shellproc[] = "\
1321556Srgrimes/*\n\
1331556Srgrimes * This routine is called to initialize the shell to run a shell procedure.\n\
1341556Srgrimes */\n";
1351556Srgrimes
1361556Srgrimes
1371556Srgrimesstruct event event[] = {
1381556Srgrimes	{"INIT", "init", init},
1391556Srgrimes	{"RESET", "reset", reset},
1401556Srgrimes	{"SHELLPROC", "initshellproc", shellproc},
1411556Srgrimes	{NULL, NULL}
1421556Srgrimes};
1431556Srgrimes
1441556Srgrimes
1451556Srgrimeschar *curfile;				/* current file */
1461556Srgrimesint linno;				/* current line */
1471556Srgrimeschar *header_files[200];		/* list of header files */
1481556Srgrimesstruct text defines;			/* #define statements */
1491556Srgrimesstruct text decls;			/* declarations */
1501556Srgrimesint amiddecls;				/* for formatting */
1511556Srgrimes
1521556Srgrimes
1531556Srgrimesvoid readfile(), doevent(), doinclude(), dodecl(), output();
1541556Srgrimesvoid addstr(), addchar(), writetext();
1551556Srgrimes
1561556Srgrimes#define equal(s1, s2)	(strcmp(s1, s2) == 0)
1571556Srgrimes
1581556SrgrimesFILE *ckfopen();
1591556Srgrimeschar *savestr();
1601556Srgrimesvoid *ckmalloc __P((int));
1611556Srgrimesvoid error();
1621556Srgrimes
1631556Srgrimesmain(argc, argv)
1641556Srgrimes	char **argv;
1651556Srgrimes	{
1661556Srgrimes	char **ap;
1671556Srgrimes	int fd;
1681556Srgrimes	char c;
1691556Srgrimes
1701556Srgrimes	if (argc < 2)
1711556Srgrimes		error("Usage:  mkinit command file...");
1721556Srgrimes	header_files[0] = "\"shell.h\"";
1731556Srgrimes	header_files[1] = "\"mystring.h\"";
1741556Srgrimes	for (ap = argv + 2 ; *ap ; ap++)
1751556Srgrimes		readfile(*ap);
1761556Srgrimes	output();
1771556Srgrimes	if (file_changed()) {
1781556Srgrimes		unlink(OUTFILE);
1791556Srgrimes		link(OUTTEMP, OUTFILE);
1801556Srgrimes		unlink(OUTTEMP);
1811556Srgrimes	} else {
1821556Srgrimes		unlink(OUTTEMP);
1831556Srgrimes		if (touch(OUTOBJ))
1841556Srgrimes			exit(0);		/* no compilation necessary */
1851556Srgrimes	}
1861556Srgrimes	printf("%s\n", argv[1]);
1871556Srgrimes	execl("/bin/sh", "sh", "-c", argv[1], (char *)0);
1881556Srgrimes	error("Can't exec shell");
1891556Srgrimes}
1901556Srgrimes
1911556Srgrimes
1921556Srgrimes/*
1931556Srgrimes * Parse an input file.
1941556Srgrimes */
1951556Srgrimes
1961556Srgrimesvoid
1971556Srgrimesreadfile(fname)
1981556Srgrimes	char *fname;
1991556Srgrimes	{
2001556Srgrimes	FILE *fp;
2011556Srgrimes	char line[1024];
2021556Srgrimes	struct event *ep;
2031556Srgrimes
2041556Srgrimes	fp = ckfopen(fname, "r");
2051556Srgrimes	curfile = fname;
2061556Srgrimes	linno = 0;
2071556Srgrimes	amiddecls = 0;
2081556Srgrimes	while (fgets(line, sizeof line, fp) != NULL) {
2091556Srgrimes		linno++;
2101556Srgrimes		for (ep = event ; ep->name ; ep++) {
2111556Srgrimes			if (line[0] == ep->name[0] && match(ep->name, line)) {
2121556Srgrimes				doevent(ep, fp, fname);
2131556Srgrimes				break;
2141556Srgrimes			}
2151556Srgrimes		}
2161556Srgrimes		if (line[0] == 'I' && match("INCLUDE", line))
2171556Srgrimes			doinclude(line);
2181556Srgrimes		if (line[0] == 'M' && match("MKINIT", line))
2191556Srgrimes			dodecl(line, fp);
2201556Srgrimes		if (line[0] == '#' && gooddefine(line))
2211556Srgrimes			addstr(line, &defines);
2221556Srgrimes	}
2231556Srgrimes	fclose(fp);
2241556Srgrimes}
2251556Srgrimes
2261556Srgrimes
2271556Srgrimesint
2281556Srgrimesmatch(name, line)
2291556Srgrimes	char *name;
2301556Srgrimes	char *line;
2311556Srgrimes	{
2321556Srgrimes	register char *p, *q;
2331556Srgrimes
2341556Srgrimes	p = name, q = line;
2351556Srgrimes	while (*p) {
2361556Srgrimes		if (*p++ != *q++)
2371556Srgrimes			return 0;
2381556Srgrimes	}
2391556Srgrimes	if (*q != '{' && *q != ' ' && *q != '\t' && *q != '\n')
2401556Srgrimes		return 0;
2411556Srgrimes	return 1;
2421556Srgrimes}
2431556Srgrimes
2441556Srgrimes
2451556Srgrimesint
2461556Srgrimesgooddefine(line)
2471556Srgrimes	char *line;
2481556Srgrimes	{
2491556Srgrimes	register char *p;
2501556Srgrimes
2511556Srgrimes	if (! match("#define", line))
2521556Srgrimes		return 0;			/* not a define */
2531556Srgrimes	p = line + 7;
2541556Srgrimes	while (*p == ' ' || *p == '\t')
2551556Srgrimes		p++;
2561556Srgrimes	while (*p != ' ' && *p != '\t') {
2571556Srgrimes		if (*p == '(')
2581556Srgrimes			return 0;		/* macro definition */
2591556Srgrimes		p++;
2601556Srgrimes	}
2611556Srgrimes	while (*p != '\n' && *p != '\0')
2621556Srgrimes		p++;
2631556Srgrimes	if (p[-1] == '\\')
2641556Srgrimes		return 0;			/* multi-line definition */
2651556Srgrimes	return 1;
2661556Srgrimes}
2671556Srgrimes
2681556Srgrimes
2691556Srgrimesvoid
2701556Srgrimesdoevent(ep, fp, fname)
2711556Srgrimes	register struct event *ep;
2721556Srgrimes	FILE *fp;
2731556Srgrimes	char *fname;
2741556Srgrimes	{
2751556Srgrimes	char line[1024];
2761556Srgrimes	int indent;
2771556Srgrimes	char *p;
2781556Srgrimes
2791556Srgrimes	sprintf(line, "\n      /* from %s: */\n", fname);
2801556Srgrimes	addstr(line, &ep->code);
2811556Srgrimes	addstr("      {\n", &ep->code);
2821556Srgrimes	for (;;) {
2831556Srgrimes		linno++;
2841556Srgrimes		if (fgets(line, sizeof line, fp) == NULL)
2851556Srgrimes			error("Unexpected EOF");
2861556Srgrimes		if (equal(line, "}\n"))
2871556Srgrimes			break;
2881556Srgrimes		indent = 6;
2891556Srgrimes		for (p = line ; *p == '\t' ; p++)
2901556Srgrimes			indent += 8;
2911556Srgrimes		for ( ; *p == ' ' ; p++)
2921556Srgrimes			indent++;
2931556Srgrimes		if (*p == '\n' || *p == '#')
2941556Srgrimes			indent = 0;
2951556Srgrimes		while (indent >= 8) {
2961556Srgrimes			addchar('\t', &ep->code);
2971556Srgrimes			indent -= 8;
2981556Srgrimes		}
2991556Srgrimes		while (indent > 0) {
3001556Srgrimes			addchar(' ', &ep->code);
3011556Srgrimes			indent--;
3021556Srgrimes		}
3031556Srgrimes		addstr(p, &ep->code);
3041556Srgrimes	}
3051556Srgrimes	addstr("      }\n", &ep->code);
3061556Srgrimes}
3071556Srgrimes
3081556Srgrimes
3091556Srgrimesvoid
3101556Srgrimesdoinclude(line)
3111556Srgrimes	char *line;
3121556Srgrimes	{
3131556Srgrimes	register char *p;
3141556Srgrimes	char *name;
3151556Srgrimes	register char **pp;
3161556Srgrimes
3171556Srgrimes	for (p = line ; *p != '"' && *p != '<' && *p != '\0' ; p++);
3181556Srgrimes	if (*p == '\0')
3191556Srgrimes		error("Expecting '\"' or '<'");
3201556Srgrimes	name = p;
3211556Srgrimes	while (*p != ' ' && *p != '\t' && *p != '\n')
3221556Srgrimes		p++;
3231556Srgrimes	if (p[-1] != '"' && p[-1] != '>')
3241556Srgrimes		error("Missing terminator");
3251556Srgrimes	*p = '\0';
3261556Srgrimes
3271556Srgrimes	/* name now contains the name of the include file */
3281556Srgrimes	for (pp = header_files ; *pp && ! equal(*pp, name) ; pp++);
3291556Srgrimes	if (*pp == NULL)
3301556Srgrimes		*pp = savestr(name);
3311556Srgrimes}
3321556Srgrimes
3331556Srgrimes
3341556Srgrimesvoid
3351556Srgrimesdodecl(line1, fp)
3361556Srgrimes	char *line1;
3371556Srgrimes	FILE *fp;
3381556Srgrimes	{
3391556Srgrimes	char line[1024];
3401556Srgrimes	register char *p, *q;
3411556Srgrimes
3421556Srgrimes	if (strcmp(line1, "MKINIT\n") == 0) { /* start of struct/union decl */
3431556Srgrimes		addchar('\n', &decls);
3441556Srgrimes		do {
3451556Srgrimes			linno++;
3461556Srgrimes			if (fgets(line, sizeof line, fp) == NULL)
3471556Srgrimes				error("Unterminated structure declaration");
3481556Srgrimes			addstr(line, &decls);
3491556Srgrimes		} while (line[0] != '}');
3501556Srgrimes		amiddecls = 0;
3511556Srgrimes	} else {
3521556Srgrimes		if (! amiddecls)
3531556Srgrimes			addchar('\n', &decls);
3541556Srgrimes		q = NULL;
3554204Spst		for (p = line1 + 6 ; *p != '=' && *p != '/' && *p != '\n'; p++);
3561556Srgrimes		if (*p == '=') {		/* eliminate initialization */
3571556Srgrimes			for (q = p ; *q && *q != ';' ; q++);
3581556Srgrimes			if (*q == '\0')
3591556Srgrimes				q = NULL;
3601556Srgrimes			else {
3611556Srgrimes				while (p[-1] == ' ')
3621556Srgrimes					p--;
3631556Srgrimes				*p = '\0';
3641556Srgrimes			}
3651556Srgrimes		}
3661556Srgrimes		addstr("extern", &decls);
3671556Srgrimes		addstr(line1 + 6, &decls);
3681556Srgrimes		if (q != NULL)
3691556Srgrimes			addstr(q, &decls);
3701556Srgrimes		amiddecls = 1;
3711556Srgrimes	}
3721556Srgrimes}
3731556Srgrimes
3741556Srgrimes
3751556Srgrimes
3761556Srgrimes/*
3771556Srgrimes * Write the output to the file OUTTEMP.
3781556Srgrimes */
3791556Srgrimes
3801556Srgrimesvoid
3811556Srgrimesoutput() {
3821556Srgrimes	FILE *fp;
3831556Srgrimes	char **pp;
3841556Srgrimes	struct event *ep;
3851556Srgrimes
3861556Srgrimes	fp = ckfopen(OUTTEMP, "w");
3871556Srgrimes	fputs(writer, fp);
3881556Srgrimes	for (pp = header_files ; *pp ; pp++)
3891556Srgrimes		fprintf(fp, "#include %s\n", *pp);
3901556Srgrimes	fputs("\n\n\n", fp);
3911556Srgrimes	writetext(&defines, fp);
3921556Srgrimes	fputs("\n\n", fp);
3931556Srgrimes	writetext(&decls, fp);
3941556Srgrimes	for (ep = event ; ep->name ; ep++) {
3951556Srgrimes		fputs("\n\n\n", fp);
3961556Srgrimes		fputs(ep->comment, fp);
3971556Srgrimes		fprintf(fp, "\nvoid\n%s() {\n", ep->routine);
3981556Srgrimes		writetext(&ep->code, fp);
3991556Srgrimes		fprintf(fp, "}\n");
4001556Srgrimes	}
4011556Srgrimes	fclose(fp);
4021556Srgrimes}
4031556Srgrimes
4041556Srgrimes
4051556Srgrimes/*
4061556Srgrimes * Return true if the new output file is different from the old one.
4071556Srgrimes */
4081556Srgrimes
4091556Srgrimesint
4101556Srgrimesfile_changed() {
4111556Srgrimes	register FILE *f1, *f2;
4121556Srgrimes	register int c;
4131556Srgrimes
4141556Srgrimes	if ((f1 = fopen(OUTFILE, "r")) == NULL
4151556Srgrimes	 || (f2 = fopen(OUTTEMP, "r")) == NULL)
4161556Srgrimes		return 1;
4171556Srgrimes	while ((c = getc(f1)) == getc(f2)) {
4181556Srgrimes		if (c == EOF)
4191556Srgrimes			return 0;
4201556Srgrimes	}
4211556Srgrimes	return 1;
4221556Srgrimes}
4231556Srgrimes
4241556Srgrimes
4251556Srgrimes/*
4261556Srgrimes * Touch a file.  Returns 0 on failure, 1 on success.
4271556Srgrimes */
4281556Srgrimes
4291556Srgrimesint
4301556Srgrimestouch(file)
4311556Srgrimes	char *file;
4321556Srgrimes	{
4331556Srgrimes	int fd;
4341556Srgrimes	char c;
4351556Srgrimes
4361556Srgrimes	if ((fd = open(file, O_RDWR)) < 0)
4371556Srgrimes		return 0;
4381556Srgrimes	if (read(fd, &c, 1) != 1) {
4391556Srgrimes		close(fd);
4401556Srgrimes		return 0;
4411556Srgrimes	}
4421556Srgrimes	lseek(fd, (off_t)0, 0);
4431556Srgrimes	write(fd, &c, 1);
4441556Srgrimes	close(fd);
4451556Srgrimes	return 1;
4461556Srgrimes}
4471556Srgrimes
4481556Srgrimes
4491556Srgrimes
4501556Srgrimes/*
4511556Srgrimes * A text structure is simply a block of text that is kept in memory.
4521556Srgrimes * Addstr appends a string to the text struct, and addchar appends a single
4531556Srgrimes * character.
4541556Srgrimes */
4551556Srgrimes
4561556Srgrimesvoid
4571556Srgrimesaddstr(s, text)
4581556Srgrimes	register char *s;
4591556Srgrimes	register struct text *text;
4601556Srgrimes	{
4611556Srgrimes	while (*s) {
4621556Srgrimes		if (--text->nleft < 0)
4631556Srgrimes			addchar(*s++, text);
4641556Srgrimes		else
4651556Srgrimes			*text->nextc++ = *s++;
4661556Srgrimes	}
4671556Srgrimes}
4681556Srgrimes
4691556Srgrimes
4701556Srgrimesvoid
4711556Srgrimesaddchar(c, text)
4721556Srgrimes	register struct text *text;
4731556Srgrimes	{
4741556Srgrimes	struct block *bp;
4751556Srgrimes
4761556Srgrimes	if (--text->nleft < 0) {
4771556Srgrimes		bp = ckmalloc(sizeof *bp);
4781556Srgrimes		if (text->start == NULL)
4791556Srgrimes			text->start = bp;
4801556Srgrimes		else
4811556Srgrimes			text->last->next = bp;
4821556Srgrimes		text->last = bp;
4831556Srgrimes		text->nextc = bp->text;
4841556Srgrimes		text->nleft = BLOCKSIZE - 1;
4851556Srgrimes	}
4861556Srgrimes	*text->nextc++ = c;
4871556Srgrimes}
4881556Srgrimes
4891556Srgrimes/*
4901556Srgrimes * Write the contents of a text structure to a file.
4911556Srgrimes */
4921556Srgrimesvoid
4931556Srgrimeswritetext(text, fp)
4941556Srgrimes	struct text *text;
4951556Srgrimes	FILE *fp;
4961556Srgrimes	{
4971556Srgrimes	struct block *bp;
4981556Srgrimes
4991556Srgrimes	if (text->start != NULL) {
5001556Srgrimes		for (bp = text->start ; bp != text->last ; bp = bp->next)
5011556Srgrimes			fwrite(bp->text, sizeof (char), BLOCKSIZE, fp);
5021556Srgrimes		fwrite(bp->text, sizeof (char), BLOCKSIZE - text->nleft, fp);
5031556Srgrimes	}
5041556Srgrimes}
5051556Srgrimes
5061556SrgrimesFILE *
5071556Srgrimesckfopen(file, mode)
5081556Srgrimes	char *file;
5091556Srgrimes	char *mode;
5101556Srgrimes	{
5111556Srgrimes	FILE *fp;
5121556Srgrimes
5131556Srgrimes	if ((fp = fopen(file, mode)) == NULL) {
5141556Srgrimes		fprintf(stderr, "Can't open %s\n", file);
5151556Srgrimes		exit(2);
5161556Srgrimes	}
5171556Srgrimes	return fp;
5181556Srgrimes}
5191556Srgrimes
5201556Srgrimesvoid *
5211556Srgrimesckmalloc(nbytes) {
5221556Srgrimes	register char *p;
5231556Srgrimes	char *malloc();
5241556Srgrimes
5251556Srgrimes	if ((p = malloc(nbytes)) == NULL)
5261556Srgrimes		error("Out of space");
5271556Srgrimes	return p;
5281556Srgrimes}
5291556Srgrimes
5301556Srgrimeschar *
5311556Srgrimessavestr(s)
5321556Srgrimes	char *s;
5331556Srgrimes	{
5341556Srgrimes	register char *p;
5351556Srgrimes
5361556Srgrimes	p = ckmalloc(strlen(s) + 1);
5371556Srgrimes	strcpy(p, s);
5381556Srgrimes	return p;
5391556Srgrimes}
5401556Srgrimes
5411556Srgrimesvoid
5421556Srgrimeserror(msg)
5431556Srgrimes	char *msg;
5441556Srgrimes	{
5451556Srgrimes	if (curfile != NULL)
5461556Srgrimes		fprintf(stderr, "%s:%d: ", curfile, linno);
5471556Srgrimes	fprintf(stderr, "%s\n", msg);
5481556Srgrimes	exit(2);
5491556Srgrimes}
550