]>
Commit | Line | Data |
---|---|---|
51e135ce A |
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 */ |