#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include "error.h"
static void errorprint(FILE *place, Eptr errorp, boolean print_all);
static void text(Eptr p, boolean use_all);
static void insert(int place);
static void execvarg(int n_pissed_on, int *r_argc, char ***r_argv);
static void diverterrors(char *name, int dest, Eptr **files, int ix,
boolean previewed, int nterrors);
static void hackfile(char *name, Eptr **files, int ix, int nerrors);
static int countfiles(Eptr *errors);
static int nopertain(Eptr **files);
static int oktotouch(char *filename);
static boolean preview(int nerrors, Eptr **files, int ix);
static int settotouch(char *name);
static boolean edit(char *name);
static int mustoverwrite(FILE *preciousfile, FILE *tmpfile);
static int mustwrite(char *base, int n, FILE *preciousfile);
static void writetouched(int overwrite);
#define EITERATE(p, fv, i) for (p = fv[i]; p < fv[i+1]; p++)
#define ECITERATE(ei, p, lb) \
for (ei = lb; p = errors[ei], ei < nerrors; ei++)
#define FILEITERATE(fi, lb) for (fi = lb; fi <= nfiles; fi++)
int touchstatus = Q_YES;
boolean *touchedfiles;
void
findfiles(int nerrors, Eptr *errors, int *r_nfiles, Eptr ***r_files)
{
int nfiles;
Eptr **files;
char *name;
int ei;
int fi;
Eptr errorp;
nfiles = countfiles(errors);
files = Calloc(nfiles + 3, sizeof (Eptr*));
touchedfiles = Calloc(nfiles+3, sizeof (boolean));
files[0] = &errors[0];
ECITERATE(ei, errorp, 0) {
if (!(NOTSORTABLE(errorp->error_e_class)))
break;
}
files[1] = &errors[ei];
touchedfiles[0] = touchedfiles[1] = FALSE;
name = "\1";
fi = 1;
ECITERATE(ei, errorp, ei) {
if ((errorp->error_e_class == C_NULLED) ||
(errorp->error_e_class == C_TRUE)) {
if (strcmp(errorp->error_text[0], name) != 0) {
name = errorp->error_text[0];
touchedfiles[fi] = FALSE;
files[fi] = &errors[ei];
fi++;
}
}
}
files[fi] = &errors[nerrors];
*r_nfiles = nfiles;
*r_files = files;
}
static int
countfiles(Eptr *errors)
{
char *name;
int ei;
Eptr errorp;
int nfiles;
nfiles = 0;
name = "\1";
ECITERATE(ei, errorp, 0) {
if (SORTABLE(errorp->error_e_class)) {
if (strcmp(errorp->error_text[0], name) != 0) {
nfiles++;
name = errorp->error_text[0];
}
}
}
return (nfiles);
}
char *class_table[] = {
"Unknown",
"ignore",
"synchronization",
"discarded",
"non specific",
"specific to this file",
"nulled",
"true",
"duplicated"
};
int class_count[C_LAST - C_FIRST] = {0};
void
filenames(int nfiles, Eptr **files)
{
int fi;
char *sep = " ";
int someerrors;
someerrors = nopertain(files);
if (nfiles) {
someerrors++;
(void) fprintf(stdout, terse
? "%d file%s"
: "%d file%s contain%s errors",
nfiles, plural(nfiles), verbform(nfiles));
if (!terse) {
FILEITERATE(fi, 1) {
(void) fprintf(stdout, "%s\"%s\" (%d)",
sep, (*files[fi])->error_text[0],
files[fi+1] - files[fi]);
sep = ", ";
}
}
(void) fprintf(stdout, "\n");
}
if (!someerrors)
(void) fprintf(stdout, "No errors.\n");
}
static int
nopertain(Eptr **files)
{
int type;
int someerrors = 0;
Eptr *erpp;
Eptr errorp;
if (files[1] - files[0] <= 0)
return (0);
for (type = C_UNKNOWN; NOTSORTABLE(type); type++) {
if (class_count[type] <= 0)
continue;
if (type > C_SYNC)
someerrors++;
if (terse) {
(void) fprintf(stdout, "\t%d %s errors NOT PRINTED\n",
class_count[type], class_table[type]);
} else {
(void) fprintf(stdout, "\n\t%d %s errors follow\n",
class_count[type], class_table[type]);
EITERATE(erpp, files, 0) {
errorp = *erpp;
if (errorp->error_e_class == type) {
errorprint(stdout, errorp, TRUE);
}
}
}
}
return (someerrors);
}
boolean
touchfiles(int nfiles, Eptr **files, int *r_edargc, char ***r_edargv)
{
char *name;
Eptr errorp;
int fi;
Eptr *erpp;
int ntrueerrors;
boolean scribbled;
int n_pissed_on;
int spread;
FILEITERATE(fi, 1) {
name = (*files[fi])->error_text[0];
spread = files[fi+1] - files[fi];
(void) fprintf(stdout, terse
? "\"%s\" has %d error%s, "
: "\nFile \"%s\" has %d error%s.\n",
name, spread, plural(spread));
ntrueerrors = 0;
EITERATE(erpp, files, fi) {
errorp = *erpp;
if (errorp->error_e_class == C_TRUE)
ntrueerrors++;
}
(void) fprintf(stdout, terse ? "insert %d\n" :
"\t%d of these errors can be inserted into the file.\n",
ntrueerrors);
hackfile(name, files, fi, ntrueerrors);
}
scribbled = FALSE;
n_pissed_on = 0;
FILEITERATE(fi, 1) {
scribbled |= touchedfiles[fi];
n_pissed_on++;
}
if (scribbled) {
execvarg(n_pissed_on, r_edargc, r_edargv);
return (TRUE);
} else {
if (!terse)
(void) fprintf(stdout, "You didn't touch any files.\n");
return (FALSE);
}
}
static void
hackfile(char *name, Eptr **files, int ix, int nerrors)
{
boolean previewed;
int errordest;
if (!oktotouch(name)) {
previewed = FALSE;
errordest = TOSTDOUT;
} else {
previewed = preview(nerrors, files, ix);
errordest = settotouch(name);
}
if (errordest != TOSTDOUT)
touchedfiles[ix] = TRUE;
if (previewed && (errordest == TOSTDOUT))
return;
diverterrors(name, errordest, files, ix, previewed, nerrors);
if (errordest == TOTHEFILE) {
writetouched(1);
}
}
static boolean
preview(int nerrors, Eptr **files, int ix)
{
int back;
Eptr *erpp;
if (nerrors <= 0)
return (FALSE);
back = FALSE;
if (query) {
switch (inquire(terse
? "Preview? "
: "Do you want to preview the errors first? ")) {
case Q_YES:
case Q_yes:
back = TRUE;
EITERATE(erpp, files, ix) {
errorprint(stdout, *erpp, TRUE);
}
if (!terse)
(void) fprintf(stdout, "\n");
default:
break;
}
}
return (back);
}
static int
settotouch(char *name)
{
int dest = TOSTDOUT;
if (query) {
switch (touchstatus = inquire(terse
? "Touch? "
: "Do you want to touch file \"%s\"? ",
name)) {
case Q_NO:
case Q_no:
return (dest);
default:
break;
}
}
switch (probethisfile(name)) {
case F_NOTREAD:
dest = TOSTDOUT;
(void) fprintf(stdout, terse
? "\"%s\" unreadable\n"
: "File \"%s\" is unreadable\n",
name);
break;
case F_NOTWRITE:
dest = TOSTDOUT;
(void) fprintf(stdout, terse
? "\"%s\" unwritable\n"
: "File \"%s\" is unwritable\n",
name);
break;
case F_NOTEXIST:
dest = TOSTDOUT;
(void) fprintf(stdout,
terse ? "\"%s\" not found\n" :
"Can't find file \"%s\" to insert error "
"messages into.\n",
name);
break;
default:
dest = edit(name) ? TOSTDOUT : TOTHEFILE;
break;
}
return (dest);
}
static void
diverterrors(char *name, int dest, Eptr **files, int ix,
boolean previewed, int nterrors)
{
int nerrors;
Eptr *erpp;
Eptr errorp;
nerrors = files[ix+1] - files[ix];
if ((nerrors != nterrors) && (!previewed)) {
(void) fprintf(stdout, terse
? "Uninserted errors\n"
: ">>Uninserted errors for file \"%s\" follow.\n",
name);
}
EITERATE(erpp, files, ix) {
errorp = *erpp;
if (errorp->error_e_class != C_TRUE) {
if (previewed || touchstatus == Q_NO)
continue;
errorprint(stdout, errorp, TRUE);
continue;
}
switch (dest) {
case TOSTDOUT:
if (previewed || touchstatus == Q_NO)
continue;
errorprint(stdout, errorp, TRUE);
break;
case TOTHEFILE:
insert(errorp->error_line);
text(errorp, FALSE);
break;
}
}
}
static int
oktotouch(char *filename)
{
extern char *suffixlist;
char *src;
char *pat;
char *osrc;
pat = suffixlist;
if (pat == 0)
return (0);
if (*pat == '*')
return (1);
while (*pat++ != '.')
continue;
--pat;
for (src = &filename[strlen(filename)], --src;
(src > filename) && (*src != '.'); --src)
continue;
if (*src != '.')
return (0);
for (src++, pat++, osrc = src; *src && *pat; src = osrc, pat++) {
for (; *src &&
*pat &&
*pat != '.' &&
*pat != '*' &&
*src == *pat;
src++, pat++)
continue;
if (*src == 0 && (*pat == 0 || *pat == '.' || *pat == '*'))
return (1);
if (*src != 0 && *pat == '*')
return (1);
while (*pat && *pat != '.')
pat++;
if (! *pat)
return (0);
}
return (0);
}
static void
execvarg(int n_pissed_on, int *r_argc, char ***r_argv)
{
Eptr p;
char *sep;
int fi;
(*r_argv) = Calloc(n_pissed_on + 3, sizeof (char *));
(*r_argc) = n_pissed_on + 2;
(*r_argv)[1] = "+1;/###/";
n_pissed_on = 2;
if (!terse) {
(void) fprintf(stdout, "You touched file(s):");
sep = " ";
}
FILEITERATE(fi, 1) {
if (!touchedfiles[fi])
continue;
p = *(files[fi]);
if (!terse) {
(void) fprintf(stdout, "%s\"%s\"", sep,
p->error_text[0]);
sep = ", ";
}
(*r_argv)[n_pissed_on++] = p->error_text[0];
}
if (!terse)
(void) fprintf(stdout, "\n");
(*r_argv)[n_pissed_on] = 0;
}
FILE *o_touchedfile;
FILE *n_touchedfile;
char *o_name;
char n_name[64];
char *canon_name = "/tmp/ErrorXXXXXX";
int o_lineno;
int n_lineno;
boolean tempfileopen = FALSE;
static boolean
edit(char *name)
{
o_name = name;
if ((o_touchedfile = fopen(name, "r")) == NULL) {
(void) fprintf(stderr,
"%s: Can't open file \"%s\" to touch (read).\n",
processname, name);
return (TRUE);
}
(void) strcpy(n_name, canon_name);
(void) mktemp(n_name);
if ((n_touchedfile = fopen(n_name, "w")) == NULL) {
(void) fprintf(stderr,
"%s: Can't open file \"%s\" to touch (write).\n",
processname, name);
return (TRUE);
}
tempfileopen = TRUE;
n_lineno = 0;
o_lineno = 0;
return (FALSE);
}
char edbuf[BUFSIZ];
static void
insert(int place)
{
--place;
for (; o_lineno < place; o_lineno++, n_lineno++) {
if (fgets(edbuf, BUFSIZ, o_touchedfile) == NULL)
return;
(void) fputs(edbuf, n_touchedfile);
}
}
static void
text(Eptr p, boolean use_all)
{
int offset = use_all ? 0 : 2;
(void) fputs(lang_table[p->error_language].lang_incomment,
n_touchedfile);
(void) fprintf(n_touchedfile, "%d [%s] ",
p->error_line,
lang_table[p->error_language].lang_name);
wordvprint(n_touchedfile, p->error_lgtext-offset, p->error_text+offset);
(void) fputs(lang_table[p->error_language].lang_outcomment,
n_touchedfile);
n_lineno++;
}
static void
writetouched(int overwrite)
{
int nread;
FILE *localfile;
FILE *tmpfile;
int botch;
int oktorm;
botch = 0;
oktorm = 1;
while ((nread = fread(edbuf, 1, sizeof (edbuf),
o_touchedfile)) != 0) {
if (nread != fwrite(edbuf, 1, nread, n_touchedfile)) {
botch = 1;
(void) fprintf(stderr,
"%s: write failure: No errors inserted in \"%s\"\n",
processname, o_name);
}
}
(void) fclose(n_touchedfile);
(void) fclose(o_touchedfile);
if (botch == 0 && overwrite) {
botch = 0;
localfile = NULL;
tmpfile = NULL;
if ((localfile = fopen(o_name, "w")) == NULL) {
(void) fprintf(stderr,
"%s: Can't open file \"%s\" to overwrite.\n",
processname, o_name);
botch++;
}
if ((tmpfile = fopen(n_name, "r")) == NULL) {
(void) fprintf(stderr,
"%s: Can't open file \"%s\" to read.\n",
processname, n_name);
botch++;
}
if (!botch)
oktorm = mustoverwrite(localfile, tmpfile);
if (localfile != NULL)
(void) fclose(localfile);
if (tmpfile != NULL)
(void) fclose(tmpfile);
}
if (oktorm == 0) {
(void) fprintf(stderr,
"%s: Catastrophe: A copy of \"%s: was saved in \"%s\"\n",
processname, o_name, n_name);
exit(1);
}
(void) unlink(n_name);
tempfileopen = FALSE;
}
static int
mustoverwrite(FILE *preciousfile, FILE *tmpfile)
{
int nread;
while ((nread = fread(edbuf, 1, sizeof (edbuf), tmpfile)) != 0) {
if (mustwrite(edbuf, nread, preciousfile) == 0)
return (0);
}
return (1);
}
static int
mustwrite(char *base, int n, FILE *preciousfile)
{
int nwrote;
if (n <= 0)
return (1);
nwrote = fwrite(base, 1, n, preciousfile);
if (nwrote == n)
return (1);
perror(processname);
switch (inquire(terse
? "Botch overwriting: retry? "
: "Botch overwriting the source file: retry? ")) {
case Q_YES:
case Q_yes:
(void) mustwrite(base + nwrote, n - nwrote, preciousfile);
return (1);
case Q_NO:
case Q_no:
switch (inquire("Are you sure? ")) {
case Q_YES:
case Q_yes:
return (0);
case Q_NO:
case Q_no:
default:
(void) mustwrite(base + nwrote, n - nwrote,
preciousfile);
return (1);
}
default:
return (0);
}
}
void
onintr(int sig)
{
switch (inquire(terse
? "\nContinue? "
: "\nInterrupt: Do you want to continue? ")) {
case Q_YES:
case Q_yes:
(void) signal(SIGINT, onintr);
return;
default:
if (tempfileopen) {
writetouched(0);
}
exit(1);
}
}
static void
errorprint(FILE *place, Eptr errorp, boolean print_all)
{
int offset = print_all ? 0 : 2;
if (errorp->error_e_class == C_IGNORE)
return;
(void) fprintf(place, "[%s] ",
lang_table[errorp->error_language].lang_name);
wordvprint(place, errorp->error_lgtext-offset,
errorp->error_text+offset);
(void) putc('\n', place);
}
int
inquire(char *format, ...)
{
char buffer[128];
va_list args;
if (queryfile == NULL)
return (0);
for (;;) {
do {
va_start(args, format);
(void) fflush(stdout);
(void) vfprintf(stderr, format, args);
(void) fflush(stderr);
va_end(args);
} while (fgets(buffer, 127, queryfile) == NULL);
switch (buffer[0]) {
case 'Y': return (Q_YES);
case 'y': return (Q_yes);
case 'N': return (Q_NO);
case 'n': return (Q_no);
default: (void) fprintf(stderr, "Yes or No only!\n");
}
}
}
int
probethisfile(char *name)
{
struct stat statbuf;
if (stat(name, &statbuf) < 0)
return (F_NOTEXIST);
if ((statbuf.st_mode & S_IREAD) == 0)
return (F_NOTREAD);
if ((statbuf.st_mode & S_IWRITE) == 0)
return (F_NOTWRITE);
return (F_TOUCHIT);
}