home *** CD-ROM | disk | FTP | other *** search
- Subject: v13i062: Merge C code with compiler error messages
- Newsgroups: comp.sources.unix
- Sender: sources
- Approved: rsalz@uunet.UU.NET
-
- Submitted-by: Brent Callaghan <brent%sparky@SUN.COM>
- Posting-number: Volume 13, Issue 62
- Archive-name: mcc
-
- Mcc (Merge C Compiler) behaves just like a C for an error free
- compile. However, if the compiler finds syntax errors, it merges the
- error messages with the source and invokes your editor (default is vi)
- on the merged file. On exiting the editor it strips the merged
- messages and if the source file was modified it re-runs the compiler
- for another try.
-
- This posting contains a shell script and a C program with identical
- functionality. The C program is noticeably faster.
-
- Made in New Zealand --> Brent Callaghan @ Sun Microsystems
- uucp: sun!bcallaghan
- phone: (415) 691 6188
-
- [ This is kind of like the BSD error function, but not quite. It's useful
- in its own right. I wrote the Makefile. The script isn't plain old-style
- V7 etc /bin/sh -- it uses a shell function -- so beware. --r$ ]
-
- ----------------------------------------
- #/bin/sh
- #This is a shar file. To use:
- # 1. Remove everything before the /bin/sh line
- # 2. Execute with /bin/sh (not csh) to extract the files:
- # Makefile
- # mcc.1
- # mcc.sh
- # mcc.c
- file="${0}"
- echo extracting Makefile 1>&2
- cat >Makefile << 'EnD of Makefile'
- # If you're on 4.2 or earlier, use the first line; else the second.
- CFLAGS=-O -DBSD
- #CFLAGS=-O
-
- mcc: mcc.c
- $(CC) $(CFLAGS) -o mcc mcc.c
- all: mcc mcc.1
- install: all
- @echo copy mcc and mcc.1 to the appropriate directories.
- EnD of Makefile
-
- echo extracting mcc.1 1>&2
- cat >mcc.1 << 'EnD of mcc.1'
- .TH MCC 1 "26 December 1986"
- .SH NAME
- mcc \- merge \fBC\fP compiler
- .SH SYNOPSIS
- .B mcc
- \ \
- .I <cc command line args>
-
- .SH INTRODUCTION
- Ridding a program of syntax errors involves a cycle of compiling and
- editing.
- For a large program, this process can extend over many cycles.
- Most \fBC\fP compilers present syntax errors as a list of source line
- numbers and messages.
- This information must be remembered or copied elsewhere before the
- editor is invoked and erases the screen.
- This compile/edit cycle can be speeded considerably by eliminating the
- error message copying step.
-
- .SH DESCRIPTION
- .B Mcc
- brings together the \fBC\fP compiler \fBcc\fP(1), and the screen
- editor \fBvi\fP(1).
- It runs the \fBC\fP compiler, passing all command line arguments.
- It's behavior is identical to the \fBC\fP compiler, unless the
- compiler detects syntax errors in the source.
- For example: Use
- .sp 0.5
- \fLmcc prog.c -o prog\fP
- .sp 0.3
- instead of
- .sp 0.3
- \fLcc prog.c -o prog\fP
- .sp 0.5
- .B Mcc
- merges the source with the syntax error messages and invokes
- \fBvi\fP on the merged file.
- The cursor is positioned on the first line in error.
- Every error message follows the line it refers to.
- Appended to the error line is a reference to the next error line
- or the string "\fL(last error)\fP".
- Once the corrections have been made the editor should
- be exited normally.
- .B Mcc
- strips the error messages from the source and re-invokes the compiler
- for another try.
- .PP
- This process continues until the program is free of syntax
- errors.
- The edit/compile cycle can be broken
- by leaving the editor without making changes or
- by terminating \fBmcc\fP with a keyboard interrupt
- having left the editor.
- .PP
- Since errors from the linker \fBld\fP(1) do not contain line numbers
- \fBmcc\fP lists them and exits.
- .PP
- Since \fBmcc\fP returns the same exit value as \fBcc\fP,
- it can be utilized by \fBmake\fP(1).
- By setting \fLCC=mcc\fP either as an exported environment variable
- or within a \fImakefile\fP, \fBmake\fP will invoke
- \fBcc\fP via \fBmcc\fP.
- This feature allows simple syntax errors to be repaired without
- having to re-run the entire \fBmake\fP.
- .PP
- An alternative editor to \fBvi\fP may be specified by assigning
- its name to the environment variable EDITOR.
- Similarly, an alternative compiler name can be assigned to COMPILER.
- The compiler syntax error messages must match the format of
- the \fBC\fP preprocessor or \fBC\fP compiler.
-
- .SH FILES
- /tmp/err* Syntax errors from \fBcc\fP
- .br
- /tmp/pid.source Merged source and errors
-
- .SH "SEE ALSO"
- cc(1), vi(1), make(1)
- EnD of mcc.1
- echo extracting mcc.sh 1>&2
- cat >mcc.sh << 'EnD of mcc.sh'
- #!/bin/sh
- #
- # @(#) mcc - Merges syntax error messages from C compiler into
- # source and brings up vi with cursor at first error.
- # Re-compiles automatically when editor exits.
- # Author: Brent Callaghan
-
- trap "rm -f /tmp/$$.* ; exit \$RSLT" 0 1 2 3 15 # clean up
- quit() { cat /tmp/$$.err1 ; exit ; } # give up gracefully
-
- until ${COMPILER:=cc} "$@" >/tmp/$$.err1 2>&1
- do
- RSLT=$?
- sed -e 's/^"\(.*\)", line \([0-9][0-9]*\): /\1 \2 /' \
- -e 's/^\(.*\): \([0-9][0-9]*\): /\1 \2 /' \
- < /tmp/$$.err1 > /tmp/$$.err2
- read SRC LINE null < /tmp/$$.err2
- case "$LINE" in [0-9]*) ;; *) quit ;; esac # valid line # ?
- if [ ! -w "$SRC" ] ; then quit ; fi # source writeable ?
- awk -F" " '/^'$SRC'/{printf "%s5\t>>>> %s <<<<\n", $2, $3}' \
- < /tmp/$$.err2 > /tmp/$$.mrg1
- awk '{printf "%d0\t%s\n", NR, $0}' < $SRC |
- sort -m -n /tmp/$$.mrg1 - | # merge err msgs
- sed -e 's/^[0-9][0-9]* //' > /tmp/$$.$SRC
- CHKSUM=`sum /tmp/$$.$SRC`
- vi +$LINE /tmp/$$.$SRC # fix errors
- if [ "$CHKSUM" = "`sum /tmp/$$.$SRC`" ] ; then exit ; fi
- echo " $COMPILER $*"
- grep -v "^>>>> " /tmp/$$.$SRC > /tmp/$$.mrg2 # strip err msgs
- mv /tmp/$$.mrg2 $SRC
- done
- cat /tmp/$$.err1 # list warnings
- RSLT=0 # compiled OK
- EnD of mcc.sh
- echo extracting mcc.c 1>&2
- cat >mcc.c << 'EnD of mcc.c'
- /************************************************************
- *
- * Program: mcc
- * By: Brent Callaghan
- * Date: July 1984
- *
- * Function: Runs the C compiler, passing all command line
- * arguments. If the compiler returns a non-zero
- * result, the syntax errors are merged with the
- * source and the user's editor is invoked. The
- * cursor is placed on the first line in error.
- * Exit from the editor re-invokes the C compiler.
- * This loop continues until the C compiler exits
- * to the linker, the source file is not changed,
- * or the user kills mcc with a keyboard interrupt
- * after exiting the editor.
- *
- * Environment variables EDITOR and COMPILER may
- * be used to set an alternative editor or compiler.
- *
- * ~~~ PUBLIC DOMAIN ~~~
- *
- * This program may be freely used and distributed
- * but I would rather you did not sell it.
- *
- ************************************************************
- */
- #include <stdio.h>
- #include <ctype.h>
- #include <signal.h>
-
- #ifdef BSD
- #include <strings.h>
- #define strchr index
- #define strrchr rindex
- #else
- #include <string.h>
- #endif
-
- extern char * getenv();
- static char *errname = "/tmp/errXXXXXX";
- static char mergename[128];
- static char *srcname;
- static char *editor, *edname, *compiler;
- static int pid, viedit, firsterr;
- static int chksum1, chksum2;
-
- /*
- * Form 16 bit checksum of source line
- */
- int
- checksum(sum, line)
- register int sum;
- register char *line;
- {
- while (*line++) {
- if (sum & 1)
- sum = (sum >> 1) + 0x8000;
- else
- sum >>= 1;
-
- sum = (sum + *line) & 0xFFFF;
- }
- return sum;
- }
-
- int
- runc(argv, errname)
- char **argv;
- char *errname;
- {
- int status;
-
- switch (pid = fork()) {
- case 0: /* child */
- (void) freopen(errname, "w", stderr);
- execvp(compiler, argv);
- perror("Couldn't exec compiler");
- exit (1);
-
- case -1: /* Error */
- perror("Couldn't fork compiler");
- exit (1);
-
- default: /* Parent */
- while (wait(&status) != pid); /* wait for compile to finish */
- break;
- }
- return ((status >> 8) & 0xFF);
- }
-
- void
- listerrs(errname)
- char *errname;
- {
- FILE *errfile;
- char errline[BUFSIZ + 1];
-
- if ((errfile = fopen(errname, "r")) == NULL)
- return;
- while (fgets(errline, BUFSIZ, errfile) != NULL)
- (void) fputs(errline, stderr);
- (void) fclose(errfile);
- (void) unlink(errname);
- }
-
- void
- edit(mergename)
- char *mergename;
- {
- int status;
- char sfirsterr[6];
-
- switch (pid = fork()) {
- case 0: /* Child */
- if (viedit) {
- (void) sprintf(sfirsterr, "+%d", firsterr);
- (void) printf(" vi %s %s\n", sfirsterr, mergename);
- execlp(editor, "vi", sfirsterr, mergename, NULL);
- } else {
- (void) printf(" %s %s\n", edname, mergename);
- execlp(editor, edname, mergename, NULL);
- }
- perror("Couldn't exec editor");
- listerrs(errname);
- exit (1);
-
- case -1: /* Error */
- perror("Couldn't fork editor");
- listerrs(errname);
- exit (1);
-
- default: /* Parent */
- while (wait(&status) != pid); /* wait for editor to finish */
- break;
- }
- }
-
- int
- errinfo(errfile, srcname, errmsg)
- FILE *errfile;
- char *srcname, *errmsg;
- {
- static char errline[BUFSIZ + 1];
- char slineno[8];
- char *p1, *p2;
-
- if (fgets(errline, BUFSIZ, errfile) == NULL)
- return 0;
-
- errline[strlen(errline) - 1] = '\0'; /* trim newline */
- p1 = errline;
-
- /* Get source file id */
-
- if (*p1 == '"') /* cc msg */
- p2 = strchr(++p1, '"');
- else /* cpp msg */
- p2 = strchr(p1, ':');
- if (p2 == NULL || p1 == p2)
- return 0;
- *p2 = '\0';
- (void) strcpy(srcname, p1);
-
- /* Get source line number */
-
- for (p1 = p2 + 1 ; *p1 ; p1++)
- if (isdigit(*p1)) break;
- if (*p1 == '\0')
- return 0;
- p2 = strchr(p1, ':');
- if (p2 == NULL)
- return 0;
- *p2 = '\0';
- (void) strcpy(slineno, p1);
-
- /* The rest is the error message */
-
- (void) strcpy(errmsg, p2 + 1);
-
- return atoi(slineno);
- }
-
- char *
- merge(errname, mergename)
- char *errname, *mergename;
- {
- FILE *errfile, *srcfile, *mergefile;
- int eof = 0, slineno, elineno, elines;
- static char firstname[128];
- char srcline[BUFSIZ + 1];
- char srcname[128], errmsg[80];
-
- if ((errfile = fopen(errname, "r")) == NULL) {
- perror(errname);
- exit (1);
- }
- if ((firsterr = errinfo(errfile, srcname, errmsg)) == 0)
- return NULL;
- if (access(srcname, 2) < 0) /* writeable ? */
- return NULL;
- if ((srcfile = fopen(srcname, "r")) == NULL) {
- perror(srcname);
- exit (1);
- }
- if (*mergename == '\0') {
- char *p = strrchr(srcname, '/');
- if (p == NULL)
- p = srcname;
- else
- P++;
- (void) sprintf(mergename, "/tmp/%d.%s", getpid(), p);
- }
- if ((mergefile = fopen(mergename, "w")) == NULL) {
- perror(mergename);
- exit (1);
- }
- slineno = 0;
- elineno = firsterr;
- elines = 0;
- (void) strcpy(firstname, srcname);
- chksum1 = 0;
-
- if (!viedit) {
- (void) fprintf(mergefile, ">>>><<<< (%d)\n", firsterr + 1);
- elines++;
- }
- while (!eof) {
- if (!(eof = (fgets(srcline, BUFSIZ, srcfile) == NULL))) {
- chksum1 = checksum(chksum1, srcline);
- (void) fputs(srcline, mergefile);
- }
- slineno++;
- while (slineno == elineno) {
- elines++;
- (void) fprintf(mergefile, ">>>> %s <<<<", errmsg);
- if ((elineno = errinfo(errfile, srcname, errmsg)) == 0
- || strcmp(firstname, srcname) != 0)
- (void) fprintf(mergefile, " (last error)\n");
- else
- (void) fprintf(mergefile, " (%d)\n", elineno + elines);
- }
- }
- (void) fclose(errfile);
- (void) fclose(srcfile);
- (void) fclose(mergefile);
- return (firstname);
- }
-
- /*
- * Strip out merged error messages and compute checksum
- */
- void
- unmerge(mergename, srcname)
- char *mergename, *srcname;
- {
- FILE *mergefile, *srcfile;
- char *p, srcline[BUFSIZ + 1];
-
- if ((mergefile = fopen(mergename, "r")) == NULL) {
- perror(mergename);
- exit (1);
- }
- if ((srcfile = fopen(srcname, "w")) == NULL) {
- perror(srcname);
- exit (1);
- }
- chksum2 = 0;
- while (fgets(srcline, BUFSIZ, mergefile) != NULL) {
- for (p = srcline; isspace(*p); p++);
- if (strncmp(p, ">>>>", 4) != 0) {
- chksum2 = checksum(chksum2, srcline);
- (void) fputs(srcline, srcfile);
- }
- }
-
- (void) fclose(mergefile);
- (void) fclose(srcfile);
- }
-
- void
- quit()
- {
- (void) kill(pid, SIGTERM);
- (void) unlink(errname);
- (void) unlink(mergename);
- exit (1);
- }
-
- main(argc, argv)
- int argc;
- char *argv[];
- {
- int i, status;
-
- if ((editor = getenv("EDITOR")) == NULL)
- editor = "vi";
- edname = (edname = strrchr(editor, '/')) == NULL ? editor : edname + 1;
- viedit = strcmp(edname, "vi") == 0;
-
- if ((compiler = getenv("COMPILER")) == NULL)
- compiler = "cc";
- argv[0] = compiler;
-
- (void) mktemp(errname);
-
- signal(SIGINT, quit);
- signal(SIGTERM, quit);
- signal(SIGHUP, quit);
-
- while (status = runc(argv, errname)) {
- if ((srcname = merge(errname, mergename)) == NULL) {
- listerrs(errname);
- exit (status); /* couldn't merge */
- }
- edit(mergename);
- (void) unlink(errname);
-
- signal(SIGINT, SIG_IGN);
- signal(SIGTERM, SIG_IGN);
- signal(SIGHUP, SIG_IGN);
-
- unmerge(mergename, srcname);
- (void) unlink(mergename);
-
- signal(SIGINT, quit);
- signal(SIGTERM, quit);
- signal(SIGHUP, quit);
-
- if (chksum1 == chksum2) /* file unchanged ? */
- break;
-
- putchar(' ');
- for (i = 0; i < argc; i++)
- (void) printf("%s ", argv[i]);
- putchar('\n');
- }
- listerrs(errname);
- (void) unlink(errname);
- exit (status);
- }
- EnD of mcc.c
-