#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <stdarg.h>
#include <string.h>
#include <assert.h>
#include <Block.h>
#include "fsck_messages.h"
#include "fsck_keys.h"
#include "fsck_msgnums.h"
extern fsck_message_t fsck_messages_common[];
struct messages {
int low;
int high;
fsck_message_t *msgs;
struct messages *next, *prev;
};
#define cfFromFD 0x01
struct context {
FILE *fp; int flags; int verb; enum fsck_output_type style;
enum fsck_default_answer_type resp; int num; fsck_message_t **msgs;
void (*writer)(fsck_ctx_t, const char*); char guiControl;
char xmlControl;
fsckBlock_t preMessage;
fsckBlock_t postMessage;
};
static void
printv(fsck_ctx_t c, const char *fmt, va_list ap)
{
struct context *ctx = (struct context *)c;
char buf[BUFSIZ + 1];
size_t length;
va_list ap2;
if (c == NULL)
return;
__va_copy(ap2, ap); length = vsnprintf(buf, BUFSIZ, fmt, ap);
if (length > BUFSIZ) {
size_t l2 = length + 1;
char *bufp = malloc(l2);
if (bufp == NULL) {
strcpy(buf, "* * * cannot allocate memory * * *\n");
bufp = buf;
} else {
length = vsnprintf(bufp, length, fmt, ap2);
if (length >= l2) { strcpy(buf, " * * * cannot allocate memory * * *\n");
free(bufp);
bufp = buf;
} else {
if (ctx->writer) (ctx->writer)(ctx, bufp);
free(bufp);
bufp = NULL;
}
}
if (bufp == NULL)
return;
}
if (ctx->writer) (ctx->writer)(ctx, buf);
return;
}
static void
printargs(fsck_ctx_t c, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
printv(c, fmt, ap);
}
static void
stdprint(fsck_ctx_t c, const char *str)
{
struct context *ctx = (struct context*)c;
if (c) {
fputs(str, ctx->fp ? ctx->fp : stdout);
fflush(ctx->fp ? ctx->fp : stdout);
}
}
static const char *
typestring(int type)
{
switch (type) {
case fsckMsgVerify:
return kfsckVerify;
case fsckMsgInfo:
return kfsckInformation;
case fsckMsgRepair:
return kfsckRepair;
case fsckMsgSuccess:
return kfsckSuccess;
case fsckMsgError:
return kfsckError;
case fsckMsgFail:
return kfsckFail;
case fsckMsgDamageInfo:
return kfsckDamageinfo;
case fsckMsgProgress:
return kfsckProgress;
default:
return kfsckUnknown;
}
}
static const char *
verbosity_string(int level)
{
switch(level) {
case fsckLevel0:
return kfsckLevel0;
case fsckLevel1:
default:
return kfsckLevel1;
}
}
static char *
convertfmt(const char *in)
{
char *retval = NULL;
int numargs = 0;
char *cp;
enum { fNone, fPercent } fs;
for (cp = (char*)in; cp; cp = strchr(cp, '%')) {
numargs++;
cp++;
}
retval = calloc(1, strlen(in) + numargs * 5 + 1);
if (retval == NULL)
return NULL;
fs = fNone;
numargs = 0;
for (cp = retval; *in; in++) {
if (fs == fNone) {
*cp++ = *in;
if (*in == '%') {
if (in[1] == '%') {
*cp++ = '%';
in++;
} else {
fs = fPercent;
cp += sprintf(cp, "%d$@", ++numargs);
}
}
} else if (fs == fPercent) {
switch (*in) {
case 'd': case 'i': case 'o': case 'u': case 'x':
case 'X': case 'D': case 'O': case 'U': case 'e':
case 'E': case 'f': case 'F': case 'g': case 'G':
case 'a': case 'A': case 'c': case 'C': case 's':
case 'S': case 'p': case 'n':
fs = fNone;
break;
}
}
}
*cp = 0;
return retval;
}
fsck_ctx_t
fsckCreate(void)
{
struct context *rv = NULL;
rv = calloc(1, sizeof(*rv));
if (rv == NULL) {
return NULL;
}
if (fsckAddMessages(rv, fsck_messages_common) == -1) {
fsckDestroy(rv);
return NULL;
}
fsckSetWriter(rv, &stdprint);
return (fsck_ctx_t)rv;
}
void
fsckSetBlock(fsck_ctx_t c, fsck_block_phase_t phase, fsckBlock_t bp)
{
struct context *ctx = c;
if (c != NULL) {
switch (phase) {
case fsckPhaseBeforeMessage:
if (ctx->preMessage) {
Block_release(ctx->preMessage);
ctx->preMessage = NULL;
}
if (bp)
ctx->preMessage = (fsckBlock_t)Block_copy(bp);
break;
case fsckPhaseAfterMessage:
if (ctx->postMessage) {
Block_release(ctx->postMessage);
ctx->postMessage = NULL;
}
if (bp)
ctx->postMessage = (fsckBlock_t)Block_copy(bp);
break;
case fsckPhaseNone:
break;
}
}
return;
}
fsckBlock_t
fsckGetBlock(fsck_ctx_t c, fsck_block_phase_t phase)
{
struct context *ctx = c;
fsckBlock_t retval = NULL;
if (c != NULL) {
switch (phase) {
case fsckPhaseBeforeMessage:
retval = ctx->preMessage;
break;
case fsckPhaseAfterMessage:
retval = ctx->postMessage;
break;
case fsckPhaseNone:
break;
}
}
return retval;
}
int
fsckSetWriter(fsck_ctx_t c, void (*fp)(fsck_ctx_t, const char*))
{
struct context *ctx = c;
if (c != NULL) {
ctx->writer = fp;
return 0;
} else {
return -1;
}
}
int
fsckSetOutput(fsck_ctx_t c, FILE *fp)
{
struct context *ctx = c;
if (c != NULL) {
ctx->fp = fp;
return 0;
} else
return -1;
}
int
fsckSetFile(fsck_ctx_t c, int f)
{
struct context *ctx = c;
if (c != NULL) {
FILE *out = fdopen(f, "w");
if (out != NULL) {
ctx->fp = out;
ctx->flags |= cfFromFD;
return 0;
}
}
return -1;
}
int
fsckSetVerbosity(fsck_ctx_t c, int v)
{
struct context *ctx = c;
if (c != NULL) {
ctx->verb = v;
return 0;
}
return -1;
}
int
fsckGetVerbosity(fsck_ctx_t c)
{
struct context *ctx = c;
return ctx ? ctx->verb : -1;
}
int
fsckSetOutputStyle(fsck_ctx_t c, enum fsck_output_type s)
{
struct context *ctx = c;
if (c != NULL) {
ctx->style = s;
return 0;
}
return -1;
}
enum fsck_output_type
fsckGetOutputStyle(fsck_ctx_t c)
{
struct context *ctx = c;
return ctx ? ctx->style : fsckOutputUndefined;
}
int
fsckSetDefaultResponse(fsck_ctx_t c, enum fsck_default_answer_type r)
{
struct context *ctx = c;
if (ctx) {
ctx->resp = r;
return 0;
}
return -1;
}
int
fsckAskPrompt(fsck_ctx_t c, const char *prompt, ...)
{
struct context *ctx = c;
int rv = -2;
va_list ap;
if (ctx == NULL)
return -1;
va_start(ap, prompt);
if (ctx->style == fsckOutputTraditional && ctx->fp) {
int count = 0;
doit:
printv(ctx, prompt, ap);
switch (ctx->resp) {
default:
rv = -1;
break;
case fsckDefaultNo:
rv = 0;
break;
case fsckDefaultYes:
rv = 1;
break;
}
if (rv == -1) {
char *resp = NULL;
size_t len;
count++;
resp = fgetln(stdin, &len);
if (resp == NULL || len == 0) {
if (count > 10) {
rv = 0;
printargs(ctx, "\n");
goto done;
} else {
goto doit;
}
}
switch (resp[0]) {
case 'y':
case 'Y':
rv = 1;
break;
case 'n':
case 'N':
rv = 0;
break;
default:
goto doit;
}
} else {
printargs(ctx, rv == 0 ? "NO\n" : "YES\n");
}
} else {
switch (ctx->resp) {
default:
rv = -1;
break;
case fsckDefaultNo:
rv = 0;
break;
case fsckDefaultYes:
rv = 1;
break;
}
}
done:
return rv;
}
void
fsckDestroy(fsck_ctx_t c)
{
struct context *ctx = c;
if (c == NULL)
return;
if (ctx->msgs)
free(ctx->msgs);
if (ctx->flags & cfFromFD) {
fclose(ctx->fp);
}
if (ctx->preMessage) {
Block_release(ctx->preMessage);
}
if (ctx->postMessage) {
Block_release(ctx->postMessage);
}
free(ctx);
return;
}
static int
msgCompar(const void *p1, const void *p2)
{
fsck_message_t *const *k1 = p1, *const *k2 = p2;
return ((*k1)->msgnum - (*k2)->msgnum);
}
int
fsckAddMessages(fsck_ctx_t c, fsck_message_t *m)
{
struct context *ctx = c;
fsck_message_t *ptr, **new;
int cnt, i;
if (ctx == NULL || m == NULL || m->msg == NULL)
return 0;
for (cnt = 0, ptr = m; ptr->msg; ptr++, cnt++)
;
new = realloc(ctx->msgs, sizeof(fsck_message_t*) * (ctx->num + cnt));
if (new == NULL)
return -1;
ctx->msgs = new;
for (i = 0; i < cnt; i++) {
ctx->msgs[i + ctx->num] = &m[i];
}
ctx->num += cnt;
qsort(ctx->msgs, ctx->num, sizeof(fsck_message_t*), msgCompar);
return 0;
}
static int
bCompar(const void *kp, const void *ap)
{
const int *ip = kp;
fsck_message_t * const *mp = ap;
return (*ip - (*mp)->msgnum);
}
static fsck_message_t *
findmessage(struct context *ctx, int msgnum)
{
fsck_message_t **rv;
if (ctx == NULL)
return NULL;
rv = bsearch(&msgnum, ctx->msgs, ctx->num, sizeof(rv), bCompar);
if (rv)
return *rv;
else
return NULL;
}
static char *
fsckPrintToString(fsck_message_t *m, va_list ap)
{
char *retval = NULL;
char *tmpstr = NULL;
char *astr = "";
if (m->type == fsckMsgProgress) {
return NULL;
}
switch (m->type) {
case fsckMsgVerify:
case fsckMsgRepair:
case fsckMsgSuccess:
case fsckMsgFail:
astr = "** ";
break;
case fsckMsgError:
case fsckMsgDamageInfo:
case fsckMsgInfo:
astr = " ";
break;
}
vasprintf(&tmpstr, m->msg, ap);
if (tmpstr) {
asprintf(&retval, "%s%s\n", astr, tmpstr);
free(tmpstr);
}
return retval;
}
static int
fsckPrintString(struct context *ctx, fsck_message_t *m, va_list ap)
{
if (m->type != fsckMsgProgress)
{
char *str = fsckPrintToString(m, ap);
if (str) {
printargs(ctx, str);
free(str);
}
}
return 0;
}
static int
fsckPrintXML(struct context *ctx, fsck_message_t *m, va_list ap)
{
char *newmsg = convertfmt(m->msg);
if (newmsg == NULL) {
return -1;
}
printargs(ctx, "<plist version=\"1.0\">\n");
printargs(ctx, "\t<dict>\n");
printargs(ctx, "\t\t<key>%s</key> <string>%s</string>\n",
kfsckType, typestring(m->type));
if (m->msgnum != fsckProgress) {
printargs(ctx, "\t\t<key>%s</key> <integer>%s</integer>\n",
kfsckVerbosity, verbosity_string(m->level));
printargs(ctx, "\t\t<key>%s</key> <integer>%u</integer>\n",
kfsckMsgNumber, m->msgnum);
printargs(ctx, "\t\t<key>%s</key> <string>%s</string>\n",
kfsckMsgString, newmsg);
}
if (m->numargs > 0) {
int i;
printargs(ctx, "\t\t<key>%s</key>\n", kfsckParams);
printargs(ctx, "\t\t<array>\n");
for (i = 0; i < m->numargs; i++) {
if (m->argtype[i] == fsckTypeInt) {
int x = va_arg(ap, int);
printargs(ctx, "\t\t\t<integer>%d</integer>\n", x);
} else if (m->argtype[i] == fsckTypeLong) {
long x = va_arg(ap, long);
printargs(ctx, "\t\t\t<integer>%ld</integer>\n", x);
} else if (m->argtype[i] == fsckTypeString) {
char *p = va_arg(ap, char*);
printargs(ctx, "\t\t\t<string>%s</string>\n", p);
} else if (m->argtype[i] == fsckTypePath) {
char *p = va_arg(ap, char*);
printargs(ctx, "\t\t\t<dict><key>%s</key> <string>%s</string></dict>\n", kfsckParamPathKey, p);
} else if (m->argtype[i] == fsckTypeFile) {
char *p = va_arg(ap, char*);
printargs(ctx, "\t\t\t<dict><key>%s</key> <string>%s</string></dict>\n", kfsckParamFileKey, p);
} else if (m->argtype[i] == fsckTypeDirectory) {
char *p = va_arg(ap, char*);
printargs(ctx, "\t\t\t<dict><key>%s</key> <string>%s</string></dict>\n", kfsckParamDirectoryKey, p);
} else if (m->argtype[i] == fsckTypeVolume) {
char *p = va_arg(ap, char*);
printargs(ctx, "\t\t\t<dict><key>%s</key> <string>%s</string></dict>\n", kfsckParamVolumeKey, p);
} else if (m->argtype[i] == fsckTypeFSType) {
char *p = va_arg(ap, char*);
printargs(ctx, "\t\t\t<dict><key>%s</key> <string>%s</string></dict>\n", kfsckParamFSTypeKey, p);
} else if (m->argtype[i] == fsckTypeProgress) {
int x = va_arg(ap, int);
printargs(ctx, "\t\t\t<integer>%d</integer>\n", x);
} else {
void *p = va_arg(ap, void*);
printargs(ctx, "\t\t\t<integer>%p</integer>\n", p);
}
}
printargs(ctx, "\t\t</array>\n");
}
printargs(ctx, "\t</dict>\n");
printargs(ctx, "</plist>\n");
free(newmsg);
return 0;
}
static int
fsckPrintGUI(struct context *ctx, fsck_message_t *m, va_list ap)
{
char t;
int i;
char *newmsg = convertfmt(m->msg);
if (newmsg == NULL)
return -1;
switch (m->type) {
case fsckMsgVerify:
case fsckMsgInfo:
case fsckMsgRepair:
case fsckMsgSuccess:
t = 'S'; break;
case fsckMsgError:
case fsckMsgFail:
case fsckMsgDamageInfo:
t = 'E'; break;
case fsckMsgProgress:
t = '%'; break;
default:
t = '?'; break;
}
if (m->msgnum != fsckProgress) {
printargs(ctx, "(%c,\"%s\",%d)\n", t, newmsg, m->numargs);
}
for (i = 0; i < m->numargs; i++) {
switch (m->argtype[i]) {
case fsckTypeInt:
printargs(ctx, "%d\n", (int)va_arg(ap, int)); break;
case fsckTypeLong:
printargs(ctx, "%ld\n", (long)va_arg(ap, long)); break;
case fsckTypeProgress:
printargs(ctx, "(%d %%)\n", (int)va_arg(ap, int)); break;
case fsckTypeString:
case fsckTypePath:
case fsckTypeFile:
case fsckTypeDirectory:
case fsckTypeVolume:
case fsckTypeFSType:
printargs(ctx, "%s\n", (char*)va_arg(ap, char*)); break;
default:
printargs(ctx, "%p\n", (void*)va_arg(ap, void*)); break;
}
}
free(newmsg);
return 0;
}
static int
fsckPrintNothing(struct context *ctx, fsck_message_t *m, va_list ap)
{
return -1;
}
int
fsckPrint(fsck_ctx_t c, int m, ...)
{
int (*func)(struct context *, fsck_message_t *, va_list);
struct context *ctx = c;
fsck_message_t *msg;
va_list ap;
int retval = 0;
va_start(ap, m);
if (c == NULL)
return -1;
msg = findmessage(ctx, m);
assert(msg != NULL);
if (msg == NULL) {
return -1; }
switch (ctx->style) {
case fsckOutputTraditional:
func = fsckPrintString;
break;
case fsckOutputGUI:
func = fsckPrintGUI;
break;
case fsckOutputXML:
func = fsckPrintXML;
break;
default:
func = fsckPrintNothing;
break;
}
if (ctx->preMessage) {
va_list vaBlock;
fsck_block_status_t rv;
va_copy(vaBlock, ap);
rv = (ctx->preMessage)(c, m, vaBlock);
if (rv == fsckBlockAbort) {
retval = -1;
goto done;
}
if (rv == fsckBlockIgnore) {
retval = 0;
goto done;
}
}
if (ctx->writer) {
retval = (*func)(ctx, msg, ap);
} else {
retval = 0; }
if (ctx->postMessage) {
va_list vaBlock;
fsck_block_status_t rv;
va_copy(vaBlock, ap);
rv = (ctx->postMessage)(c, m, vaBlock);
if (rv == fsckBlockAbort) {
retval = -1;
goto done;
}
if (rv == fsckBlockIgnore) {
retval = 0;
goto done;
}
}
#if 0
if (ctx->logfp) {
char *str;
va_start(ap, m);
str = fsckPrintToString(msg, ap);
if (str) {
(*ctx->logfp)(str);
free(str);
}
}
#endif
done:
return retval;
}
enum fsck_msgtype
fsckMsgClass(fsck_ctx_t c, int msgNum)
{
struct context *ctx = c;
fsck_message_t *m;
if (c == NULL)
return fsckMsgUnknown;
m = findmessage(ctx, msgNum);
if (m == NULL)
return fsckMsgUnknown;
return m->type;
}
#ifdef FSCK_MAKESTRINGS
int
main(int ac, char **av)
{
fsck_message_t *msg;
extern fsck_message_t hfs_errors[];
extern fsck_message_t hfs_messages[];
printf("/* Standard messages */\n");
for (msg = fsck_messages_common;
msg->msg != NULL;
msg++) {
char *newstr = convertfmt(msg->msg);
if (newstr == NULL) {
printf("\"%s\" = \"%s\";\n", msg->msg, msg->msg);
} else {
printf("\"%s\" = \"%s\";\n", newstr, newstr);
free(newstr);
}
}
printf("\n/* HFS-specific standard messages */\n");
for (msg = hfs_messages;
msg->msg != NULL;
msg++) {
char *newstr = convertfmt(msg->msg);
if (newstr == NULL) {
printf("\"%s\" = \"%s\";\n", msg->msg, msg->msg);
} else {
printf("\"%s\" = \"%s\";\n", newstr, newstr);
free(newstr);
}
}
printf("\n/* HFS-specific errors */\n");
for (msg = hfs_errors;
msg->msg != NULL;
msg++) {
char *newstr = convertfmt(msg->msg);
if (newstr == NULL) {
printf("\"%s\" = \"%s\";\n", msg->msg, msg->msg);
} else {
printf("\"%s\" = \"%s\";\n", newstr, newstr);
free(newstr);
}
}
return 0;
}
#endif
#ifdef FSCK_TEST
main(int ac, char **av)
{
fsck_ctx_t fctx;
enum fsck_output_type t = fsckOutputUndefined;
int (*func)(fsck_ctx_t, int, ...);
int i;
fctx = fsckCreate();
if (ac == 2) {
if (!strcmp(av[1], "-g")) {
t = fsckOutputGUI;
fsckSetStyle(fctx, t);
fsckSetDefaultResponse(fctx, fsckDefaultYes);
} else if (!strcmp(av[1], "-s")) {
t = fsckOutputTraditional;
fsckSetStyle(fctx, t);
} else if (!strcmp(av[1], "-x")) {
t = fsckOutputXML;
fsckSetStyle(fctx, t);
fsckSetDefaultResponse(fctx, fsckDefaultYes);
}
}
fsckSetOutput(fctx, stdout);
fsckPrint(fctx, fsckInformation, "fsck", "version");
i = fsckAskPrompt(fctx, "Unknown file %s; remove? [y|n] ", "/tmp/foo");
if (i == 1) {
fprintf(stderr, "\n\nfile %s is to be removed\n\n", "/tmp/foo");
}
fsckPrint(fctx, fsckProgress, 10);
fsckPrint(fctx, fsckVolumeNotRepaired);
fsckDestroy(fctx);
return 0;
}
#endif