home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / compsrcs / unix / volume13 / mcc < prev    next >
Encoding:
Text File  |  1988-09-11  |  12.8 KB  |  512 lines

  1. Subject:  v13i062:  Merge C code with compiler error messages
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: Brent Callaghan <brent%sparky@SUN.COM>
  7. Posting-number: Volume 13, Issue 62
  8. Archive-name: mcc
  9.  
  10. Mcc (Merge C Compiler) behaves just like a C for an error free
  11. compile.  However, if the compiler finds syntax errors, it merges the
  12. error messages with the source and invokes your editor (default is vi)
  13. on the merged file.  On exiting the editor it strips the merged
  14. messages and if the source file was modified it re-runs the compiler
  15. for another try.
  16.  
  17. This posting contains a shell script and a C program with identical
  18. functionality.  The C program is noticeably faster.
  19.  
  20. Made in New Zealand -->  Brent Callaghan  @ Sun Microsystems
  21.              uucp: sun!bcallaghan
  22.              phone: (415) 691 6188
  23.  
  24. [  This is kind of like the BSD error function, but not quite.  It's useful
  25.    in its own right.  I wrote the Makefile.  The script isn't plain old-style
  26.    V7 etc /bin/sh -- it uses a shell function -- so beware.  --r$  ]
  27.  
  28. ----------------------------------------
  29. #/bin/sh
  30. #This is a shar file.  To use:
  31. #  1. Remove everything before the /bin/sh line
  32. #  2. Execute with /bin/sh (not csh) to extract the files:
  33. #         Makefile
  34. #         mcc.1
  35. #         mcc.sh
  36. #         mcc.c
  37. file="${0}"
  38. echo extracting Makefile 1>&2
  39. cat >Makefile << 'EnD of Makefile'
  40. #  If you're on 4.2 or earlier, use the first line; else the second.
  41. CFLAGS=-O -DBSD
  42. #CFLAGS=-O
  43.  
  44. mcc:    mcc.c
  45.     $(CC) $(CFLAGS) -o mcc mcc.c
  46. all:    mcc mcc.1
  47. install:    all
  48.     @echo copy mcc and mcc.1 to the appropriate directories.
  49. EnD of Makefile
  50.  
  51. echo extracting mcc.1 1>&2
  52. cat >mcc.1 << 'EnD of mcc.1'
  53. .TH MCC 1 "26 December 1986"
  54. .SH NAME
  55. mcc \- merge \fBC\fP compiler
  56. .SH SYNOPSIS
  57. .B mcc 
  58. \ \ 
  59. .I <cc command line args>
  60.  
  61. .SH INTRODUCTION
  62. Ridding a program of syntax errors involves a cycle of compiling and
  63. editing.
  64. For a large program, this process can extend over many cycles.
  65. Most \fBC\fP compilers present syntax errors as a list of source line
  66. numbers and messages.
  67. This information must be remembered or copied elsewhere before the
  68. editor is invoked and erases the screen.
  69. This compile/edit cycle can be speeded considerably by eliminating the
  70. error message copying step.
  71.  
  72. .SH DESCRIPTION
  73. .B Mcc
  74. brings together the \fBC\fP compiler \fBcc\fP(1), and the screen
  75. editor \fBvi\fP(1).
  76. It runs the \fBC\fP compiler, passing all command line arguments.
  77. It's behavior is identical to the \fBC\fP compiler, unless the
  78. compiler detects syntax errors in the source.
  79. For example:  Use
  80. .sp 0.5
  81.     \fLmcc prog.c -o prog\fP
  82. .sp 0.3
  83. instead of
  84. .sp 0.3
  85.     \fLcc prog.c -o prog\fP
  86. .sp 0.5
  87. .B Mcc
  88. merges the source with the syntax error messages and invokes
  89. \fBvi\fP on the merged file.
  90. The cursor is positioned on the first line in error.
  91. Every error message follows the line it refers to.
  92. Appended to the error line is a reference to the next error line
  93. or the string "\fL(last error)\fP".
  94. Once the corrections have been made the editor should 
  95. be exited normally.
  96. .B Mcc
  97. strips the error messages from the source and re-invokes the compiler
  98. for another try.
  99. .PP
  100. This process continues until the program is free of syntax
  101. errors.
  102. The edit/compile cycle can be broken
  103. by leaving the editor without making changes or 
  104. by terminating \fBmcc\fP with a keyboard interrupt
  105. having left the editor.
  106. .PP
  107. Since errors from the linker \fBld\fP(1) do not contain line numbers
  108. \fBmcc\fP lists them and exits.
  109. .PP
  110. Since \fBmcc\fP returns the same exit value as \fBcc\fP,
  111. it can be utilized by \fBmake\fP(1).
  112. By setting \fLCC=mcc\fP either as an exported environment variable
  113. or within a \fImakefile\fP, \fBmake\fP will invoke 
  114. \fBcc\fP via \fBmcc\fP.
  115. This feature allows simple syntax errors to be repaired without
  116. having to re-run the entire \fBmake\fP.
  117. .PP
  118. An alternative editor to \fBvi\fP may be specified by assigning
  119. its name to the environment variable EDITOR.
  120. Similarly, an alternative compiler name can be assigned to COMPILER.
  121. The compiler syntax error messages must match the format of
  122. the \fBC\fP preprocessor or \fBC\fP compiler.
  123.  
  124. .SH FILES
  125. /tmp/err*    Syntax errors from \fBcc\fP
  126. .br
  127. /tmp/pid.source    Merged source and errors
  128.  
  129. .SH "SEE ALSO"
  130. cc(1), vi(1), make(1)
  131. EnD of mcc.1
  132. echo extracting mcc.sh 1>&2
  133. cat >mcc.sh << 'EnD of mcc.sh'
  134. #!/bin/sh
  135. #
  136. #  @(#) mcc -  Merges syntax error messages from C compiler into
  137. #     source and brings up vi with cursor at first error.
  138. #     Re-compiles automatically when editor exits.
  139. #  Author: Brent Callaghan
  140.  
  141. trap "rm -f /tmp/$$.* ; exit \$RSLT" 0 1 2 3 15    # clean up
  142. quit() { cat /tmp/$$.err1 ; exit ; }        # give up gracefully
  143.  
  144. until ${COMPILER:=cc} "$@" >/tmp/$$.err1 2>&1 
  145. do
  146.     RSLT=$?
  147.     sed -e 's/^"\(.*\)", line \([0-9][0-9]*\): /\1    \2    /' \
  148.         -e 's/^\(.*\): \([0-9][0-9]*\): /\1    \2    /' \
  149.         < /tmp/$$.err1 > /tmp/$$.err2
  150.     read SRC LINE null < /tmp/$$.err2
  151.     case "$LINE" in [0-9]*) ;; *) quit ;; esac    # valid line # ?
  152.     if [ ! -w "$SRC" ] ; then quit ; fi        # source writeable ?
  153.     awk -F"    " '/^'$SRC'/{printf "%s5\t>>>>  %s  <<<<\n", $2, $3}' \
  154.         < /tmp/$$.err2 > /tmp/$$.mrg1
  155.     awk '{printf "%d0\t%s\n", NR, $0}' < $SRC |
  156.     sort -m -n /tmp/$$.mrg1 - |            # merge err msgs
  157.     sed -e 's/^[0-9][0-9]*    //' > /tmp/$$.$SRC
  158.     CHKSUM=`sum /tmp/$$.$SRC`
  159.     vi +$LINE /tmp/$$.$SRC                # fix errors
  160.     if [ "$CHKSUM" = "`sum /tmp/$$.$SRC`" ] ; then exit ; fi
  161.     echo "  $COMPILER $*"
  162.     grep -v "^>>>> " /tmp/$$.$SRC > /tmp/$$.mrg2    # strip err msgs
  163.     mv /tmp/$$.mrg2 $SRC
  164. done
  165. cat /tmp/$$.err1                    # list warnings
  166. RSLT=0                            # compiled OK
  167. EnD of mcc.sh
  168. echo extracting mcc.c 1>&2
  169. cat >mcc.c << 'EnD of mcc.c'
  170. /************************************************************
  171.  *
  172.  *  Program: mcc
  173.  *  By:      Brent Callaghan
  174.  *  Date:    July 1984
  175.  *
  176.  *  Function: Runs the C compiler, passing all command line 
  177.  *        arguments. If the compiler returns a non-zero
  178.  *        result, the syntax errors are merged with the
  179.  *        source and the user's editor is invoked. The
  180.  *        cursor is placed on the first line in error.
  181.  *        Exit from the editor re-invokes the C compiler.
  182.  *        This loop continues until the C compiler exits
  183.  *        to the linker, the source file is not changed,
  184.  *        or the user kills mcc with a keyboard interrupt
  185.  *        after exiting the editor.
  186.  *
  187.  *        Environment variables EDITOR and COMPILER may
  188.  *        be used to set an alternative editor or compiler.
  189.  *
  190.  *              ~~~  PUBLIC DOMAIN  ~~~
  191.  *        
  192.  *       This program may be freely used and distributed
  193.  *       but I would rather you did not sell it.
  194.  *
  195.  ************************************************************
  196.  */
  197. #include <stdio.h>
  198. #include <ctype.h>
  199. #include <signal.h>
  200.  
  201. #ifdef BSD
  202. #include <strings.h>
  203. #define strchr  index
  204. #define strrchr rindex
  205. #else
  206. #include <string.h>
  207. #endif
  208.  
  209. extern char * getenv();
  210. static char *errname  = "/tmp/errXXXXXX";
  211. static char mergename[128];
  212. static char *srcname;
  213. static char *editor, *edname, *compiler;
  214. static int pid, viedit, firsterr;
  215. static int chksum1, chksum2;
  216.  
  217. /*
  218.  * Form 16 bit checksum of source line
  219.  */
  220. int 
  221. checksum(sum, line)
  222.     register int sum;
  223.     register char *line;
  224. {
  225.     while (*line++) {
  226.     if (sum & 1)
  227.         sum = (sum >> 1) + 0x8000;
  228.     else
  229.         sum >>= 1;
  230.  
  231.     sum = (sum + *line) & 0xFFFF;
  232.     }
  233.     return sum;
  234. }
  235.  
  236. int 
  237. runc(argv, errname)
  238.     char **argv;
  239.     char *errname;
  240. {
  241.     int status;
  242.  
  243.     switch (pid = fork()) {
  244.     case 0:            /* child */
  245.     (void) freopen(errname, "w", stderr);
  246.     execvp(compiler, argv);
  247.     perror("Couldn't exec compiler");
  248.     exit (1);
  249.  
  250.     case -1:            /* Error */
  251.     perror("Couldn't fork compiler");
  252.     exit (1);
  253.  
  254.     default:            /* Parent */
  255.     while (wait(&status) != pid);    /* wait for compile to finish */
  256.     break;
  257.     }
  258.     return ((status >> 8) & 0xFF);
  259. }
  260.  
  261. void 
  262. listerrs(errname)
  263.     char *errname;
  264. {
  265.     FILE *errfile;
  266.     char errline[BUFSIZ + 1];
  267.  
  268.     if ((errfile = fopen(errname, "r")) == NULL)
  269.     return;
  270.     while (fgets(errline, BUFSIZ, errfile) != NULL)
  271.     (void) fputs(errline, stderr);
  272.     (void) fclose(errfile);
  273.     (void) unlink(errname);
  274. }
  275.  
  276. void 
  277. edit(mergename)
  278.     char *mergename;
  279. {
  280.     int status;
  281.     char sfirsterr[6];
  282.  
  283.     switch (pid = fork()) {
  284.     case 0:            /* Child */
  285.     if (viedit) {
  286.         (void) sprintf(sfirsterr, "+%d", firsterr);
  287.         (void) printf(" vi %s %s\n", sfirsterr, mergename);
  288.         execlp(editor, "vi", sfirsterr, mergename, NULL);
  289.     } else {
  290.         (void) printf(" %s %s\n", edname, mergename);
  291.         execlp(editor, edname, mergename, NULL);
  292.     }
  293.     perror("Couldn't exec editor");
  294.     listerrs(errname);
  295.     exit (1);
  296.  
  297.     case -1:            /* Error */
  298.     perror("Couldn't fork editor");
  299.     listerrs(errname);
  300.     exit (1);
  301.  
  302.     default:            /* Parent */
  303.     while (wait(&status) != pid);    /* wait for editor to finish */
  304.     break;
  305.     }
  306. }
  307.  
  308. int 
  309. errinfo(errfile, srcname, errmsg)
  310.     FILE *errfile;
  311.     char *srcname, *errmsg;
  312. {
  313.     static char errline[BUFSIZ + 1];
  314.     char slineno[8];
  315.     char *p1, *p2;
  316.  
  317.     if (fgets(errline, BUFSIZ, errfile) == NULL)
  318.     return 0;
  319.  
  320.     errline[strlen(errline) - 1] = '\0';    /* trim newline */
  321.     p1 = errline;
  322.  
  323.     /* Get source file id */
  324.  
  325.     if (*p1 == '"')        /* cc  msg */
  326.     p2 = strchr(++p1, '"');
  327.     else            /* cpp msg */
  328.     p2 = strchr(p1, ':');
  329.     if (p2 == NULL || p1 == p2)
  330.     return 0;
  331.     *p2 = '\0';
  332.     (void) strcpy(srcname, p1);
  333.  
  334.     /* Get source line number */
  335.  
  336.     for (p1 = p2 + 1 ; *p1 ; p1++)
  337.     if (isdigit(*p1)) break;
  338.     if (*p1 == '\0')
  339.     return 0;
  340.     p2 = strchr(p1, ':');
  341.     if (p2 == NULL)
  342.     return 0;
  343.     *p2 = '\0';
  344.     (void) strcpy(slineno, p1);
  345.  
  346.     /* The rest is the error message */
  347.  
  348.     (void) strcpy(errmsg, p2 + 1);
  349.  
  350.     return atoi(slineno);
  351. }
  352.  
  353. char *
  354. merge(errname, mergename)
  355.     char *errname, *mergename;
  356. {
  357.     FILE *errfile, *srcfile, *mergefile;
  358.     int eof = 0, slineno, elineno, elines;
  359.     static char firstname[128];
  360.     char srcline[BUFSIZ + 1];
  361.     char srcname[128], errmsg[80];
  362.  
  363.     if ((errfile = fopen(errname, "r")) == NULL) {
  364.     perror(errname);
  365.     exit (1);
  366.     }
  367.     if ((firsterr = errinfo(errfile, srcname, errmsg)) == 0)
  368.     return NULL;
  369.     if (access(srcname, 2) < 0)    /* writeable ? */
  370.     return NULL;
  371.     if ((srcfile = fopen(srcname, "r")) == NULL) {
  372.     perror(srcname);
  373.     exit (1);
  374.     }
  375.     if (*mergename == '\0') {
  376.     char *p = strrchr(srcname, '/');
  377.     if (p == NULL)
  378.         p = srcname;
  379.     else
  380.         P++;
  381.     (void) sprintf(mergename, "/tmp/%d.%s", getpid(), p);
  382.     }
  383.     if ((mergefile = fopen(mergename, "w")) == NULL) {
  384.     perror(mergename);
  385.     exit (1);
  386.     }
  387.     slineno = 0;
  388.     elineno = firsterr;
  389.     elines = 0;
  390.     (void) strcpy(firstname, srcname);
  391.     chksum1 = 0;
  392.  
  393.     if (!viedit) {
  394.         (void) fprintf(mergefile, ">>>><<<< (%d)\n", firsterr + 1);
  395.         elines++;
  396.     }
  397.     while (!eof) {
  398.     if (!(eof = (fgets(srcline, BUFSIZ, srcfile) == NULL))) {
  399.         chksum1 = checksum(chksum1, srcline);
  400.         (void) fputs(srcline, mergefile);
  401.     }
  402.     slineno++;
  403.     while (slineno == elineno) {
  404.         elines++;
  405.         (void) fprintf(mergefile, ">>>> %s <<<<", errmsg);
  406.         if ((elineno = errinfo(errfile, srcname, errmsg)) == 0
  407.         || strcmp(firstname, srcname) != 0)
  408.         (void) fprintf(mergefile, " (last error)\n");
  409.         else
  410.         (void) fprintf(mergefile, " (%d)\n", elineno + elines);
  411.     }
  412.     }
  413.     (void) fclose(errfile);
  414.     (void) fclose(srcfile);
  415.     (void) fclose(mergefile);
  416.     return (firstname);
  417. }
  418.  
  419. /*
  420.  * Strip out merged error messages and compute checksum
  421.  */
  422. void 
  423. unmerge(mergename, srcname)
  424.     char *mergename, *srcname;
  425. {
  426.     FILE *mergefile, *srcfile;
  427.     char *p, srcline[BUFSIZ + 1];
  428.  
  429.     if ((mergefile = fopen(mergename, "r")) == NULL) {
  430.     perror(mergename);
  431.     exit (1);
  432.     }
  433.     if ((srcfile = fopen(srcname, "w")) == NULL) {
  434.     perror(srcname);
  435.     exit (1);
  436.     }
  437.     chksum2 = 0;
  438.     while (fgets(srcline, BUFSIZ, mergefile) != NULL) {
  439.     for (p = srcline; isspace(*p); p++);
  440.     if (strncmp(p, ">>>>", 4) != 0) {
  441.         chksum2 = checksum(chksum2, srcline);
  442.         (void) fputs(srcline, srcfile);
  443.     }
  444.     }
  445.  
  446.     (void) fclose(mergefile);
  447.     (void) fclose(srcfile);
  448. }
  449.  
  450. void 
  451. quit()
  452. {
  453.     (void) kill(pid, SIGTERM);
  454.     (void) unlink(errname);
  455.     (void) unlink(mergename);
  456.     exit (1);
  457. }
  458.  
  459. main(argc, argv)
  460.     int argc;
  461.     char *argv[];
  462. {
  463.     int i, status;
  464.  
  465.     if ((editor = getenv("EDITOR")) == NULL)
  466.     editor = "vi";
  467.     edname = (edname = strrchr(editor, '/')) == NULL ? editor : edname + 1;
  468.     viedit = strcmp(edname, "vi") == 0;
  469.  
  470.     if ((compiler = getenv("COMPILER")) == NULL)
  471.     compiler = "cc";
  472.     argv[0] = compiler;
  473.  
  474.     (void) mktemp(errname);
  475.  
  476.     signal(SIGINT, quit);
  477.     signal(SIGTERM, quit);
  478.     signal(SIGHUP, quit);
  479.  
  480.     while (status = runc(argv, errname)) {
  481.     if ((srcname = merge(errname, mergename)) == NULL) {
  482.         listerrs(errname);
  483.         exit (status);    /* couldn't merge */
  484.     }
  485.     edit(mergename);
  486.     (void) unlink(errname);
  487.  
  488.     signal(SIGINT,  SIG_IGN);
  489.     signal(SIGTERM, SIG_IGN);
  490.     signal(SIGHUP,  SIG_IGN);
  491.  
  492.     unmerge(mergename, srcname);
  493.     (void) unlink(mergename);
  494.  
  495.     signal(SIGINT,  quit);
  496.     signal(SIGTERM, quit);
  497.     signal(SIGHUP,  quit);
  498.  
  499.     if (chksum1 == chksum2)    /* file unchanged ? */
  500.         break;
  501.  
  502.     putchar(' ');
  503.     for (i = 0; i < argc; i++)
  504.         (void) printf("%s ", argv[i]);
  505.     putchar('\n');
  506.     }
  507.     listerrs(errname);
  508.     (void) unlink(errname);
  509.     exit (status);
  510. }
  511. EnD of mcc.c
  512.