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
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]
24 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
30 #include <sys/types.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
;
67 findfiles(int nerrors
, Eptr
*errors
, int *r_nfiles
, Eptr
***r_files
)
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
)))
93 * Now, and partition off all error messages
96 files
[1] = &errors
[ei
];
97 touchedfiles
[0] = touchedfiles
[1] = FALSE
;
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
];
111 files
[fi
] = &errors
[nerrors
];
117 countfiles(Eptr
*errors
)
126 ECITERATE(ei
, errorp
, 0) {
127 if (SORTABLE(errorp
->error_e_class
)) {
128 if (strcmp(errorp
->error_text
[0], name
) != 0) {
130 name
= errorp
->error_text
[0];
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};
152 filenames(int nfiles
, Eptr
**files
)
159 * first, simply dump out errors that
160 * don't pertain to any file
162 someerrors
= nopertain(files
);
166 (void) fprintf(stdout
, terse
168 : "%d file%s contain%s errors",
169 nfiles
, plural(nfiles
), verbform(nfiles
));
172 (void) fprintf(stdout
, "%s\"%s\" (%d)",
173 sep
, (*files
[fi
])->error_text
[0],
174 files
[fi
+1] - files
[fi
]);
178 (void) fprintf(stdout
, "\n");
181 (void) fprintf(stdout
, "No errors.\n");
185 * Dump out errors that don't pertain to any file
188 nopertain(Eptr
**files
)
195 if (files
[1] - files
[0] <= 0)
197 for (type
= C_UNKNOWN
; NOTSORTABLE(type
); type
++) {
198 if (class_count
[type
] <= 0)
203 (void) fprintf(stdout
, "\t%d %s errors NOT PRINTED\n",
204 class_count
[type
], class_table
[type
]);
206 (void) fprintf(stdout
, "\n\t%d %s errors follow\n",
207 class_count
[type
], class_table
[type
]);
208 EITERATE(erpp
, files
, 0) {
210 if (errorp
->error_e_class
== type
) {
211 errorprint(stdout
, errorp
, TRUE
);
220 touchfiles(int nfiles
, Eptr
**files
, int *r_edargc
, char ***r_edargv
)
228 int n_pissed_on
; /* # of file touched */
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.
244 EITERATE(erpp
, files
, fi
) {
246 if (errorp
->error_e_class
== C_TRUE
)
249 (void) fprintf(stdout
, terse
? "insert %d\n" :
250 "\t%d of these errors can be inserted into the file.\n",
253 hackfile(name
, files
, fi
, ntrueerrors
);
258 scribbled
|= touchedfiles
[fi
];
263 * Construct an execv argument
265 execvarg(n_pissed_on
, r_edargc
, r_edargv
);
269 (void) fprintf(stdout
, "You didn't touch any files.\n");
275 hackfile(char *name
, Eptr
**files
, int ix
, int nerrors
)
278 int errordest
; /* where errors go */
280 if (!oktotouch(name
)) {
282 errordest
= TOSTDOUT
;
284 previewed
= preview(nerrors
, files
, ix
);
285 errordest
= settotouch(name
);
288 if (errordest
!= TOSTDOUT
)
289 touchedfiles
[ix
] = TRUE
;
291 if (previewed
&& (errordest
== TOSTDOUT
))
294 diverterrors(name
, errordest
, files
, ix
, previewed
, nerrors
);
296 if (errordest
== TOTHEFILE
) {
298 * overwrite the original file
305 preview(int nerrors
, Eptr
**files
, int ix
)
314 switch (inquire(terse
316 : "Do you want to preview the errors first? ")) {
320 EITERATE(erpp
, files
, ix
) {
321 errorprint(stdout
, *erpp
, TRUE
);
324 (void) fprintf(stdout
, "\n");
333 settotouch(char *name
)
338 switch (touchstatus
= inquire(terse
340 : "Do you want to touch file \"%s\"? ",
350 switch (probethisfile(name
)) {
353 (void) fprintf(stdout
, terse
354 ? "\"%s\" unreadable\n"
355 : "File \"%s\" is unreadable\n",
360 (void) fprintf(stdout
, terse
361 ? "\"%s\" unwritable\n"
362 : "File \"%s\" is unwritable\n",
367 (void) fprintf(stdout
,
368 terse
? "\"%s\" not found\n" :
369 "Can't find file \"%s\" to insert error "
374 dest
= edit(name
) ? TOSTDOUT
: TOTHEFILE
;
381 diverterrors(char *name
, int dest
, Eptr
**files
, int ix
,
382 boolean previewed
, int nterrors
)
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",
397 EITERATE(erpp
, files
, ix
) {
399 if (errorp
->error_e_class
!= C_TRUE
) {
400 if (previewed
|| touchstatus
== Q_NO
)
402 errorprint(stdout
, errorp
, TRUE
);
407 if (previewed
|| touchstatus
== Q_NO
)
409 errorprint(stdout
, errorp
, TRUE
);
412 insert(errorp
->error_line
);
420 oktotouch(char *filename
)
422 extern char *suffixlist
;
432 while (*pat
++ != '.')
434 --pat
; /* point to the period */
436 for (src
= &filename
[strlen(filename
)], --src
;
437 (src
> filename
) && (*src
!= '.'); --src
)
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... */
450 if (*src
== 0 && (*pat
== 0 || *pat
== '.' || *pat
== '*'))
452 if (*src
!= 0 && *pat
== '*')
454 while (*pat
&& *pat
!= '.')
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.
472 execvarg(int n_pissed_on
, int *r_argc
, char ***r_argv
)
478 (*r_argv
) = Calloc(n_pissed_on
+ 3, sizeof (char *));
479 (*r_argc
) = n_pissed_on
+ 2;
480 (*r_argv
)[1] = "+1;/###/";
483 (void) fprintf(stdout
, "You touched file(s):");
487 if (!touchedfiles
[fi
])
491 (void) fprintf(stdout
, "%s\"%s\"", sep
,
495 (*r_argv
)[n_pissed_on
++] = p
->error_text
[0];
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 */
506 char *canon_name
= "/tmp/ErrorXXXXXX";
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
518 if ((o_touchedfile
= fopen(name
, "r")) == NULL
) {
519 (void) fprintf(stderr
,
520 "%s: Can't open file \"%s\" to touch (read).\n",
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",
538 * Position to the line (before, after) the line given by 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
)
549 (void) fputs(edbuf
, n_touchedfile
);
554 text(Eptr p
, boolean use_all
)
556 int offset
= use_all
? 0 : 2;
558 (void) fputs(lang_table
[p
->error_language
].lang_incomment
,
560 (void) fprintf(n_touchedfile
, "%d [%s] ",
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
,
570 * write the touched file to its temporary copy,
571 * then bring the temporary in over the local file
574 writetouched(int overwrite
)
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?
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
) {
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
);
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
);
619 oktorm
= mustoverwrite(localfile
, tmpfile
);
620 if (localfile
!= NULL
)
621 (void) fclose(localfile
);
623 (void) fclose(tmpfile
);
626 (void) fprintf(stderr
,
627 "%s: Catastrophe: A copy of \"%s: was saved in \"%s\"\n",
628 processname
, o_name
, n_name
);
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
641 mustoverwrite(FILE *preciousfile
, FILE *tmpfile
)
645 while ((nread
= fread(edbuf
, 1, sizeof (edbuf
), tmpfile
)) != 0) {
646 if (mustwrite(edbuf
, nread
, preciousfile
) == 0)
652 * return 0 on catastrophe
655 mustwrite(char *base
, int n
, FILE *preciousfile
)
661 nwrote
= fwrite(base
, 1, n
, preciousfile
);
665 switch (inquire(terse
666 ? "Botch overwriting: retry? "
667 : "Botch overwriting the source file: retry? ")) {
670 (void) mustwrite(base
+ nwrote
, n
- nwrote
, preciousfile
);
674 switch (inquire("Are you sure? ")) {
680 (void) mustwrite(base
+ nwrote
, n
- nwrote
,
693 switch (inquire(terse
695 : "\nInterrupt: Do you want to continue? ")) {
698 (void) signal(SIGINT
, onintr
);
703 * Don't overwrite the original file!
713 errorprint(FILE *place
, Eptr errorp
, boolean print_all
)
715 int offset
= print_all
? 0 : 2;
717 if (errorp
->error_e_class
== C_IGNORE
)
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
);
728 inquire(char *format
, ...)
733 if (queryfile
== NULL
)
737 va_start(args
, format
);
738 (void) fflush(stdout
);
739 (void) vfprintf(stderr
, format
, args
);
740 (void) fflush(stderr
);
742 } while (fgets(buffer
, 127, queryfile
) == NULL
);
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
)
757 if (stat(name
, &statbuf
) < 0)
759 if ((statbuf
.st_mode
& S_IREAD
) == 0)
761 if ((statbuf
.st_mode
& S_IWRITE
) == 0)