]> git.saurik.com Git - apple/hfs.git/blob - fsck_hfs/fsck_messages.c
4047941a8a2b4d7c39dedc2d0831fb4b1d6b8953
[apple/hfs.git] / fsck_hfs / fsck_messages.c
1 /*
2 * Copyright (c) 2008 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <assert.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <stddef.h>
28 #include <stdarg.h>
29 #include <string.h>
30 #include <assert.h>
31 #include <Block.h>
32
33 #include "fsck_messages.h"
34 #include "fsck_keys.h"
35 #include "fsck_msgnums.h"
36
37 extern fsck_message_t fsck_messages_common[];
38
39 // The following structures are used internally, only
40 struct messages {
41 int low;
42 int high;
43 fsck_message_t *msgs;
44 struct messages *next, *prev;
45 };
46
47 #define cfFromFD 0x01
48
49 /*
50 * The internal verson of fsck_ctx_t -- this describes the output type,
51 * where it goes, etc. It's an opaque type so that it can change size
52 * in the future without affecting any clients of the code.
53 */
54
55 struct context {
56 FILE *fp; // output file structure
57 int flags; // various flags, mostly private
58 int verb; // the verbosity of the program -- controls what is output
59 enum fsck_output_type style;
60 enum fsck_default_answer_type resp; // none, no, or yes
61 int num; // number of messages in the array
62 fsck_message_t **msgs;
63 void (*writer)(fsck_ctx_t, const char*); // write strings to stdout
64 void (*logger)(fsck_ctx_t, const char *); // write strings to log file
65 char guiControl;
66 char xmlControl;
67 char writeToLog; // When 1, the string should be written to log file, otherwise to standard out.
68 fsckBlock_t preMessage;
69 fsckBlock_t postMessage;
70 };
71
72 /*
73 * printv(fsck_ctxt_t, const char *, va_list)
74 * Take the format and ap list, and turn them into a string.
75 * Then call the writer to print it out (or do whatever
76 * the writer wants with it, if it's an app-supplised function).
77 *
78 */
79 static void
80 printv(fsck_ctx_t c, const char *fmt, va_list ap)
81 {
82 struct context *ctx = (struct context *)c;
83 char buf[BUFSIZ + 1];
84 size_t length;
85 va_list ap2;
86
87 if (c == NULL)
88 return;
89 __va_copy(ap2, ap); // Just in case we need it
90 length = vsnprintf(buf, BUFSIZ, fmt, ap);
91 if (length > BUFSIZ) {
92 // We need to allocate space for it
93 size_t l2 = length + 1;
94 char *bufp = malloc(l2);
95 if (bufp == NULL) {
96 strcpy(buf, "* * * cannot allocate memory * * *\n");
97 bufp = buf;
98 } else {
99 length = vsnprintf(bufp, length, fmt, ap2);
100 if (length >= l2) { // This should not happen!
101 strcpy(buf, " * * * cannot allocate memory * * *\n");
102 free(bufp);
103 bufp = buf;
104 } else {
105 if (ctx->writer) (ctx->writer)(ctx, bufp);
106 free(bufp);
107 bufp = NULL;
108 }
109 }
110 if (bufp == NULL)
111 return;
112 }
113
114 // If the current state of printing is logging to file,
115 // call the logger that writes strings only in traditional
116 // output forms. Otherwise, print the strings in the
117 // format option provided by the caller.
118 if (ctx->writeToLog == 1) {
119 if (ctx->logger) (ctx->logger)(ctx, buf);
120 } else {
121 if (ctx->writer) (ctx->writer)(ctx, buf);
122 }
123 return;
124 }
125
126 /*
127 * printargs(fsck_ctx_t, const char *, ...)
128 * An argument-list verison of printv. It simply wraps up
129 * the argument list in a va_list, and then calls printv.
130 */
131 static void
132 printargs(fsck_ctx_t c, const char *fmt, ...)
133 {
134 va_list ap;
135
136 va_start(ap, fmt);
137 printv(c, fmt, ap);
138 }
139
140 /*
141 * stdprint(fsck_ctx_t, const char *)
142 * Default writer. Just prints to the set FILE*, or stdout
143 * if it's not set.
144 */
145
146 static void
147 stdprint(fsck_ctx_t c, const char *str)
148 {
149 struct context *ctx = (struct context*)c;
150 if (c) {
151 fputs(str, ctx->fp ? ctx->fp : stdout);
152 fflush(ctx->fp ? ctx->fp : stdout);
153 }
154
155 }
156 /*
157 * typestring(int type)
158 * Return a string value corresponding to the type. This is used
159 * to present it during XML output, as one of the appropriate
160 * tags.
161 */
162 static const char *
163 typestring(int type)
164 {
165 switch (type) {
166 case fsckMsgVerify:
167 return kfsckVerify;
168 case fsckMsgInfo:
169 return kfsckInformation;
170 case fsckMsgRepair:
171 return kfsckRepair;
172 case fsckMsgSuccess:
173 return kfsckSuccess;
174 case fsckMsgError:
175 return kfsckError;
176 case fsckMsgFail:
177 return kfsckFail;
178 case fsckMsgDamageInfo:
179 return kfsckDamageinfo;
180 case fsckMsgProgress:
181 return kfsckProgress;
182 case fsckMsgNotice:
183 return kfsckInformation;
184 default:
185 return kfsckUnknown;
186 }
187 }
188
189 /*
190 * verbosity_string(int type)
191 * Return a string value corresponding to the verbosity. This is
192 * used to present it during XML output, as one of the appropriate
193 * tags.
194 */
195 static const char *
196 verbosity_string(int level)
197 {
198 switch(level) {
199 case fsckLevel0:
200 return kfsckLevel0;
201 case fsckLevel1:
202 default:
203 return kfsckLevel1;
204 }
205 }
206
207 /*
208 * convertfmt(const char *in)
209 * This is an ugly little function whose job is to convert
210 * from a normal printf-style string (e.g., "How now %s cow?")
211 * into something that can be used with Cocoa formatting. This
212 * means replacing each "%<formatter>" with "%<number>$@"; the
213 * reason we do this is so that the internationalized strings can
214 * move parameters around as desired (e.g., in language A, the third
215 * parameter may need to be first). The caller needs to free the
216 * return value.
217 */
218 static char *
219 convertfmt(const char *in)
220 {
221 char *retval = NULL;
222 int numargs = 0;
223 char *cp;
224 enum { fNone, fPercent } fs;
225
226 for (cp = (char*)in; cp; cp = strchr(cp, '%')) {
227 numargs++;
228 cp++;
229 }
230
231 retval = calloc(1, strlen(in) + numargs * 5 + 1);
232 if (retval == NULL)
233 return NULL;
234
235 fs = fNone;
236 numargs = 0;
237 for (cp = retval; *in; in++) {
238 if (fs == fNone) {
239 *cp++ = *in;
240 if (*in == '%') {
241 if (in[1] == '%') {
242 *cp++ = '%';
243 in++;
244 } else {
245 fs = fPercent;
246 cp += sprintf(cp, "%d$@", ++numargs);
247 }
248 }
249 } else if (fs == fPercent) {
250 switch (*in) {
251 case 'd': case 'i': case 'o': case 'u': case 'x': case 'l':
252 case 'X': case 'D': case 'O': case 'U': case 'e':
253 case 'E': case 'f': case 'F': case 'g': case 'G':
254 case 'a': case 'A': case 'c': case 'C': case 's':
255 case 'S': case 'p': case 'n':
256 fs = fNone;
257 break;
258 }
259 }
260 }
261 *cp = 0;
262 return retval;
263 }
264
265 /*
266 * fsckCreate()
267 * Allocates space for an fsck_ctx_t context. It also sets up
268 * the standard message blocks (defined earlier in this file).
269 * It will return NULL in the case of any error.
270 */
271 fsck_ctx_t
272 fsckCreate(void)
273 {
274 struct context *rv = NULL;
275
276 rv = calloc(1, sizeof(*rv));
277 if (rv == NULL) {
278 return NULL;
279 }
280 if (fsckAddMessages(rv, fsck_messages_common) == -1) {
281 fsckDestroy(rv);
282 return NULL;
283 }
284 fsckSetWriter(rv, &stdprint);
285
286 return (fsck_ctx_t)rv;
287 }
288
289 /*
290 * fsckSetBlock()
291 * Sets the block to be called for the specific phase -- currently, only
292 * before or after a message is to be printed/logged. The block is copied
293 * for later use.
294 */
295 void
296 fsckSetBlock(fsck_ctx_t c, fsck_block_phase_t phase, fsckBlock_t bp)
297 {
298 struct context *ctx = c;
299 if (c != NULL) {
300 switch (phase) {
301 case fsckPhaseBeforeMessage:
302 if (ctx->preMessage) {
303 Block_release(ctx->preMessage);
304 ctx->preMessage = NULL;
305 }
306 if (bp)
307 ctx->preMessage = (fsckBlock_t)Block_copy(bp);
308 break;
309 case fsckPhaseAfterMessage:
310 if (ctx->postMessage) {
311 Block_release(ctx->postMessage);
312 ctx->postMessage = NULL;
313 }
314 if (bp)
315 ctx->postMessage = (fsckBlock_t)Block_copy(bp);
316 break;
317 case fsckPhaseNone:
318 /* Just here for compiler warnings */
319 break;
320 }
321
322 }
323 return;
324 }
325
326 /*
327 * fsckGetBlock()
328 * Return the pointer to the block for the specified phase. The block pointer
329 * is not copied.
330 */
331 fsckBlock_t
332 fsckGetBlock(fsck_ctx_t c, fsck_block_phase_t phase)
333 {
334 struct context *ctx = c;
335 fsckBlock_t retval = NULL;
336 if (c != NULL) {
337 switch (phase) {
338 case fsckPhaseBeforeMessage:
339 retval = ctx->preMessage;
340 break;
341 case fsckPhaseAfterMessage:
342 retval = ctx->postMessage;
343 break;
344 case fsckPhaseNone:
345 break;
346 }
347 }
348 return retval;
349 }
350
351 /*
352 * fsckSetWriter(context, void (*)(fsck_ctx_t, const char *)
353 * Call a function for each message to be printed.
354 * This defaults to stdprint (see above).
355 */
356 int
357 fsckSetWriter(fsck_ctx_t c, void (*fp)(fsck_ctx_t, const char*))
358 {
359 struct context *ctx = c;
360 if (c != NULL) {
361 ctx->writer = fp;
362 return 0;
363 } else {
364 return -1;
365 }
366 }
367
368 /* Initialize the logger function that will write strings to log file */
369 int
370 fsckSetLogger(fsck_ctx_t c, void (*fp)(fsck_ctx_t, const char*))
371 {
372 struct context *ctx = c;
373 if (c != NULL) {
374 ctx->logger = fp;
375 return 0;
376 } else {
377 return -1;
378 }
379 }
380
381 /*
382 * fsckSetOutput(context, FILE*)
383 * Set the FILE* to be used for output. Returns
384 * 0 on success, and -1 if it has already been set.
385 */
386 int
387 fsckSetOutput(fsck_ctx_t c, FILE *fp)
388 {
389 struct context *ctx = c;
390
391 if (c != NULL) {
392 ctx->fp = fp;
393 return 0;
394 } else
395 return -1;
396 }
397
398 /*
399 * fsckSetFile(context, fd)
400 * Use a file descriptor, instead of a FILE*, for output.
401 * Because of how stdio works, you should not use 1 or 2
402 * for this -- use fsckSetOutput() with stdout/stderr instead.
403 * If you do use this, then fsckDestroy() will close the FILE*
404 * it creates here.
405 * It returns -1 on error, and 0 on success.
406 */
407 int
408 fsckSetFile(fsck_ctx_t c, int f)
409 {
410 struct context *ctx = c;
411
412 if (c != NULL) {
413 FILE *out = fdopen(f, "w");
414
415 if (out != NULL) {
416 ctx->fp = out;
417 ctx->flags |= cfFromFD;
418 return 0;
419 }
420 }
421 return -1;
422 }
423
424 /*
425 * fsckSetVerbosity(context, level)
426 * Sets the verbosity level associated with this context.
427 * This is used to determine which messages are output -- only
428 * messages with a level equal to, or less than, the context's
429 * verbosity level are output.
430 */
431 int
432 fsckSetVerbosity(fsck_ctx_t c, int v)
433 {
434 struct context *ctx = c;
435
436 if (c != NULL) {
437 ctx->verb = v;
438 return 0;
439 }
440 return -1;
441 }
442
443 /*
444 * fsckGetVerbosity(context)
445 * Return the verbosity level previously set, or -1 on error.
446 */
447 int
448 fsckGetVerbosity(fsck_ctx_t c)
449 {
450 struct context *ctx = c;
451
452 return ctx ? ctx->verb : -1;
453 }
454
455 /*
456 * fsckSetOutputStyle(context, output_type)
457 * Set the output style to one of the defined style:
458 * Traditional (normal terminal-output); GUI (the parenthesized
459 * method used previously by DM/DU); and XML (the new plist
460 * format that is the raison d'etre for this code). It does not
461 * (yet) check if the input value is sane.
462 */
463 int
464 fsckSetOutputStyle(fsck_ctx_t c, enum fsck_output_type s)
465 {
466 struct context *ctx = c;
467
468 if (c != NULL) {
469 ctx->style = s;
470 return 0;
471 }
472 return -1;
473 }
474
475 /*
476 * fsckGetStyle(context)
477 * Return the output style set for this context, or
478 * fsckOUtputUndefined.
479 */
480 enum fsck_output_type
481 fsckGetOutputStyle(fsck_ctx_t c)
482 {
483 struct context *ctx = c;
484
485 return ctx ? ctx->style : fsckOutputUndefined;
486 }
487
488 /*
489 * fsckSetDefaultResponse(context, default_answer_tye)
490 * The purpose of this function is to allow fsck to run without
491 * interaction, and have a default answer (yes or no) for any
492 * question that might be presented. See fsckAskPrompt()
493 */
494 int
495 fsckSetDefaultResponse(fsck_ctx_t c, enum fsck_default_answer_type r)
496 {
497 struct context *ctx = c;
498
499 if (ctx) {
500 ctx->resp = r;
501 return 0;
502 }
503 return -1;
504 }
505
506 /*
507 * fsckAskPrompt(context, prompt, ...)
508 * Ask a question of the user, preceded by the given
509 * printf-format prompt. E.g., "CONTINUE? "); the
510 * question mark should be included if you want it
511 * displayed. If a default answer has been set, then
512 * it will be used; otherwise, it will try to get an
513 * answer from the user. Return values are 1 for "yes",
514 * 0 for "no"; -1 for an invalid default; and -2 for error.
515 */
516 int
517 fsckAskPrompt(fsck_ctx_t c, const char *prompt, ...)
518 {
519 struct context *ctx = c;
520 int rv = -2;
521 va_list ap;
522
523 if (ctx == NULL)
524 return -1;
525
526 va_start(ap, prompt);
527
528 if (ctx->style == fsckOutputTraditional && ctx->fp) {
529 int count = 0;
530 doit:
531 printv(ctx, prompt, ap);
532 switch (ctx->resp) {
533 default:
534 rv = -1;
535 break;
536 case fsckDefaultNo:
537 rv = 0;
538 break;
539 case fsckDefaultYes:
540 rv = 1;
541 break;
542 }
543 if (rv == -1) {
544 char *resp = NULL;
545 size_t len;
546
547 count++;
548 resp = fgetln(stdin, &len);
549 if (resp == NULL || len == 0) {
550 if (count > 10) {
551 // Only ask so many times...
552 rv = 0;
553 printargs(ctx, "%s", "\n");
554 goto done;
555 } else {
556 goto doit;
557 }
558 }
559 switch (resp[0]) {
560 case 'y':
561 case 'Y':
562 rv = 1;
563 break;
564 case 'n':
565 case 'N':
566 rv = 0;
567 break;
568 default:
569 goto doit;
570 }
571 } else {
572 printargs(ctx, "%s", rv == 0 ? "NO\n" : "YES\n");
573 }
574 } else {
575 switch (ctx->resp) {
576 default:
577 rv = -1;
578 break;
579 case fsckDefaultNo:
580 rv = 0;
581 break;
582 case fsckDefaultYes:
583 rv = 1;
584 break;
585 }
586 }
587 done:
588 return rv;
589 }
590
591 /*
592 * fsckDestroy(context)
593 * Finish up with a context, and release any resources
594 * it had.
595 */
596 void
597 fsckDestroy(fsck_ctx_t c)
598 {
599 struct context *ctx = c;
600
601 if (c == NULL)
602 return;
603
604 if (ctx->msgs)
605 free(ctx->msgs);
606
607 if (ctx->flags & cfFromFD) {
608 fclose(ctx->fp);
609 }
610 if (ctx->preMessage) {
611 Block_release(ctx->preMessage);
612 }
613 if (ctx->postMessage) {
614 Block_release(ctx->postMessage);
615 }
616
617 free(ctx);
618 return;
619 }
620
621 /*
622 * msgCompar(void*, void*)
623 * Used by fsckAddMessages() for qsort(). All it does is
624 * compare the message number for two fsck_messages.
625 */
626 static int
627 msgCompar(const void *p1, const void *p2)
628 {
629 fsck_message_t *const *k1 = p1, *const *k2 = p2;
630
631 return ((*k1)->msgnum - (*k2)->msgnum);
632 }
633
634 /*
635 * fsckAddMessages(context, message*)
636 * Add a block of messages to this context. We do not assume,
637 * or require, that they are in sorted order. This is probably
638 * not the best it could be, becasue first we look through the
639 * block once, counting how many messages there are; then we
640 * allocate extra space for the existing block, and copy in the
641 * messages to it. This means 2 passes through, which isn't ideal
642 * (however, it should be called very infrequently). After that,
643 * we sort the new block, sorting based on the message number.
644 * In the event of failure, it'll return -1.
645 * XXX We make no attempt to ensure that there are not duplicate
646 * message numbers!
647 */
648 int
649 fsckAddMessages(fsck_ctx_t c, fsck_message_t *m)
650 {
651 struct context *ctx = c;
652 fsck_message_t *ptr, **new;
653 int cnt, i;
654
655 if (ctx == NULL || m == NULL || m->msg == NULL)
656 return 0;
657
658 for (cnt = 0, ptr = m; ptr->msg; ptr++, cnt++)
659 ;
660
661 new = realloc(ctx->msgs, sizeof(fsck_message_t*) * (ctx->num + cnt));
662 if (new == NULL)
663 return -1;
664 ctx->msgs = new;
665
666 for (i = 0; i < cnt; i++) {
667 ctx->msgs[i + ctx->num] = &m[i];
668 }
669 ctx->num += cnt;
670
671 qsort(ctx->msgs, ctx->num, sizeof(fsck_message_t*), msgCompar);
672
673 return 0;
674 }
675
676 /*
677 * bCompar(void *, void *)
678 * An fsck_message_t* comparision function for
679 * bsearch(). The first parameter is a pointer to
680 * the message number we're searching for; the second
681 * parameter is a pointer to an fsck_message_t.
682 * bsearch() needs to know whether that message is less than,
683 * equal to, or greater than the desired one.
684 */
685 static int
686 bCompar(const void *kp, const void *ap)
687 {
688 const int *ip = kp;
689 fsck_message_t * const *mp = ap;
690
691 return (*ip - (*mp)->msgnum);
692 }
693
694 /*
695 * findmessage(context, msgnum)
696 * Find the desired message number in the context. It uses
697 * bsearch() and... does very little itself. (An earlier version
698 * did a lot more.)
699 */
700 static fsck_message_t *
701 findmessage(struct context *ctx, int msgnum)
702 {
703 fsck_message_t **rv;
704
705 if (ctx == NULL)
706 return NULL;
707
708 rv = bsearch(&msgnum, ctx->msgs, ctx->num, sizeof(rv), bCompar);
709
710 if (rv)
711 return *rv;
712 else
713 return NULL;
714 }
715
716 /*
717 * fsckPrintToString(message, va_list)
718 * fsckPrintString(context, message, va_list)
719 * These two functions are used to print out a traditional message on the
720 * console. Note that it outputs "** " for the messages
721 * it does print out (Verify, Repair, Success, and Fail);
722 * other messages are not printed out.
723 *
724 * fsckPrintToString() is also used for message logging.
725 *
726 */
727 static char *
728 fsckPrintToString(fsck_message_t *m, va_list ap)
729 {
730 char *retval = NULL;
731 char *tmpstr = NULL;
732 char *astr = ""; // String at beginning
733 char *pstr = ""; // String at end
734
735 /* No progress messages required in traditional output */
736 if (m->type == fsckMsgProgress) {
737 return NULL;
738 }
739 switch (m->type) {
740 case fsckMsgVerify:
741 case fsckMsgRepair:
742 case fsckMsgSuccess:
743 case fsckMsgFail:
744 astr = "** ";
745 break;
746
747 case fsckMsgError:
748 case fsckMsgDamageInfo:
749 case fsckMsgInfo:
750 astr = " ";
751 break;
752 case fsckMsgNotice:
753 pstr = astr = " *****";
754 break;
755 }
756 vasprintf(&tmpstr, m->msg, ap);
757 if (tmpstr) {
758 asprintf(&retval, "%s%s%s\n", astr, tmpstr, pstr);
759 free(tmpstr);
760 }
761 return retval;
762 }
763
764 static int
765 fsckPrintString(struct context *ctx, fsck_message_t *m, va_list ap)
766 {
767 // Traditional fsck doesn't print this out
768 if (m->type != fsckMsgProgress)
769 {
770 char *str = fsckPrintToString(m, ap);
771 if (str) {
772 printargs(ctx, "%s", str);
773 free(str);
774 }
775 }
776 return 0;
777 }
778
779 /*
780 * fsckPrintXML(context, message, va_list)
781 * Print out a message in XML (well, plist) format.
782 * This involves printint out a standard header and closer
783 * for each message, and calling fflush() when it's done.
784 */
785 static int
786 fsckPrintXML(struct context *ctx, fsck_message_t *m, va_list ap)
787 {
788 char *newmsg = convertfmt(m->msg);
789 /* See convertfmt() for details */
790 if (newmsg == NULL) {
791 return -1;
792 }
793 printargs(ctx, "%s", "<plist version=\"1.0\">\n");
794 printargs(ctx, "%s", "\t<dict>\n");
795 printargs(ctx, "\t\t<key>%s</key> <string>%s</string>\n",
796 kfsckType, typestring(m->type));
797 /*
798 * XXX - should be a "cleaner" way of doing this: we only want
799 * to print out these keys if it's NOT a progress indicator.
800 */
801 if (m->msgnum != fsckProgress) {
802 printargs(ctx, "\t\t<key>%s</key> <integer>%s</integer>\n",
803 kfsckVerbosity, verbosity_string(m->level));
804 printargs(ctx, "\t\t<key>%s</key> <integer>%u</integer>\n",
805 kfsckMsgNumber, m->msgnum);
806 printargs(ctx, "\t\t<key>%s</key> <string>%s</string>\n",
807 kfsckMsgString, newmsg);
808 }
809 if (m->numargs > 0) {
810 int i;
811 /*
812 * Each parameter has a type. This basically boils down to
813 * a string or an integer, but some kinds of strings are
814 * handled specially. Specifically, paths, volume names,
815 * etc.
816 */
817 printargs(ctx, "\t\t<key>%s</key>\n", kfsckParams);
818 printargs(ctx, "%s", "\t\t<array>\n");
819 for (i = 0; i < m->numargs; i++) {
820 if (m->argtype[i] == fsckTypeInt) {
821 int x = va_arg(ap, int);
822 printargs(ctx, "\t\t\t<integer>%d</integer>\n", x);
823 } else if (m->argtype[i] == fsckTypeLong) {
824 long x = va_arg(ap, long);
825 printargs(ctx, "\t\t\t<integer>%ld</integer>\n", x);
826 } else if (m->argtype[i] == fsckTypeFileSize) {
827 off_t x = va_arg(ap, off_t);
828 printargs(ctx, "\t\t\t<integer>%llu</integer>\n", x);
829 } else if (m->argtype[i] == fsckTypeString) {
830 char *p = va_arg(ap, char*);
831 printargs(ctx, "\t\t\t<string>%s</string>\n", p);
832 } else if (m->argtype[i] == fsckTypePath) {
833 char *p = va_arg(ap, char*);
834 printargs(ctx, "\t\t\t<dict><key>%s</key> <string>%s</string></dict>\n", kfsckParamPathKey, p);
835 } else if (m->argtype[i] == fsckTypeFile) {
836 char *p = va_arg(ap, char*);
837 printargs(ctx, "\t\t\t<dict><key>%s</key> <string>%s</string></dict>\n", kfsckParamFileKey, p);
838 } else if (m->argtype[i] == fsckTypeDirectory) {
839 char *p = va_arg(ap, char*);
840 printargs(ctx, "\t\t\t<dict><key>%s</key> <string>%s</string></dict>\n", kfsckParamDirectoryKey, p);
841 } else if (m->argtype[i] == fsckTypeVolume) {
842 char *p = va_arg(ap, char*);
843 printargs(ctx, "\t\t\t<dict><key>%s</key> <string>%s</string></dict>\n", kfsckParamVolumeKey, p);
844 } else if (m->argtype[i] == fsckTypeFSType) {
845 char *p = va_arg(ap, char*);
846 printargs(ctx, "\t\t\t<dict><key>%s</key> <string>%s</string></dict>\n", kfsckParamFSTypeKey, p);
847 } else if (m->argtype[i] == fsckTypeProgress) {
848 int x = va_arg(ap, int);
849 printargs(ctx, "\t\t\t<integer>%d</integer>\n", x);
850 } else {
851 /* XXX - what should default be --- string, integer, pointer? */
852 void *p = va_arg(ap, void*);
853 printargs(ctx, "\t\t\t<integer>%p</integer>\n", p);
854 }
855 }
856 printargs(ctx, "%s", "\t\t</array>\n");
857 }
858 printargs(ctx, "%s", "\t</dict>\n");
859 printargs(ctx, "%s", "</plist>\n");
860 free(newmsg);
861 return 0;
862 }
863
864 /*
865 * fsckPrintGUI(context, message, va_list)
866 * Print out a message for the previous interface for DM/DU;
867 * this looks like:
868 * ('X', "message", z)
869 * where 'X' is a type ('S' for success, 'E' for error, and
870 * '%' for progress), and z is an argument count. (Okay,
871 * progress counts are just "(% z)", where "z" is a number
872 * between 0 and 100). If there are any arguments, they follow
873 * one per line.
874 */
875 static int
876 fsckPrintGUI(struct context *ctx, fsck_message_t *m, va_list ap)
877 {
878 char t;
879 int i;
880 char *newmsg = convertfmt(m->msg);
881 if (newmsg == NULL)
882 return -1;
883
884 switch (m->type) {
885 case fsckMsgVerify:
886 case fsckMsgInfo:
887 case fsckMsgRepair:
888 case fsckMsgSuccess:
889 case fsckMsgNotice:
890 t = 'S'; break;
891 case fsckMsgError:
892 case fsckMsgFail:
893 case fsckMsgDamageInfo:
894 t = 'E'; break;
895 case fsckMsgProgress:
896 t = '%'; break;
897 default:
898 t = '?'; break;
899 }
900 if (m->msgnum != fsckProgress) {
901 printargs(ctx, "(%c,\"%s\",%d)\n", t, newmsg, m->numargs);
902 }
903 for (i = 0; i < m->numargs; i++) {
904 switch (m->argtype[i]) {
905 case fsckTypeInt:
906 printargs(ctx, "%d\n", (int)va_arg(ap, int)); break;
907 case fsckTypeLong:
908 printargs(ctx, "%ld\n", (long)va_arg(ap, long)); break;
909 case fsckTypeFileSize:
910 printargs(ctx, "%llu\n", (off_t)va_arg(ap, off_t)); break;
911 case fsckTypeProgress:
912 printargs(ctx, "(%d %%)\n", (int)va_arg(ap, int)); break;
913 case fsckTypeString:
914 case fsckTypePath:
915 case fsckTypeFile:
916 case fsckTypeDirectory:
917 case fsckTypeVolume:
918 case fsckTypeFSType:
919 printargs(ctx, "%s\n", (char*)va_arg(ap, char*)); break;
920 default:
921 printargs(ctx, "%p\n", (void*)va_arg(ap, void*)); break;
922 }
923 }
924 free(newmsg);
925 return 0;
926 }
927
928 /*
929 * fsckPrintNothing(context, message, va_list)
930 * Don't actually print anything. Used for testing and debugging, nothing
931 * else.
932 */
933 static int
934 fsckPrintNothing(struct context *ctx, fsck_message_t *m, va_list ap)
935 {
936 return -1;
937 }
938
939 /*
940 * fsckPrint(context, msgnum, ...)
941 * Print out a message identified by msgnum, using the data and
942 * context information in the contexxt. This will look up the message,
943 * and then print it out to the requested output stream using the style
944 * that was selected. It returns 0 on success, and -1 on failure.
945 *
946 * Note: WriteError() and RcdError() call fsckPrint internally, and
947 * therefore take care of generating the output correctly.
948 */
949 int
950 fsckPrint(fsck_ctx_t c, int m, ...)
951 {
952 int (*func)(struct context *, fsck_message_t *, va_list);
953 struct context *ctx = c;
954 fsck_message_t *msg;
955 va_list ap;
956 int retval = 0;
957
958 va_start(ap, m);
959
960 if (c == NULL)
961 return -1;
962
963 msg = findmessage(ctx, m);
964 assert(msg != NULL);
965 if (msg == NULL) {
966 return -1; // Should log something
967 }
968
969 switch (ctx->style) {
970 case fsckOutputTraditional:
971 func = fsckPrintString;
972 break;
973 case fsckOutputGUI:
974 func = fsckPrintGUI;
975 break;
976 case fsckOutputXML:
977 func = fsckPrintXML;
978 break;
979 default:
980 func = fsckPrintNothing;
981 break;
982 }
983
984 if (ctx->preMessage) {
985 va_list vaBlock;
986 fsck_block_status_t rv;
987
988 va_copy(vaBlock, ap);
989 rv = (ctx->preMessage)(c, m, vaBlock);
990 if (rv == fsckBlockAbort) {
991 retval = -1;
992 goto done;
993 }
994 if (rv == fsckBlockIgnore) {
995 retval = 0;
996 goto done;
997 }
998 }
999
1000 // Write string in traditional form to log file first
1001 ctx->writeToLog = 1;
1002 va_list logfile_ap;
1003 va_copy(logfile_ap, ap);
1004 retval = fsckPrintString(ctx, msg, logfile_ap);
1005 ctx->writeToLog = 0;
1006
1007 if (ctx->writer) {
1008 // Now write string to standard output now as per caller's specifications
1009 retval = (*func)(ctx, msg, ap);
1010 } else {
1011 retval = 0; // NULL fp means don't output anything
1012 }
1013 if (ctx->postMessage) {
1014 va_list vaBlock;
1015 fsck_block_status_t rv;
1016
1017 va_copy(vaBlock, ap);
1018 rv = (ctx->postMessage)(c, m, vaBlock);
1019 if (rv == fsckBlockAbort) {
1020 retval = -1;
1021 goto done;
1022 }
1023 if (rv == fsckBlockIgnore) {
1024 retval = 0;
1025 goto done;
1026 }
1027 }
1028
1029 done:
1030 return retval;
1031 }
1032
1033 /*
1034 * fsckMsgClass(context, msgnum)
1035 * Return the message class (Verify, Successs, Failure, etc.)
1036 * for a given message number. If the message number is unknown,
1037 * it returns fsckMsgUnknown.
1038 */
1039 enum fsck_msgtype
1040 fsckMsgClass(fsck_ctx_t c, int msgNum)
1041 {
1042 struct context *ctx = c;
1043 fsck_message_t *m;
1044
1045 if (c == NULL)
1046 return fsckMsgUnknown;
1047
1048 m = findmessage(ctx, msgNum);
1049 if (m == NULL)
1050 return fsckMsgUnknown;
1051
1052 return m->type;
1053 }
1054
1055 /*
1056 * The following section is used to make the internationalizable
1057 * string file; this is a file that contains each message string,
1058 * followed by an '=' and then the string again. This is then doctored
1059 * by the internationalization folks. By putting it in here, this means
1060 * we need to compile the source file (and any others that have the messages
1061 * we care about) specially, and then be run as part of the build process.
1062 */
1063 #ifdef FSCK_MAKESTRINGS
1064 int
1065 main(int ac, char **av)
1066 {
1067 fsck_message_t *msg;
1068 extern fsck_message_t hfs_errors[];
1069 extern fsck_message_t hfs_messages[];
1070
1071 printf("/* Standard messages */\n");
1072 for (msg = fsck_messages_common;
1073 msg->msg != NULL;
1074 msg++) {
1075 char *newstr = convertfmt(msg->msg);
1076
1077 if (newstr == NULL) {
1078 printf("\"%s\" = \"%s\";\n", msg->msg, msg->msg);
1079 } else {
1080 printf("\"%s\" = \"%s\";\n", newstr, newstr);
1081 free(newstr);
1082 }
1083 }
1084
1085 printf("\n/* HFS-specific standard messages */\n");
1086 for (msg = hfs_messages;
1087 msg->msg != NULL;
1088 msg++) {
1089 char *newstr = convertfmt(msg->msg);
1090
1091 if (newstr == NULL) {
1092 printf("\"%s\" = \"%s\";\n", msg->msg, msg->msg);
1093 } else {
1094 printf("\"%s\" = \"%s\";\n", newstr, newstr);
1095 free(newstr);
1096 }
1097 }
1098
1099 printf("\n/* HFS-specific errors */\n");
1100 for (msg = hfs_errors;
1101 msg->msg != NULL;
1102 msg++) {
1103 char *newstr = convertfmt(msg->msg);
1104
1105 if (newstr == NULL) {
1106 printf("\"%s\" = \"%s\";\n", msg->msg, msg->msg);
1107 } else {
1108 printf("\"%s\" = \"%s\";\n", newstr, newstr);
1109 free(newstr);
1110 }
1111 }
1112
1113 return 0;
1114 }
1115 #endif /* FSCK_MAKESTRINGS */
1116
1117 /*
1118 * This is used only for testing; it'll take some dumb arguments on
1119 * the command line, and then print out some messages. It tests the
1120 * allocation, initialization, and searching.
1121 */
1122 #ifdef FSCK_TEST
1123 main(int ac, char **av)
1124 {
1125 fsck_ctx_t fctx;
1126 enum fsck_output_type t = fsckOutputUndefined;
1127 int (*func)(fsck_ctx_t, int, ...);
1128 int i;
1129
1130 fctx = fsckCreate();
1131
1132 if (ac == 2) {
1133 if (!strcmp(av[1], "-g")) {
1134 t = fsckOutputGUI;
1135 fsckSetStyle(fctx, t);
1136 fsckSetDefaultResponse(fctx, fsckDefaultYes);
1137 } else if (!strcmp(av[1], "-s")) {
1138 t = fsckOutputTraditional;
1139 fsckSetStyle(fctx, t);
1140 } else if (!strcmp(av[1], "-x")) {
1141 t = fsckOutputXML;
1142 fsckSetStyle(fctx, t);
1143 fsckSetDefaultResponse(fctx, fsckDefaultYes);
1144 }
1145 }
1146
1147 fsckSetOutput(fctx, stdout);
1148 fsckPrint(fctx, fsckInformation, "fsck", "version");
1149
1150 i = fsckAskPrompt(fctx, "Unknown file %s; remove? [y|n] ", "/tmp/foo");
1151 if (i == 1) {
1152 fprintf(stderr, "\n\nfile %s is to be removed\n\n", "/tmp/foo");
1153 }
1154 fsckPrint(fctx, fsckProgress, 10);
1155 fsckPrint(fctx, fsckVolumeNotRepaired);
1156
1157 fsckDestroy(fctx);
1158
1159 return 0;
1160 }
1161
1162 #endif /* FSCK_TEST */