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