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 37extern fsck_message_t fsck_messages_common[]; 38 39// The following structures are used internally, only 40struct 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 55struct 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 */ 79static void 80printv(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 */ 131static void 132printargs(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 146static void 147stdprint(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 */ 162static const char * 163typestring(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 */ 195static const char * 196verbosity_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 */ 218static char * 219convertfmt(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 */ 271fsck_ctx_t 272fsckCreate(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 */ 295void 296fsckSetBlock(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 */ 331fsckBlock_t 332fsckGetBlock(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 */ 356int 357fsckSetWriter(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 */ 369int 370fsckSetLogger(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 */ 386int 387fsckSetOutput(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 */ 407int 408fsckSetFile(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 */ 431int 432fsckSetVerbosity(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 */ 447int 448fsckGetVerbosity(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 */ 463int 464fsckSetOutputStyle(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 */ 480enum fsck_output_type 481fsckGetOutputStyle(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 */ 494int 495fsckSetDefaultResponse(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 */ 516int 517fsckAskPrompt(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; 530doit: 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 } 587done: 588 return rv; 589} 590 591/* 592 * fsckDestroy(context) 593 * Finish up with a context, and release any resources 594 * it had. 595 */ 596void 597fsckDestroy(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 */ 626static int 627msgCompar(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 */ 648int 649fsckAddMessages(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 */ 685static int 686bCompar(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 */ 700static fsck_message_t * 701findmessage(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 */ 727static char * 728fsckPrintToString(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 764static int 765fsckPrintString(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 */ 785static int 786fsckPrintXML(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 */ 875static int 876fsckPrintGUI(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 */ 933static int 934fsckPrintNothing(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 */ 949int 950fsckPrint(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 1029done: 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 */ 1039enum fsck_msgtype 1040fsckMsgClass(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 1064int 1065main(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 1123main(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 */ 1163