int mvfprintf( FILE * fp, const char * fmt, va_list ap ) { #ifndef VSNPRINTF_PERCENT_M char nfmt[256]; #else const char * nfmt = fmt; #endif int errval; /* * Save the error value as soon as possible */ #ifdef SYS_WINNT errval = GetLastError(); if (NO_ERROR == errval) #endif /* SYS_WINNT */ errval = errno; #ifndef VSNPRINTF_PERCENT_M format_errmsg(nfmt, sizeof(nfmt), fmt, errval); #else errno = errval; #endif return vfprintf(fp, nfmt, ap); }
void test_format_errmsgHangingPercent(void) { #ifndef VSNPRINTF_PERCENT_M static char fmt[] = "percent then nul term then non-nul %\0oops!"; char act_buf[64]; ZERO(act_buf); format_errmsg(act_buf, sizeof(act_buf), fmt, ENOENT); TEST_ASSERT_EQUAL_STRING(fmt, act_buf); TEST_ASSERT_EQUAL_STRING("", act_buf + 1 + strlen(act_buf)); #else TEST_IGNORE_MESSAGE("VSNPRINTF_PERCENT_M is defined") #endif }
/* the actual string format parser * Although it's not in the documentation of `printf()`, but in addition to the * `%d` conversion specifier, this supports `%i`, which takes an `int` argument * instead of a `long`. It is used only for formatting error messages (since * Sparkling integers are all `long`s), but feel free to use it yourself. * * if `errmsg' is not a NULL pointer, and an error occurred while creating the * format string, then on return, `*errmsg' will point to a string containing * a message that describes the error. */ static char *make_format_string( const char *fmt, size_t *len, int argc, void *argv, int isval, char **errmsg ) { struct string_builder bld; int argidx = 0; const char *s = fmt; const char *p = s; /* points to the beginning of the next * non-format part of the format string */ init_builder(&bld); while (*s) { if (*s == '%') { struct format_args args; init_format_args(&args); /* append preceding non-format string chunk */ if (s > p) { append_string(&bld, p, s - p); } s++; /* Actually parse the format string. * '#' flag: prepend base prefix (0b, 0, 0x) */ if (*s == '#') { args.flags |= FLAG_BASEPREFIX; s++; } /* ' ' (space) flag: prepend space if non-negative * '+' flag: always prepend explicit + or - sign */ if (*s == ' ') { args.flags |= FLAG_PADSIGN; s++; } else if (*s == '+') { args.flags |= FLAG_EXPLICITSIGN; s++; } /* leading 0 flag: pad field with zeroes */ if (*s == '0') { args.flags |= FLAG_ZEROPAD; s++; } /* field width specifier */ if (isdigit(*s)) { args.width = 0; while (isdigit(*s)) { args.width *= 10; args.width += *s++ - '0'; } } else if (*s == '*') { s++; if (isval) { SpnValue *widthptr; /* check argc if the caller wants us to do so */ if (argc >= 0 && argidx >= argc) { format_errmsg(errmsg, OUT_OF_ARGUMENTS, argidx); free(bld.buf); return NULL; } /* width specifier must be an integer */ widthptr = getarg_val(argv, &argidx); if (!isnum(widthptr)) { format_errmsg( errmsg, TYPE_MISMATCH, argidx, SPN_TTAG_NUMBER, widthptr->type ); free(bld.buf); return NULL; } if (isfloat(widthptr)) { format_errmsg( errmsg, EXPECT_INTEGER, argidx ); free(bld.buf); return NULL; } args.width = intvalue(widthptr); } else { const int *widthptr = getarg_raw(argv, &argidx); args.width = *widthptr; } } /* precision/maximal length specifier */ if (*s == '.') { s++; if (*s == '+') { args.flags |= FLAG_EXPONENTSIGN; s++; } args.precision = 0; if (isdigit(*s)) { while (isdigit(*s)) { args.precision *= 10; args.precision += *s++ - '0'; } } else if (*s == '*') { s++; if (isval) { SpnValue *precptr; /* check argc if the caller wants us to do so */ if (argc >= 0 && argidx >= argc) { format_errmsg(errmsg, OUT_OF_ARGUMENTS, argidx); free(bld.buf); return NULL; } /* precision must be an integer too */ precptr = getarg_val(argv, &argidx); if (!isnum(precptr)) { format_errmsg( errmsg, TYPE_MISMATCH, argidx, SPN_TTAG_NUMBER, precptr->type ); free(bld.buf); return NULL; } if (isfloat(precptr)) { format_errmsg( errmsg, EXPECT_INTEGER, argidx ); free(bld.buf); return NULL; } args.precision = intvalue(precptr); } else { const int *precptr = getarg_raw(argv, &argidx); args.precision = *precptr; } } } args.spec = *s++; /* check argc if the caller wants us to do so */ if (argc >= 0 && argidx >= argc) { format_errmsg(errmsg, OUT_OF_ARGUMENTS, argidx); free(bld.buf); return NULL; } /* append parsed format string */ if (append_format(&bld, &args, argv, &argidx, isval, errmsg) != 0) { free(bld.buf); return NULL; } /* update non-format chunk base pointer */ p = s; } else { s++; } } /* if the format string doesn't end with a conversion specifier, * then just append the last non-format (literal) string chunk */ if (s > p) { append_string(&bld, p, s - p); } /* append terminating NUL byte */ expand_buffer(&bld, 1); bld.buf[bld.len] = 0; if (len != NULL) { *len = bld.len; } return bld.buf; }
/* returns zero on success, nonzero on error */ static int append_format( struct string_builder *bld, const struct format_args *args, void *argv, int *argidx, int isval, char **errmsg ) { switch (args->spec) { case '%': append_string(bld, "%", 1); break; case 's': { const char *str; size_t len; if (isval) { SpnString *strobj; /* must be a string */ SpnValue *val = getarg_val(argv, argidx); if (!isstring(val)) { format_errmsg( errmsg, TYPE_MISMATCH, *argidx, SPN_TYPE_STRING, val->type ); return -1; } strobj = stringvalue(val); str = strobj->cstr; len = strobj->len; } else { str = getarg_raw(argv, argidx); len = strlen(str); } if (args->precision >= 0 && args->precision < len) { len = args->precision; } if (args->width >= 0 && args->width > len) { size_t pad = args->width - len; expand_buffer(bld, pad); while (pad-- > 0) { bld->buf[bld->len++] = ' '; } } append_string(bld, str, len); break; } case 'i': case 'd': case 'b': case 'o': case 'u': case 'x': case 'X': { char *buf, *end, *begin; size_t len = PR_LONG_DIGITS; enum format_flags flags = args->flags; unsigned base = base_for_specifier(args->spec); long n; unsigned long u; if (isval) { /* must be a number */ SpnValue *val = getarg_val(argv, argidx); if (!isnum(val)) { format_errmsg( errmsg, TYPE_MISMATCH, *argidx, SPN_TTAG_NUMBER, val->type ); return -1; } if (isint(val)) { n = intvalue(val); } else { n = floatvalue(val); /* truncate */ } } else { /* "%i" expects an int, others expect a long */ if (args->spec == 'i') { n = *(const int *)getarg_raw(argv, argidx); } else { n = *(const long *)getarg_raw(argv, argidx); } } if (args->spec == 'i' || args->spec == 'd') { /* signed conversion specifiers */ if (n < 0) { flags |= FLAG_NEGATIVE; u = -n; } else { u = n; } } else { /* unsigned conversion specifiers */ u = n; } if (args->spec == 'X') { flags |= FLAG_CAPS; } if (args->width >= 0 && args->width > len) { len = args->width; } buf = spn_malloc(len); end = buf + len; begin = ulong2str(end, u, base, args->width, flags); assert(buf <= begin); append_string(bld, begin, end - begin); free(buf); break; } case 'c': { unsigned char ch; int len = 1; /* one character is one character long... */ if (isval) { /* must be an integer */ SpnValue *val = getarg_val(argv, argidx); if (!isnum(val)) { format_errmsg( errmsg, TYPE_MISMATCH, *argidx, SPN_TTAG_NUMBER, val->type ); return -1; } if (isfloat(val)) { format_errmsg(errmsg, EXPECT_INTEGER, *argidx); return -1; } ch = intvalue(val); } else { ch = *(const long *)getarg_raw(argv, argidx); } if (args->width > len) { len = args->width; } expand_buffer(bld, len); while (len-- > 1) { bld->buf[bld->len++] = ' '; } bld->buf[bld->len++] = ch; break; } case 'f': case 'F': { char *buf, *end, *begin; size_t len; int prec; double x; enum format_flags flags = args->flags; if (isval) { SpnValue *val = getarg_val(argv, argidx); if (!isnum(val)) { format_errmsg( errmsg, TYPE_MISMATCH, *argidx, SPN_TTAG_NUMBER, val->type ); return -1; } if (isfloat(val)) { x = floatvalue(val); } else { x = intvalue(val); } } else { x = *(const double *)getarg_raw(argv, argidx); } if (args->spec == 'F') { flags |= FLAG_CAPS; } /* handle special cases */ if (+1.0 / x == +1.0 / -0.0) { /* negative zero: set sign flag and carry on */ flags |= FLAG_NEGATIVE; } else if ( x != x /* NaN */ || x == +1.0 / 0.0 /* +inf */ || x == -1.0 / 0.0 /* -inf */ ) { print_special_fp(bld, flags, args->width, x); break; } if (x < 0.0) { flags |= FLAG_NEGATIVE; x = -x; } /* at this point, `x' is non-negative or -0 */ if (x >= 1.0) { len = ceil(log10(x)) + 1; /* 10 ^ n is n + 1 digits long */ } else { len = 1; /* leading zero needs exactly one character */ } prec = args->precision < 0 ? DBL_DIG : args->precision; len += prec + 3; /* decimal point, sign, leading zero */ if (args->width >= 0 && args->width > len) { len = args->width; } buf = spn_malloc(len); end = buf + len; begin = double2str(end, x, args->width, prec, flags); assert(buf <= begin); append_string(bld, begin, end - begin); free(buf); break; } case 'B': { int boolval; const char *str; size_t len; if (isval) { /* must be a boolean */ SpnValue *val = getarg_val(argv, argidx); if (!isbool(val)) { format_errmsg( errmsg, TYPE_MISMATCH, *argidx, SPN_TTAG_BOOL, val->type ); return -1; } boolval = boolvalue(val); } else { boolval = *(const int *)getarg_raw(argv, argidx); } str = boolval ? "true" : "false"; len = strlen(str); if (args->precision >= 0 && args->precision < len) { len = args->precision; } if (args->width >= 0 && args->width > len) { size_t pad = args->width - len; expand_buffer(bld, pad); while (pad-- > 0) { bld->buf[bld->len++] = ' '; } } append_string(bld, str, len); break; } default: format_errmsg(errmsg, INVALID_SPECIFIER, ++*argidx, args->spec); return -1; } return 0; }
const char * Test::start (const char *name) { if (this->running_) return "Already running\n"; const char *msg = 0; // Reset test status to not inadvertantly report a previous test. this->status_ = -1; this->cleanup (); // Resets cmdline_, argc_, argv_ // The command line is part-way through being tokenized by strtok(). It // left off after the program name. Anything remaining are the command // line arguments for the program. Pick off whatever is there, copy it // to the cmdline_ array and fill in argc_/argv_ for the eventual run. strcpy (this->name_, name); this->argv_[0] = this->name_; this->argc_ = 1; size_t cmdchars = 0; for (char *token = strtok (0, "\t \n\r"); token != 0 && (cmdchars + strlen (token) + 1) < CMDLINE_LEN; token = strtok (0, "\t \n\r")) { // We have a new token and it will fit in cmdline_. Copy it to the // next spot in cmdline_, add it to argv_/argc_ then update cmdchars // to account for the copied-in token and its nul terminator. strcpy (&this->cmdline_[cmdchars], token); this->argv_[this->argc_] = &this->cmdline_[cmdchars]; ++this->argc_; cmdchars += (strlen (token) + 1); } char libspec[1024]; sprintf (libspec, "%s.dll", name); if ((this->dll_handle_ = LoadLibrary (libspec)) == NULL) return format_errmsg (GetLastError (), libspec); this->entry_ = (TEST_FUNC) GetProcAddress (this->dll_handle_, "main"); if (this->entry_ == NULL) { msg = format_errmsg (GetLastError (), "main"); this->cleanup (); return msg; } else { unsigned int thread_id; /* unused */ uintptr_t h = _beginthreadex (0, // security 1024 * 1024, // stack size run_test, // entrypoint (void *)this, // arglist 0, // initflag &thread_id); // thread ID this->thr_handle_ = (HANDLE) h; if (h == 0) // Test thread may have access to thr_handle_ { msg = format_errmsg (GetLastError (), "spawn"); this->cleanup (); return msg; } } return 0; }
int Peer::command (void) { // The protocol exchanges with the peer are execpted to be lock-step // request-reply command lines, so we can make assumptions about a complete // line being available to make life easier. const int MAX_RECV = 1024; char line[MAX_RECV], *p; p = &line[0]; int count = 0, len = 0; while ((count = recv (this->handle_, p, MAX_RECV - len, 0)) > 0) { p[count] = '\0'; len += count; p += count; char *nl; if ((nl = strchr (line, '\n')) == 0) continue; // At this point we have a 0-terminated string with a newline ending // the command line. Break out and process the command. break; } if (count <= 0) return -1; // Relay closed/error socket to caller char *cmd = strtok (line, "\t \n\r"); if (cmd == 0) { char err[1024]; sprintf (err, "Can't parse input: %s\n", line); this->reply (err); return -1; } // Which command is it? These commands are known: // // run <test-dll> [args] // Run test in the named test-dll; respond with "OK" or an error string. // status // If test still running return "RUNNING" else return exit status. // wait // Wait for test to exit; return "OK" // kill // Kill the thread with the most recent test; return "OK". // snaplog // Take a snapshot of the current stdout/stderr log to a new file // name and reset the stdout/stderr log. if (strcmp ("run", cmd) == 0) { char *test = strtok (0, "\t \n\r"); if (test == 0) { this->reply ("Malformed run command\n"); return -1; } // start() pulls apart the rest of the command line... const char *errmsg = this->test_.start (test); if (errmsg == 0) this->reply ("OK\n"); else this->reply (errmsg); } else if (strcmp ("status", cmd) == 0) { int status; if (this->test_.status (&status)) { char retvalmsg[64]; sprintf (retvalmsg, "%d\n", status); this->reply (retvalmsg); } else this->reply ("RUNNING\n"); } else if (strcmp ("wait", cmd) == 0) { int status = this->test_.wait (); char retvalmsg[64]; sprintf (retvalmsg, "%d\n", status); this->reply (retvalmsg); } else if (strcmp ("kill", cmd) == 0) { // Killing things is bad... say we can't and the host should reboot us. this->reply ("NO - please reboot me\n"); } else if (strcmp ("waitforfile", cmd) == 0) { char *name = strtok (0, "\t \n\r"); if (name == 0) { this->reply ("Malformed waitforfile command\n"); return -1; } char *secs_s = strtok (0, "\t \n\r"); int secs = 0; if (secs_s == 0 || (secs = atoi (secs_s)) <= 0) { this->reply ("Malformed waitforfile command\n"); return -1; } struct _stat info; const char *msg = 0; bool found = false; while (secs > 0) { if (_stat (name, &info) == -1) // No file yet { if (errno != ENOENT) { // Something more serious than no file yet; bail out. msg = format_errmsg (errno, name); break; } } else { if (info.st_size > 0) { found = true; break; } } // Either no file yet, or it's there but with no content yet. Sleep (1 * 1000); // arg is in msec --secs; } if (found) this->reply ("OK\n"); else if (secs == 0) this->reply ("TIMEOUT\n"); else this->reply (msg); } else if (strcmp ("snaplog", cmd) == 0) { if (logf == INVALID_HANDLE_VALUE) { this->reply ("NONE\n"); } else { CloseHandle (logf); if (0 == rename (LogName, "snapshot.txt")) { char abspath[1024]; if (_fullpath (abspath, "snapshot.txt", 1024)) { strcat (abspath, "\n"); this->reply (abspath); } else { // Last ditch effort to get a name back to the client this->reply ("\\ni-rt\\system\\snapshot.txt\n"); } } else { this->reply ("NONE\n"); } // Reset stdout/stderr to a new file logf = CreateFile (LogName, FILE_ALL_ACCESS, FILE_SHARE_READ, 0, // security CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); SetStdHandle (STD_OUTPUT_HANDLE, logf); SetStdHandle (STD_ERROR_HANDLE, logf); } } else { this->reply ("Unrecognized command\n"); return -1; } return 0; }