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.
27 #pragma ident "%Z%%M% %I% %E% SMI"
31 #include <sys/types.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
;
68 findfiles(int nerrors
, Eptr
*errors
, int *r_nfiles
, Eptr
***r_files
)
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
)))
94 * Now, and partition off all error messages
97 files
[1] = &errors
[ei
];
98 touchedfiles
[0] = touchedfiles
[1] = FALSE
;
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
];
112 files
[fi
] = &errors
[nerrors
];
118 countfiles(Eptr
*errors
)
127 ECITERATE(ei
, errorp
, 0) {
128 if (SORTABLE(errorp
->error_e_class
)) {
129 if (strcmp(errorp
->error_text
[0], name
) != 0) {
131 name
= errorp
->error_text
[0];
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};
153 filenames(int nfiles
, Eptr
**files
)
160 * first, simply dump out errors that
161 * don't pertain to any file
163 someerrors
= nopertain(files
);
167 (void) fprintf(stdout
, terse
169 : "%d file%s contain%s errors",
170 nfiles
, plural(nfiles
), verbform(nfiles
));
173 (void) fprintf(stdout
, "%s\"%s\" (%d)",
174 sep
, (*files
[fi
])->error_text
[0],
175 files
[fi
+1] - files
[fi
]);
179 (void) fprintf(stdout
, "\n");
182 (void) fprintf(stdout
, "No errors.\n");
186 * Dump out errors that don't pertain to any file
189 nopertain(Eptr
**files
)
196 if (files
[1] - files
[0] <= 0)
198 for (type
= C_UNKNOWN
; NOTSORTABLE(type
); type
++) {
199 if (class_count
[type
] <= 0)
204 (void) fprintf(stdout
, "\t%d %s errors NOT PRINTED\n",
205 class_count
[type
], class_table
[type
]);
207 (void) fprintf(stdout
, "\n\t%d %s errors follow\n",
208 class_count
[type
], class_table
[type
]);
209 EITERATE(erpp
, files
, 0) {
211 if (errorp
->error_e_class
== type
) {
212 errorprint(stdout
, errorp
, TRUE
);
221 touchfiles(int nfiles
, Eptr
**files
, int *r_edargc
, char ***r_edargv
)
229 int n_pissed_on
; /* # of file touched */
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.
245 EITERATE(erpp
, files
, fi
) {
247 if (errorp
->error_e_class
== C_TRUE
)
250 (void) fprintf(stdout
, terse
? "insert %d\n" :
251 "\t%d of these errors can be inserted into the file.\n",
254 hackfile(name
, files
, fi
, ntrueerrors
);
259 scribbled
|= touchedfiles
[fi
];
264 * Construct an execv argument
266 execvarg(n_pissed_on
, r_edargc
, r_edargv
);
270 (void) fprintf(stdout
, "You didn't touch any files.\n");
276 hackfile(char *name
, Eptr
**files
, int ix
, int nerrors
)
279 int errordest
; /* where errors go */
281 if (!oktotouch(name
)) {
283 errordest
= TOSTDOUT
;
285 previewed
= preview(nerrors
, files
, ix
);
286 errordest
= settotouch(name
);
289 if (errordest
!= TOSTDOUT
)
290 touchedfiles
[ix
] = TRUE
;
292 if (previewed
&& (errordest
== TOSTDOUT
))
295 diverterrors(name
, errordest
, files
, ix
, previewed
, nerrors
);
297 if (errordest
== TOTHEFILE
) {
299 * overwrite the original file
306 preview(int nerrors
, Eptr
**files
, int ix
)
315 switch (inquire(terse
317 : "Do you want to preview the errors first? ")) {
321 EITERATE(erpp
, files
, ix
) {
322 errorprint(stdout
, *erpp
, TRUE
);
325 (void) fprintf(stdout
, "\n");
334 settotouch(char *name
)
339 switch (touchstatus
= inquire(terse
341 : "Do you want to touch file \"%s\"? ",
351 switch (probethisfile(name
)) {
354 (void) fprintf(stdout
, terse
355 ? "\"%s\" unreadable\n"
356 : "File \"%s\" is unreadable\n",
361 (void) fprintf(stdout
, terse
362 ? "\"%s\" unwritable\n"
363 : "File \"%s\" is unwritable\n",
368 (void) fprintf(stdout
,
369 terse
? "\"%s\" not found\n" :
370 "Can't find file \"%s\" to insert error "
375 dest
= edit(name
) ? TOSTDOUT
: TOTHEFILE
;
382 diverterrors(char *name
, int dest
, Eptr
**files
, int ix
,
383 boolean previewed
, int nterrors
)
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",
398 EITERATE(erpp
, files
, ix
) {
400 if (errorp
->error_e_class
!= C_TRUE
) {
401 if (previewed
|| touchstatus
== Q_NO
)
403 errorprint(stdout
, errorp
, TRUE
);
408 if (previewed
|| touchstatus
== Q_NO
)
410 errorprint(stdout
, errorp
, TRUE
);
413 insert(errorp
->error_line
);
421 oktotouch(char *filename
)
423 extern char *suffixlist
;
433 while (*pat
++ != '.')
435 --pat
; /* point to the period */
437 for (src
= &filename
[strlen(filename
)], --src
;
438 (src
> filename
) && (*src
!= '.'); --src
)
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... */
451 if (*src
== 0 && (*pat
== 0 || *pat
== '.' || *pat
== '*'))
453 if (*src
!= 0 && *pat
== '*')
455 while (*pat
&& *pat
!= '.')
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.
473 execvarg(int n_pissed_on
, int *r_argc
, char ***r_argv
)
479 (*r_argv
) = Calloc(n_pissed_on
+ 3, sizeof (char *));
480 (*r_argc
) = n_pissed_on
+ 2;
481 (*r_argv
)[1] = "+1;/###/";
484 (void) fprintf(stdout
, "You touched file(s):");
488 if (!touchedfiles
[fi
])
492 (void) fprintf(stdout
, "%s\"%s\"", sep
,
496 (*r_argv
)[n_pissed_on
++] = p
->error_text
[0];
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 */
507 char *canon_name
= "/tmp/ErrorXXXXXX";
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
519 if ((o_touchedfile
= fopen(name
, "r")) == NULL
) {
520 (void) fprintf(stderr
,
521 "%s: Can't open file \"%s\" to touch (read).\n",
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",
539 * Position to the line (before, after) the line given by 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
)
550 (void) fputs(edbuf
, n_touchedfile
);
555 text(Eptr p
, boolean use_all
)
557 int offset
= use_all
? 0 : 2;
559 (void) fputs(lang_table
[p
->error_language
].lang_incomment
,
561 (void) fprintf(n_touchedfile
, "%d [%s] ",
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
,
571 * write the touched file to its temporary copy,
572 * then bring the temporary in over the local file
575 writetouched(int overwrite
)
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?
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
) {
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
);
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
);
620 oktorm
= mustoverwrite(localfile
, tmpfile
);
621 if (localfile
!= NULL
)
622 (void) fclose(localfile
);
624 (void) fclose(tmpfile
);
627 (void) fprintf(stderr
,
628 "%s: Catastrophe: A copy of \"%s: was saved in \"%s\"\n",
629 processname
, o_name
, n_name
);
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
642 mustoverwrite(FILE *preciousfile
, FILE *tmpfile
)
646 while ((nread
= fread(edbuf
, 1, sizeof (edbuf
), tmpfile
)) != NULL
) {
647 if (mustwrite(edbuf
, nread
, preciousfile
) == 0)
653 * return 0 on catastrophe
656 mustwrite(char *base
, int n
, FILE *preciousfile
)
662 nwrote
= fwrite(base
, 1, n
, preciousfile
);
666 switch (inquire(terse
667 ? "Botch overwriting: retry? "
668 : "Botch overwriting the source file: retry? ")) {
671 (void) mustwrite(base
+ nwrote
, n
- nwrote
, preciousfile
);
675 switch (inquire("Are you sure? ")) {
681 (void) mustwrite(base
+ nwrote
, n
- nwrote
,
694 switch (inquire(terse
696 : "\nInterrupt: Do you want to continue? ")) {
699 (void) signal(SIGINT
, onintr
);
704 * Don't overwrite the original file!
714 errorprint(FILE *place
, Eptr errorp
, boolean print_all
)
716 int offset
= print_all
? 0 : 2;
718 if (errorp
->error_e_class
== C_IGNORE
)
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
);
729 inquire(char *format
, ...)
734 if (queryfile
== NULL
)
738 va_start(args
, format
);
739 (void) fflush(stdout
);
740 (void) vfprintf(stderr
, format
, args
);
741 (void) fflush(stderr
);
743 } while (fgets(buffer
, 127, queryfile
) == NULL
);
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
)
758 if (stat(name
, &statbuf
) < 0)
760 if ((statbuf
.st_mode
& S_IREAD
) == 0)
762 if ((statbuf
.st_mode
& S_IWRITE
) == 0)