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