/* LOGSCAN.C Scans log of a STATA ado-file run, searches for any errors that STATA found, and displays the surrounding program lines if error was encountered. To be called from FINDBUG.ADO using STATA. Timothy J. Schmidt November 7, 1994 STB-23: ip7 */ #include #define LNLENGTH 81 typedef char BUFFER[LNLENGTH] ; int main() { int flag = 0, counter = 0, strindex, cmdindex ; long marker, endofile ; char cmd[9] = "", *blankstr = "", *WHILE = "while", *FOR = "for", *IF = "if", *LOCAL = "local", *noiadv = "noi", *quiadv = "qui", *byprefix = "by" ; char ch, line[LNLENGTH], *noerror = "* qQ\r\n", *errormsg = "No error" ; BUFFER buff1, buff2, buff3, buff4, buff5, buff6, buff7, buff8, buff9, buff10, buff11 ; FILE *infile, *outfile ; infile = fopen("_FINDBUG.LOG", "rb") ; /* Read _FINDBUG.LOG in binary mode */ if (infile == NULL) { fputs("Could not open file: _FINDBUG.LOG\n", stdout) ; exit(1) ; } if (fseek(infile, 0L, SEEK_END) != 0) { /* Go to end of file */ fputs("fseek error -- could not find end of file\n", stdout) ; exit(2) ; } endofile = ftell(infile) ; /* Record byte number @ end of file */ /* Read file in reverse, character by character, starting five bytes from end of file. (Five-byte offset skips last \n in file.) */ for (marker = 5L ; marker <= endofile ; marker++) { if (fseek(infile, -marker, SEEK_END) != 0) { fputs("fseek error\n", stdout) ; exit(2) ; } if ((ch = fgetc(infile)) == EOF) { fputs("End of file has been reached\n", stdout) ; exit(3) ; } /* \n marks the end of a line. Get the prior line and compare it to FINDBUG's signal that there were no errors: * qQ. If STATA found no errors, print message and break loop to stop reading file. */ if (ch == '\n') { if (fgets(line, LNLENGTH, infile) == NULL) { fputs("Error reading string from file\n", stdout) ; exit(4) ; } if (!(strcmp(line, noerror))) { fputs("\n\n\n\nNo errors were found\n\n", stdout) ; break ; } /* Error message is last line in log file that does not begin with a "-" (signals an executed line), a "*" (part of FINDBUG's no errors signal), a ">" (signals a line continuation), or "r(" (beginning of STATA's error code). If error is found, read message and set flag. */ if (line[0] != '-' && line[0] != ' ' && line[0] != '*' && line[0] != '>' && !(line[0] == 'r' && line[1] == '(') && flag == 0) { strncpy(errormsg, line, LNLENGTH) ; flag = 1 ; } /* Put extracted log file lines into buffers, unless current line is error message. Shift buffers to keep only lines surrounding error message. */ else if (flag != 1) { strncpy(buff1, buff2, LNLENGTH) ; strncpy(buff2, buff3, LNLENGTH) ; strncpy(buff3, buff4, LNLENGTH) ; strncpy(buff4, buff5, LNLENGTH) ; strncpy(buff5, buff6, LNLENGTH) ; strncpy(buff6, buff7, LNLENGTH) ; strncpy(buff7, buff8, LNLENGTH) ; strncpy(buff8, buff9, LNLENGTH) ; strncpy(buff9, buff10, LNLENGTH) ; strncpy(buff10, buff11, LNLENGTH) ; strncpy(buff11, line, LNLENGTH) ; } /* If current line is error message, set flag = 2 so that no more error messages will be found and subsequent lines can be read into buffers. */ if (flag == 1) flag++ ; /* Once error has been found, allow eight more passes through the loop to read log file lines that precede the error into buffers. */ if (flag == 2) counter++ ; if (counter > 8) break ; } } if (flag != 0) { outfile = fopen("JUNK.DO", "w") ; /* Create STATA do-file */ /* Continue reading through log file in reverse, picking out command lines (those that start with "-"). Write code to STATA do-file which is used to identify ado-files called in user's program. */ for (; marker <= endofile ; marker++) { fseek(infile, -marker, SEEK_END) ; ch = getc(infile) ; if (ch == '\n') { fgets(line, LNLENGTH, infile) ; if (line[0] == '-' && line[2] != '}') { strindex = 2 ; cmdindex = 0 ; while (line[strindex] != ' ' && line[strindex] != '\n' && line[strindex] != '\t') { cmd[cmdindex] = line[strindex] ; if (strindex == 3) { if (!(strcmp(cmd, byprefix)) && line[4] == ' ') { while (line[++strindex] != ':') { } strncpy(cmd, blankstr, 9) ; cmdindex = -1 ; strindex++ ; } } if (strindex == 4) { if (!(strcmp(cmd, noiadv)) || !(strcmp(cmd, quiadv))) { while (line[++strindex] != ' ') { } strncpy(cmd, blankstr, 9) ; cmdindex = -1 ; } } cmdindex++ ; strindex++ ; } if (strcmp(cmd, WHILE) && strcmp(cmd, FOR) && strcmp(cmd, IF) && strcmp(cmd, LOCAL)) { fprintf(outfile, "cap which %s\n", cmd) ; fprintf(outfile, "if (!_rc) { mac def S_adoseq = \"$S_adoseq %s\" }\n", cmd) ; } strncpy(cmd, blankstr, 9) ; } } } if (fclose(outfile) == EOF) { /* Close the do-file */ fputs("Cannot close file: JUNK.DO\n", stdout) ; exit(5) ; } /* If log file contained an error, then send buffers and error message to the standard output file (normally the user's monitor). */ fputs("\n\n\n\n", stdout) ; fputs(buff11, stdout) ; fputs(buff10, stdout) ; fputs(buff9, stdout) ; fputs(buff8, stdout) ; fputs(buff7, stdout) ; fputs(buff6, stdout) ; fputs(buff5, stdout) ; fputs(buff4, stdout) ; fputs(errormsg, stdout) ; fputs(buff3, stdout) ; fputs(buff2, stdout) ; fputs(buff1, stdout) ; fputs("\n", stdout) ; } if (fclose(infile) == EOF) { /* Close the log file */ fputs("Cannot close file: _FINDBUG.LOG\n", stdout) ; exit(5) ; } return 0 ; }