dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / sgs / error / common / errortouch.c
blob4f30fe37e752c46007e2950f5017be6660787c9a
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.
28 #include <stdio.h>
29 #include <ctype.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <signal.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <stdarg.h>
37 #include "error.h"
39 static void errorprint(FILE *place, Eptr errorp, boolean print_all);
40 static void text(Eptr p, boolean use_all);
41 static void insert(int place);
42 static void execvarg(int n_pissed_on, int *r_argc, char ***r_argv);
43 static void diverterrors(char *name, int dest, Eptr **files, int ix,
44 boolean previewed, int nterrors);
45 static void hackfile(char *name, Eptr **files, int ix, int nerrors);
46 static int countfiles(Eptr *errors);
47 static int nopertain(Eptr **files);
48 static int oktotouch(char *filename);
49 static boolean preview(int nerrors, Eptr **files, int ix);
50 static int settotouch(char *name);
51 static boolean edit(char *name);
52 static int mustoverwrite(FILE *preciousfile, FILE *tmpfile);
53 static int mustwrite(char *base, int n, FILE *preciousfile);
54 static void writetouched(int overwrite);
57 * Iterate through errors
59 #define EITERATE(p, fv, i) for (p = fv[i]; p < fv[i+1]; p++)
60 #define ECITERATE(ei, p, lb) \
61 for (ei = lb; p = errors[ei], ei < nerrors; ei++)
63 #define FILEITERATE(fi, lb) for (fi = lb; fi <= nfiles; fi++)
64 int touchstatus = Q_YES;
66 void
67 findfiles(int nerrors, Eptr *errors, int *r_nfiles, Eptr ***r_files)
69 int nfiles;
70 Eptr **files;
72 char *name;
73 int ei;
74 int fi;
75 Eptr errorp;
77 nfiles = countfiles(errors);
79 files = Calloc(nfiles + 3, sizeof (Eptr*));
80 touchedfiles = Calloc(nfiles+3, sizeof (boolean));
82 * Now, partition off the error messages
83 * into those that are synchronization, discarded or
84 * not specific to any file, and those that were
85 * nulled or true errors.
87 files[0] = &errors[0];
88 ECITERATE(ei, errorp, 0) {
89 if (!(NOTSORTABLE(errorp->error_e_class)))
90 break;
93 * Now, and partition off all error messages
94 * for a given file.
96 files[1] = &errors[ei];
97 touchedfiles[0] = touchedfiles[1] = FALSE;
98 name = "\1";
99 fi = 1;
100 ECITERATE(ei, errorp, ei) {
101 if ((errorp->error_e_class == C_NULLED) ||
102 (errorp->error_e_class == C_TRUE)) {
103 if (strcmp(errorp->error_text[0], name) != 0) {
104 name = errorp->error_text[0];
105 touchedfiles[fi] = FALSE;
106 files[fi] = &errors[ei];
107 fi++;
111 files[fi] = &errors[nerrors];
112 *r_nfiles = nfiles;
113 *r_files = files;
116 static int
117 countfiles(Eptr *errors)
119 char *name;
120 int ei;
121 Eptr errorp;
123 int nfiles;
124 nfiles = 0;
125 name = "\1";
126 ECITERATE(ei, errorp, 0) {
127 if (SORTABLE(errorp->error_e_class)) {
128 if (strcmp(errorp->error_text[0], name) != 0) {
129 nfiles++;
130 name = errorp->error_text[0];
134 return (nfiles);
137 char *class_table[] = {
138 /* C_UNKNOWN 0 */ "Unknown",
139 /* C_IGNORE 1 */ "ignore",
140 /* C_SYNC 2 */ "synchronization",
141 /* C_DISCARD 3 */ "discarded",
142 /* C_NONSPEC 4 */ "non specific",
143 /* C_THISFILE 5 */ "specific to this file",
144 /* C_NULLED 6 */ "nulled",
145 /* C_TRUE 7 */ "true",
146 /* C_DUPL 8 */ "duplicated"
149 int class_count[C_LAST - C_FIRST] = {0};
151 void
152 filenames(int nfiles, Eptr **files)
154 int fi;
155 char *sep = " ";
156 int someerrors;
159 * first, simply dump out errors that
160 * don't pertain to any file
162 someerrors = nopertain(files);
164 if (nfiles) {
165 someerrors++;
166 (void) fprintf(stdout, terse
167 ? "%d file%s"
168 : "%d file%s contain%s errors",
169 nfiles, plural(nfiles), verbform(nfiles));
170 if (!terse) {
171 FILEITERATE(fi, 1) {
172 (void) fprintf(stdout, "%s\"%s\" (%d)",
173 sep, (*files[fi])->error_text[0],
174 files[fi+1] - files[fi]);
175 sep = ", ";
178 (void) fprintf(stdout, "\n");
180 if (!someerrors)
181 (void) fprintf(stdout, "No errors.\n");
185 * Dump out errors that don't pertain to any file
187 static int
188 nopertain(Eptr **files)
190 int type;
191 int someerrors = 0;
192 Eptr *erpp;
193 Eptr errorp;
195 if (files[1] - files[0] <= 0)
196 return (0);
197 for (type = C_UNKNOWN; NOTSORTABLE(type); type++) {
198 if (class_count[type] <= 0)
199 continue;
200 if (type > C_SYNC)
201 someerrors++;
202 if (terse) {
203 (void) fprintf(stdout, "\t%d %s errors NOT PRINTED\n",
204 class_count[type], class_table[type]);
205 } else {
206 (void) fprintf(stdout, "\n\t%d %s errors follow\n",
207 class_count[type], class_table[type]);
208 EITERATE(erpp, files, 0) {
209 errorp = *erpp;
210 if (errorp->error_e_class == type) {
211 errorprint(stdout, errorp, TRUE);
216 return (someerrors);
219 boolean
220 touchfiles(int nfiles, Eptr **files, int *r_edargc, char ***r_edargv)
222 char *name;
223 Eptr errorp;
224 int fi;
225 Eptr *erpp;
226 int ntrueerrors;
227 boolean scribbled;
228 int n_pissed_on; /* # of file touched */
229 int spread;
231 FILEITERATE(fi, 1) {
232 name = (*files[fi])->error_text[0];
233 spread = files[fi+1] - files[fi];
234 (void) fprintf(stdout, terse
235 ? "\"%s\" has %d error%s, "
236 : "\nFile \"%s\" has %d error%s.\n",
237 name, spread, plural(spread));
239 * First, iterate through all error messages in this file
240 * to see how many of the error messages really will
241 * get inserted into the file.
243 ntrueerrors = 0;
244 EITERATE(erpp, files, fi) {
245 errorp = *erpp;
246 if (errorp->error_e_class == C_TRUE)
247 ntrueerrors++;
249 (void) fprintf(stdout, terse ? "insert %d\n" :
250 "\t%d of these errors can be inserted into the file.\n",
251 ntrueerrors);
253 hackfile(name, files, fi, ntrueerrors);
255 scribbled = FALSE;
256 n_pissed_on = 0;
257 FILEITERATE(fi, 1) {
258 scribbled |= touchedfiles[fi];
259 n_pissed_on++;
261 if (scribbled) {
263 * Construct an execv argument
265 execvarg(n_pissed_on, r_edargc, r_edargv);
266 return (TRUE);
267 } else {
268 if (!terse)
269 (void) fprintf(stdout, "You didn't touch any files.\n");
270 return (FALSE);
274 static void
275 hackfile(char *name, Eptr **files, int ix, int nerrors)
277 boolean previewed;
278 int errordest; /* where errors go */
280 if (!oktotouch(name)) {
281 previewed = FALSE;
282 errordest = TOSTDOUT;
283 } else {
284 previewed = preview(nerrors, files, ix);
285 errordest = settotouch(name);
288 if (errordest != TOSTDOUT)
289 touchedfiles[ix] = TRUE;
291 if (previewed && (errordest == TOSTDOUT))
292 return;
294 diverterrors(name, errordest, files, ix, previewed, nerrors);
296 if (errordest == TOTHEFILE) {
298 * overwrite the original file
300 writetouched(1);
304 static boolean
305 preview(int nerrors, Eptr **files, int ix)
307 int back;
308 Eptr *erpp;
310 if (nerrors <= 0)
311 return (FALSE);
312 back = FALSE;
313 if (query) {
314 switch (inquire(terse
315 ? "Preview? "
316 : "Do you want to preview the errors first? ")) {
317 case Q_YES:
318 case Q_yes:
319 back = TRUE;
320 EITERATE(erpp, files, ix) {
321 errorprint(stdout, *erpp, TRUE);
323 if (!terse)
324 (void) fprintf(stdout, "\n");
325 default:
326 break;
329 return (back);
332 static int
333 settotouch(char *name)
335 int dest = TOSTDOUT;
337 if (query) {
338 switch (touchstatus = inquire(terse
339 ? "Touch? "
340 : "Do you want to touch file \"%s\"? ",
341 name)) {
342 case Q_NO:
343 case Q_no:
344 return (dest);
345 default:
346 break;
350 switch (probethisfile(name)) {
351 case F_NOTREAD:
352 dest = TOSTDOUT;
353 (void) fprintf(stdout, terse
354 ? "\"%s\" unreadable\n"
355 : "File \"%s\" is unreadable\n",
356 name);
357 break;
358 case F_NOTWRITE:
359 dest = TOSTDOUT;
360 (void) fprintf(stdout, terse
361 ? "\"%s\" unwritable\n"
362 : "File \"%s\" is unwritable\n",
363 name);
364 break;
365 case F_NOTEXIST:
366 dest = TOSTDOUT;
367 (void) fprintf(stdout,
368 terse ? "\"%s\" not found\n" :
369 "Can't find file \"%s\" to insert error "
370 "messages into.\n",
371 name);
372 break;
373 default:
374 dest = edit(name) ? TOSTDOUT : TOTHEFILE;
375 break;
377 return (dest);
380 static void
381 diverterrors(char *name, int dest, Eptr **files, int ix,
382 boolean previewed, int nterrors)
384 int nerrors;
385 Eptr *erpp;
386 Eptr errorp;
388 nerrors = files[ix+1] - files[ix];
390 if ((nerrors != nterrors) && (!previewed)) {
391 (void) fprintf(stdout, terse
392 ? "Uninserted errors\n"
393 : ">>Uninserted errors for file \"%s\" follow.\n",
394 name);
397 EITERATE(erpp, files, ix) {
398 errorp = *erpp;
399 if (errorp->error_e_class != C_TRUE) {
400 if (previewed || touchstatus == Q_NO)
401 continue;
402 errorprint(stdout, errorp, TRUE);
403 continue;
405 switch (dest) {
406 case TOSTDOUT:
407 if (previewed || touchstatus == Q_NO)
408 continue;
409 errorprint(stdout, errorp, TRUE);
410 break;
411 case TOTHEFILE:
412 insert(errorp->error_line);
413 text(errorp, FALSE);
414 break;
419 static int
420 oktotouch(char *filename)
422 extern char *suffixlist;
423 char *src;
424 char *pat;
425 char *osrc;
427 pat = suffixlist;
428 if (pat == 0)
429 return (0);
430 if (*pat == '*')
431 return (1);
432 while (*pat++ != '.')
433 continue;
434 --pat; /* point to the period */
436 for (src = &filename[strlen(filename)], --src;
437 (src > filename) && (*src != '.'); --src)
438 continue;
439 if (*src != '.')
440 return (0);
442 for (src++, pat++, osrc = src; *src && *pat; src = osrc, pat++) {
443 for (; *src && /* not at end of the source */
444 *pat && /* not off end of pattern */
445 *pat != '.' && /* not off end of sub pattern */
446 *pat != '*' && /* not wild card */
447 *src == *pat; /* and equal... */
448 src++, pat++)
449 continue;
450 if (*src == 0 && (*pat == 0 || *pat == '.' || *pat == '*'))
451 return (1);
452 if (*src != 0 && *pat == '*')
453 return (1);
454 while (*pat && *pat != '.')
455 pat++;
456 if (! *pat)
457 return (0);
459 return (0);
462 * Construct an execv argument
463 * We need 1 argument for the editor's name
464 * We need 1 argument for the initial search string
465 * We need n_pissed_on arguments for the file names
466 * We need 1 argument that is a null for execv.
467 * The caller fills in the editor's name.
468 * We fill in the initial search string.
469 * We fill in the arguments, and the null.
471 static void
472 execvarg(int n_pissed_on, int *r_argc, char ***r_argv)
474 Eptr p;
475 char *sep;
476 int fi;
478 (*r_argv) = Calloc(n_pissed_on + 3, sizeof (char *));
479 (*r_argc) = n_pissed_on + 2;
480 (*r_argv)[1] = "+1;/###/";
481 n_pissed_on = 2;
482 if (!terse) {
483 (void) fprintf(stdout, "You touched file(s):");
484 sep = " ";
486 FILEITERATE(fi, 1) {
487 if (!touchedfiles[fi])
488 continue;
489 p = *(files[fi]);
490 if (!terse) {
491 (void) fprintf(stdout, "%s\"%s\"", sep,
492 p->error_text[0]);
493 sep = ", ";
495 (*r_argv)[n_pissed_on++] = p->error_text[0];
497 if (!terse)
498 (void) fprintf(stdout, "\n");
499 (*r_argv)[n_pissed_on] = 0;
502 FILE *o_touchedfile; /* the old file */
503 FILE *n_touchedfile; /* the new file */
504 char *o_name;
505 char n_name[64];
506 char *canon_name = "/tmp/ErrorXXXXXX";
507 int o_lineno;
508 int n_lineno;
509 boolean tempfileopen = FALSE;
511 * open the file; guaranteed to be both readable and writable
512 * Well, if it isn't, then return TRUE if something failed
514 static boolean
515 edit(char *name)
517 o_name = name;
518 if ((o_touchedfile = fopen(name, "r")) == NULL) {
519 (void) fprintf(stderr,
520 "%s: Can't open file \"%s\" to touch (read).\n",
521 processname, name);
522 return (TRUE);
524 (void) strcpy(n_name, canon_name);
525 (void) mktemp(n_name);
526 if ((n_touchedfile = fopen(n_name, "w")) == NULL) {
527 (void) fprintf(stderr,
528 "%s: Can't open file \"%s\" to touch (write).\n",
529 processname, name);
530 return (TRUE);
532 tempfileopen = TRUE;
533 n_lineno = 0;
534 o_lineno = 0;
535 return (FALSE);
538 * Position to the line (before, after) the line given by place
540 char edbuf[BUFSIZ];
542 static void
543 insert(int place)
545 --place; /* always insert messages before the offending line */
546 for (; o_lineno < place; o_lineno++, n_lineno++) {
547 if (fgets(edbuf, BUFSIZ, o_touchedfile) == NULL)
548 return;
549 (void) fputs(edbuf, n_touchedfile);
553 static void
554 text(Eptr p, boolean use_all)
556 int offset = use_all ? 0 : 2;
558 (void) fputs(lang_table[p->error_language].lang_incomment,
559 n_touchedfile);
560 (void) fprintf(n_touchedfile, "%d [%s] ",
561 p->error_line,
562 lang_table[p->error_language].lang_name);
563 wordvprint(n_touchedfile, p->error_lgtext-offset, p->error_text+offset);
564 (void) fputs(lang_table[p->error_language].lang_outcomment,
565 n_touchedfile);
566 n_lineno++;
570 * write the touched file to its temporary copy,
571 * then bring the temporary in over the local file
573 static void
574 writetouched(int overwrite)
576 int nread;
577 FILE *localfile;
578 FILE *tmpfile;
579 int botch;
580 int oktorm;
582 botch = 0;
583 oktorm = 1;
584 while ((nread = fread(edbuf, 1, sizeof (edbuf),
585 o_touchedfile)) != 0) {
586 if (nread != fwrite(edbuf, 1, nread, n_touchedfile)) {
588 * Catastrophe in temporary area: file system full?
590 botch = 1;
591 (void) fprintf(stderr,
592 "%s: write failure: No errors inserted in \"%s\"\n",
593 processname, o_name);
596 (void) fclose(n_touchedfile);
597 (void) fclose(o_touchedfile);
599 * Now, copy the temp file back over the original
600 * file, thus preserving links, etc
602 if (botch == 0 && overwrite) {
603 botch = 0;
604 localfile = NULL;
605 tmpfile = NULL;
606 if ((localfile = fopen(o_name, "w")) == NULL) {
607 (void) fprintf(stderr,
608 "%s: Can't open file \"%s\" to overwrite.\n",
609 processname, o_name);
610 botch++;
612 if ((tmpfile = fopen(n_name, "r")) == NULL) {
613 (void) fprintf(stderr,
614 "%s: Can't open file \"%s\" to read.\n",
615 processname, n_name);
616 botch++;
618 if (!botch)
619 oktorm = mustoverwrite(localfile, tmpfile);
620 if (localfile != NULL)
621 (void) fclose(localfile);
622 if (tmpfile != NULL)
623 (void) fclose(tmpfile);
625 if (oktorm == 0) {
626 (void) fprintf(stderr,
627 "%s: Catastrophe: A copy of \"%s: was saved in \"%s\"\n",
628 processname, o_name, n_name);
629 exit(1);
632 * Kiss the temp file good bye
634 (void) unlink(n_name);
635 tempfileopen = FALSE;
638 * return 1 if the tmpfile can be removed after writing it out
640 static int
641 mustoverwrite(FILE *preciousfile, FILE *tmpfile)
643 int nread;
645 while ((nread = fread(edbuf, 1, sizeof (edbuf), tmpfile)) != 0) {
646 if (mustwrite(edbuf, nread, preciousfile) == 0)
647 return (0);
649 return (1);
652 * return 0 on catastrophe
654 static int
655 mustwrite(char *base, int n, FILE *preciousfile)
657 int nwrote;
659 if (n <= 0)
660 return (1);
661 nwrote = fwrite(base, 1, n, preciousfile);
662 if (nwrote == n)
663 return (1);
664 perror(processname);
665 switch (inquire(terse
666 ? "Botch overwriting: retry? "
667 : "Botch overwriting the source file: retry? ")) {
668 case Q_YES:
669 case Q_yes:
670 (void) mustwrite(base + nwrote, n - nwrote, preciousfile);
671 return (1);
672 case Q_NO:
673 case Q_no:
674 switch (inquire("Are you sure? ")) {
675 case Q_YES:
676 case Q_yes:
677 return (0);
678 case Q_NO:
679 case Q_no:
680 (void) mustwrite(base + nwrote, n - nwrote,
681 preciousfile);
682 return (1);
684 default:
685 return (0);
689 /* ARGSUSED */
690 void
691 onintr(int sig)
693 switch (inquire(terse
694 ? "\nContinue? "
695 : "\nInterrupt: Do you want to continue? ")) {
696 case Q_YES:
697 case Q_yes:
698 (void) signal(SIGINT, onintr);
699 return;
700 default:
701 if (tempfileopen) {
703 * Don't overwrite the original file!
705 writetouched(0);
707 exit(1);
709 /*NOTREACHED*/
712 static void
713 errorprint(FILE *place, Eptr errorp, boolean print_all)
715 int offset = print_all ? 0 : 2;
717 if (errorp->error_e_class == C_IGNORE)
718 return;
719 (void) fprintf(place, "[%s] ",
720 lang_table[errorp->error_language].lang_name);
721 wordvprint(place, errorp->error_lgtext-offset,
722 errorp->error_text+offset);
723 (void) putc('\n', place);
726 /*PRINTFLIKE1*/
728 inquire(char *format, ...)
730 char buffer[128];
731 va_list args;
733 if (queryfile == NULL)
734 return (0);
735 for (;;) {
736 do {
737 va_start(args, format);
738 (void) fflush(stdout);
739 (void) vfprintf(stderr, format, args);
740 (void) fflush(stderr);
741 va_end(args);
742 } while (fgets(buffer, 127, queryfile) == NULL);
743 switch (buffer[0]) {
744 case 'Y': return (Q_YES);
745 case 'y': return (Q_yes);
746 case 'N': return (Q_NO);
747 case 'n': return (Q_no);
748 default: (void) fprintf(stderr, "Yes or No only!\n");
754 probethisfile(char *name)
756 struct stat statbuf;
757 if (stat(name, &statbuf) < 0)
758 return (F_NOTEXIST);
759 if ((statbuf.st_mode & S_IREAD) == 0)
760 return (F_NOTREAD);
761 if ((statbuf.st_mode & S_IWRITE) == 0)
762 return (F_NOTWRITE);
763 return (F_TOUCHIT);