8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / sgs / error / common / errortouch.c
blobe5524cb8c1f0f183b3d88fcc44f2e76dd4767519
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
20 * CDDL HEADER END
24 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
29 #include <stdio.h>
30 #include <ctype.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <signal.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <stdarg.h>
38 #include "error.h"
40 static void errorprint(FILE *place, Eptr errorp, boolean print_all);
41 static void text(Eptr p, boolean use_all);
42 static void insert(int place);
43 static void execvarg(int n_pissed_on, int *r_argc, char ***r_argv);
44 static void diverterrors(char *name, int dest, Eptr **files, int ix,
45 boolean previewed, int nterrors);
46 static void hackfile(char *name, Eptr **files, int ix, int nerrors);
47 static int countfiles(Eptr *errors);
48 static int nopertain(Eptr **files);
49 static int oktotouch(char *filename);
50 static boolean preview(int nerrors, Eptr **files, int ix);
51 static int settotouch(char *name);
52 static boolean edit(char *name);
53 static int mustoverwrite(FILE *preciousfile, FILE *tmpfile);
54 static int mustwrite(char *base, int n, FILE *preciousfile);
55 static void writetouched(int overwrite);
58 * Iterate through errors
60 #define EITERATE(p, fv, i) for (p = fv[i]; p < fv[i+1]; p++)
61 #define ECITERATE(ei, p, lb) \
62 for (ei = lb; p = errors[ei], ei < nerrors; ei++)
64 #define FILEITERATE(fi, lb) for (fi = lb; fi <= nfiles; fi++)
65 int touchstatus = Q_YES;
67 void
68 findfiles(int nerrors, Eptr *errors, int *r_nfiles, Eptr ***r_files)
70 int nfiles;
71 Eptr **files;
73 char *name;
74 int ei;
75 int fi;
76 Eptr errorp;
78 nfiles = countfiles(errors);
80 files = Calloc(nfiles + 3, sizeof (Eptr*));
81 touchedfiles = Calloc(nfiles+3, sizeof (boolean));
83 * Now, partition off the error messages
84 * into those that are synchronization, discarded or
85 * not specific to any file, and those that were
86 * nulled or true errors.
88 files[0] = &errors[0];
89 ECITERATE(ei, errorp, 0) {
90 if (!(NOTSORTABLE(errorp->error_e_class)))
91 break;
94 * Now, and partition off all error messages
95 * for a given file.
97 files[1] = &errors[ei];
98 touchedfiles[0] = touchedfiles[1] = FALSE;
99 name = "\1";
100 fi = 1;
101 ECITERATE(ei, errorp, ei) {
102 if ((errorp->error_e_class == C_NULLED) ||
103 (errorp->error_e_class == C_TRUE)) {
104 if (strcmp(errorp->error_text[0], name) != 0) {
105 name = errorp->error_text[0];
106 touchedfiles[fi] = FALSE;
107 files[fi] = &errors[ei];
108 fi++;
112 files[fi] = &errors[nerrors];
113 *r_nfiles = nfiles;
114 *r_files = files;
117 static int
118 countfiles(Eptr *errors)
120 char *name;
121 int ei;
122 Eptr errorp;
124 int nfiles;
125 nfiles = 0;
126 name = "\1";
127 ECITERATE(ei, errorp, 0) {
128 if (SORTABLE(errorp->error_e_class)) {
129 if (strcmp(errorp->error_text[0], name) != 0) {
130 nfiles++;
131 name = errorp->error_text[0];
135 return (nfiles);
138 char *class_table[] = {
139 /* C_UNKNOWN 0 */ "Unknown",
140 /* C_IGNORE 1 */ "ignore",
141 /* C_SYNC 2 */ "synchronization",
142 /* C_DISCARD 3 */ "discarded",
143 /* C_NONSPEC 4 */ "non specific",
144 /* C_THISFILE 5 */ "specific to this file",
145 /* C_NULLED 6 */ "nulled",
146 /* C_TRUE 7 */ "true",
147 /* C_DUPL 8 */ "duplicated"
150 int class_count[C_LAST - C_FIRST] = {0};
152 void
153 filenames(int nfiles, Eptr **files)
155 int fi;
156 char *sep = " ";
157 int someerrors;
160 * first, simply dump out errors that
161 * don't pertain to any file
163 someerrors = nopertain(files);
165 if (nfiles) {
166 someerrors++;
167 (void) fprintf(stdout, terse
168 ? "%d file%s"
169 : "%d file%s contain%s errors",
170 nfiles, plural(nfiles), verbform(nfiles));
171 if (!terse) {
172 FILEITERATE(fi, 1) {
173 (void) fprintf(stdout, "%s\"%s\" (%d)",
174 sep, (*files[fi])->error_text[0],
175 files[fi+1] - files[fi]);
176 sep = ", ";
179 (void) fprintf(stdout, "\n");
181 if (!someerrors)
182 (void) fprintf(stdout, "No errors.\n");
186 * Dump out errors that don't pertain to any file
188 static int
189 nopertain(Eptr **files)
191 int type;
192 int someerrors = 0;
193 Eptr *erpp;
194 Eptr errorp;
196 if (files[1] - files[0] <= 0)
197 return (0);
198 for (type = C_UNKNOWN; NOTSORTABLE(type); type++) {
199 if (class_count[type] <= 0)
200 continue;
201 if (type > C_SYNC)
202 someerrors++;
203 if (terse) {
204 (void) fprintf(stdout, "\t%d %s errors NOT PRINTED\n",
205 class_count[type], class_table[type]);
206 } else {
207 (void) fprintf(stdout, "\n\t%d %s errors follow\n",
208 class_count[type], class_table[type]);
209 EITERATE(erpp, files, 0) {
210 errorp = *erpp;
211 if (errorp->error_e_class == type) {
212 errorprint(stdout, errorp, TRUE);
217 return (someerrors);
220 boolean
221 touchfiles(int nfiles, Eptr **files, int *r_edargc, char ***r_edargv)
223 char *name;
224 Eptr errorp;
225 int fi;
226 Eptr *erpp;
227 int ntrueerrors;
228 boolean scribbled;
229 int n_pissed_on; /* # of file touched */
230 int spread;
232 FILEITERATE(fi, 1) {
233 name = (*files[fi])->error_text[0];
234 spread = files[fi+1] - files[fi];
235 (void) fprintf(stdout, terse
236 ? "\"%s\" has %d error%s, "
237 : "\nFile \"%s\" has %d error%s.\n",
238 name, spread, plural(spread));
240 * First, iterate through all error messages in this file
241 * to see how many of the error messages really will
242 * get inserted into the file.
244 ntrueerrors = 0;
245 EITERATE(erpp, files, fi) {
246 errorp = *erpp;
247 if (errorp->error_e_class == C_TRUE)
248 ntrueerrors++;
250 (void) fprintf(stdout, terse ? "insert %d\n" :
251 "\t%d of these errors can be inserted into the file.\n",
252 ntrueerrors);
254 hackfile(name, files, fi, ntrueerrors);
256 scribbled = FALSE;
257 n_pissed_on = 0;
258 FILEITERATE(fi, 1) {
259 scribbled |= touchedfiles[fi];
260 n_pissed_on++;
262 if (scribbled) {
264 * Construct an execv argument
266 execvarg(n_pissed_on, r_edargc, r_edargv);
267 return (TRUE);
268 } else {
269 if (!terse)
270 (void) fprintf(stdout, "You didn't touch any files.\n");
271 return (FALSE);
275 static void
276 hackfile(char *name, Eptr **files, int ix, int nerrors)
278 boolean previewed;
279 int errordest; /* where errors go */
281 if (!oktotouch(name)) {
282 previewed = FALSE;
283 errordest = TOSTDOUT;
284 } else {
285 previewed = preview(nerrors, files, ix);
286 errordest = settotouch(name);
289 if (errordest != TOSTDOUT)
290 touchedfiles[ix] = TRUE;
292 if (previewed && (errordest == TOSTDOUT))
293 return;
295 diverterrors(name, errordest, files, ix, previewed, nerrors);
297 if (errordest == TOTHEFILE) {
299 * overwrite the original file
301 writetouched(1);
305 static boolean
306 preview(int nerrors, Eptr **files, int ix)
308 int back;
309 Eptr *erpp;
311 if (nerrors <= 0)
312 return (FALSE);
313 back = FALSE;
314 if (query) {
315 switch (inquire(terse
316 ? "Preview? "
317 : "Do you want to preview the errors first? ")) {
318 case Q_YES:
319 case Q_yes:
320 back = TRUE;
321 EITERATE(erpp, files, ix) {
322 errorprint(stdout, *erpp, TRUE);
324 if (!terse)
325 (void) fprintf(stdout, "\n");
326 default:
327 break;
330 return (back);
333 static int
334 settotouch(char *name)
336 int dest = TOSTDOUT;
338 if (query) {
339 switch (touchstatus = inquire(terse
340 ? "Touch? "
341 : "Do you want to touch file \"%s\"? ",
342 name)) {
343 case Q_NO:
344 case Q_no:
345 return (dest);
346 default:
347 break;
351 switch (probethisfile(name)) {
352 case F_NOTREAD:
353 dest = TOSTDOUT;
354 (void) fprintf(stdout, terse
355 ? "\"%s\" unreadable\n"
356 : "File \"%s\" is unreadable\n",
357 name);
358 break;
359 case F_NOTWRITE:
360 dest = TOSTDOUT;
361 (void) fprintf(stdout, terse
362 ? "\"%s\" unwritable\n"
363 : "File \"%s\" is unwritable\n",
364 name);
365 break;
366 case F_NOTEXIST:
367 dest = TOSTDOUT;
368 (void) fprintf(stdout,
369 terse ? "\"%s\" not found\n" :
370 "Can't find file \"%s\" to insert error "
371 "messages into.\n",
372 name);
373 break;
374 default:
375 dest = edit(name) ? TOSTDOUT : TOTHEFILE;
376 break;
378 return (dest);
381 static void
382 diverterrors(char *name, int dest, Eptr **files, int ix,
383 boolean previewed, int nterrors)
385 int nerrors;
386 Eptr *erpp;
387 Eptr errorp;
389 nerrors = files[ix+1] - files[ix];
391 if ((nerrors != nterrors) && (!previewed)) {
392 (void) fprintf(stdout, terse
393 ? "Uninserted errors\n"
394 : ">>Uninserted errors for file \"%s\" follow.\n",
395 name);
398 EITERATE(erpp, files, ix) {
399 errorp = *erpp;
400 if (errorp->error_e_class != C_TRUE) {
401 if (previewed || touchstatus == Q_NO)
402 continue;
403 errorprint(stdout, errorp, TRUE);
404 continue;
406 switch (dest) {
407 case TOSTDOUT:
408 if (previewed || touchstatus == Q_NO)
409 continue;
410 errorprint(stdout, errorp, TRUE);
411 break;
412 case TOTHEFILE:
413 insert(errorp->error_line);
414 text(errorp, FALSE);
415 break;
420 static int
421 oktotouch(char *filename)
423 extern char *suffixlist;
424 char *src;
425 char *pat;
426 char *osrc;
428 pat = suffixlist;
429 if (pat == 0)
430 return (0);
431 if (*pat == '*')
432 return (1);
433 while (*pat++ != '.')
434 continue;
435 --pat; /* point to the period */
437 for (src = &filename[strlen(filename)], --src;
438 (src > filename) && (*src != '.'); --src)
439 continue;
440 if (*src != '.')
441 return (0);
443 for (src++, pat++, osrc = src; *src && *pat; src = osrc, pat++) {
444 for (; *src && /* not at end of the source */
445 *pat && /* not off end of pattern */
446 *pat != '.' && /* not off end of sub pattern */
447 *pat != '*' && /* not wild card */
448 *src == *pat; /* and equal... */
449 src++, pat++)
450 continue;
451 if (*src == 0 && (*pat == 0 || *pat == '.' || *pat == '*'))
452 return (1);
453 if (*src != 0 && *pat == '*')
454 return (1);
455 while (*pat && *pat != '.')
456 pat++;
457 if (! *pat)
458 return (0);
460 return (0);
463 * Construct an execv argument
464 * We need 1 argument for the editor's name
465 * We need 1 argument for the initial search string
466 * We need n_pissed_on arguments for the file names
467 * We need 1 argument that is a null for execv.
468 * The caller fills in the editor's name.
469 * We fill in the initial search string.
470 * We fill in the arguments, and the null.
472 static void
473 execvarg(int n_pissed_on, int *r_argc, char ***r_argv)
475 Eptr p;
476 char *sep;
477 int fi;
479 (*r_argv) = Calloc(n_pissed_on + 3, sizeof (char *));
480 (*r_argc) = n_pissed_on + 2;
481 (*r_argv)[1] = "+1;/###/";
482 n_pissed_on = 2;
483 if (!terse) {
484 (void) fprintf(stdout, "You touched file(s):");
485 sep = " ";
487 FILEITERATE(fi, 1) {
488 if (!touchedfiles[fi])
489 continue;
490 p = *(files[fi]);
491 if (!terse) {
492 (void) fprintf(stdout, "%s\"%s\"", sep,
493 p->error_text[0]);
494 sep = ", ";
496 (*r_argv)[n_pissed_on++] = p->error_text[0];
498 if (!terse)
499 (void) fprintf(stdout, "\n");
500 (*r_argv)[n_pissed_on] = 0;
503 FILE *o_touchedfile; /* the old file */
504 FILE *n_touchedfile; /* the new file */
505 char *o_name;
506 char n_name[64];
507 char *canon_name = "/tmp/ErrorXXXXXX";
508 int o_lineno;
509 int n_lineno;
510 boolean tempfileopen = FALSE;
512 * open the file; guaranteed to be both readable and writable
513 * Well, if it isn't, then return TRUE if something failed
515 static boolean
516 edit(char *name)
518 o_name = name;
519 if ((o_touchedfile = fopen(name, "r")) == NULL) {
520 (void) fprintf(stderr,
521 "%s: Can't open file \"%s\" to touch (read).\n",
522 processname, name);
523 return (TRUE);
525 (void) strcpy(n_name, canon_name);
526 (void) mktemp(n_name);
527 if ((n_touchedfile = fopen(n_name, "w")) == NULL) {
528 (void) fprintf(stderr,
529 "%s: Can't open file \"%s\" to touch (write).\n",
530 processname, name);
531 return (TRUE);
533 tempfileopen = TRUE;
534 n_lineno = 0;
535 o_lineno = 0;
536 return (FALSE);
539 * Position to the line (before, after) the line given by place
541 char edbuf[BUFSIZ];
543 static void
544 insert(int place)
546 --place; /* always insert messages before the offending line */
547 for (; o_lineno < place; o_lineno++, n_lineno++) {
548 if (fgets(edbuf, BUFSIZ, o_touchedfile) == NULL)
549 return;
550 (void) fputs(edbuf, n_touchedfile);
554 static void
555 text(Eptr p, boolean use_all)
557 int offset = use_all ? 0 : 2;
559 (void) fputs(lang_table[p->error_language].lang_incomment,
560 n_touchedfile);
561 (void) fprintf(n_touchedfile, "%d [%s] ",
562 p->error_line,
563 lang_table[p->error_language].lang_name);
564 wordvprint(n_touchedfile, p->error_lgtext-offset, p->error_text+offset);
565 (void) fputs(lang_table[p->error_language].lang_outcomment,
566 n_touchedfile);
567 n_lineno++;
571 * write the touched file to its temporary copy,
572 * then bring the temporary in over the local file
574 static void
575 writetouched(int overwrite)
577 int nread;
578 FILE *localfile;
579 FILE *tmpfile;
580 int botch;
581 int oktorm;
583 botch = 0;
584 oktorm = 1;
585 while ((nread = fread(edbuf, 1, sizeof (edbuf),
586 o_touchedfile)) != NULL) {
587 if (nread != fwrite(edbuf, 1, nread, n_touchedfile)) {
589 * Catastrophe in temporary area: file system full?
591 botch = 1;
592 (void) fprintf(stderr,
593 "%s: write failure: No errors inserted in \"%s\"\n",
594 processname, o_name);
597 (void) fclose(n_touchedfile);
598 (void) fclose(o_touchedfile);
600 * Now, copy the temp file back over the original
601 * file, thus preserving links, etc
603 if (botch == 0 && overwrite) {
604 botch = 0;
605 localfile = NULL;
606 tmpfile = NULL;
607 if ((localfile = fopen(o_name, "w")) == NULL) {
608 (void) fprintf(stderr,
609 "%s: Can't open file \"%s\" to overwrite.\n",
610 processname, o_name);
611 botch++;
613 if ((tmpfile = fopen(n_name, "r")) == NULL) {
614 (void) fprintf(stderr,
615 "%s: Can't open file \"%s\" to read.\n",
616 processname, n_name);
617 botch++;
619 if (!botch)
620 oktorm = mustoverwrite(localfile, tmpfile);
621 if (localfile != NULL)
622 (void) fclose(localfile);
623 if (tmpfile != NULL)
624 (void) fclose(tmpfile);
626 if (oktorm == 0) {
627 (void) fprintf(stderr,
628 "%s: Catastrophe: A copy of \"%s: was saved in \"%s\"\n",
629 processname, o_name, n_name);
630 exit(1);
633 * Kiss the temp file good bye
635 (void) unlink(n_name);
636 tempfileopen = FALSE;
639 * return 1 if the tmpfile can be removed after writing it out
641 static int
642 mustoverwrite(FILE *preciousfile, FILE *tmpfile)
644 int nread;
646 while ((nread = fread(edbuf, 1, sizeof (edbuf), tmpfile)) != NULL) {
647 if (mustwrite(edbuf, nread, preciousfile) == 0)
648 return (0);
650 return (1);
653 * return 0 on catastrophe
655 static int
656 mustwrite(char *base, int n, FILE *preciousfile)
658 int nwrote;
660 if (n <= 0)
661 return (1);
662 nwrote = fwrite(base, 1, n, preciousfile);
663 if (nwrote == n)
664 return (1);
665 perror(processname);
666 switch (inquire(terse
667 ? "Botch overwriting: retry? "
668 : "Botch overwriting the source file: retry? ")) {
669 case Q_YES:
670 case Q_yes:
671 (void) mustwrite(base + nwrote, n - nwrote, preciousfile);
672 return (1);
673 case Q_NO:
674 case Q_no:
675 switch (inquire("Are you sure? ")) {
676 case Q_YES:
677 case Q_yes:
678 return (0);
679 case Q_NO:
680 case Q_no:
681 (void) mustwrite(base + nwrote, n - nwrote,
682 preciousfile);
683 return (1);
685 default:
686 return (0);
690 /* ARGSUSED */
691 void
692 onintr(int sig)
694 switch (inquire(terse
695 ? "\nContinue? "
696 : "\nInterrupt: Do you want to continue? ")) {
697 case Q_YES:
698 case Q_yes:
699 (void) signal(SIGINT, onintr);
700 return;
701 default:
702 if (tempfileopen) {
704 * Don't overwrite the original file!
706 writetouched(0);
708 exit(1);
710 /*NOTREACHED*/
713 static void
714 errorprint(FILE *place, Eptr errorp, boolean print_all)
716 int offset = print_all ? 0 : 2;
718 if (errorp->error_e_class == C_IGNORE)
719 return;
720 (void) fprintf(place, "[%s] ",
721 lang_table[errorp->error_language].lang_name);
722 wordvprint(place, errorp->error_lgtext-offset,
723 errorp->error_text+offset);
724 (void) putc('\n', place);
727 /*PRINTFLIKE1*/
729 inquire(char *format, ...)
731 char buffer[128];
732 va_list args;
734 if (queryfile == NULL)
735 return (0);
736 for (;;) {
737 do {
738 va_start(args, format);
739 (void) fflush(stdout);
740 (void) vfprintf(stderr, format, args);
741 (void) fflush(stderr);
742 va_end(args);
743 } while (fgets(buffer, 127, queryfile) == NULL);
744 switch (buffer[0]) {
745 case 'Y': return (Q_YES);
746 case 'y': return (Q_yes);
747 case 'N': return (Q_NO);
748 case 'n': return (Q_no);
749 default: (void) fprintf(stderr, "Yes or No only!\n");
755 probethisfile(char *name)
757 struct stat statbuf;
758 if (stat(name, &statbuf) < 0)
759 return (F_NOTEXIST);
760 if ((statbuf.st_mode & S_IREAD) == 0)
761 return (F_NOTREAD);
762 if ((statbuf.st_mode & S_IWRITE) == 0)
763 return (F_NOTWRITE);
764 return (F_TOUCHIT);