/* return a heap allocated string formed from fmt and ap arglist * returned string is allocated with xmalloc, so must free with xfree. * * args are like printf, with the addition of the following format chars: * - %m expands to strerror(errno) * - %t expands to strftime("%x %X") [ locally preferred short date/time ] * - %T expands to rfc2822 date time [ "dd, Mon yyyy hh:mm:ss GMT offset" ] * * simple format specifiers are handled explicitly to avoid calls to * vsnprintf and allow dynamic sizing of the message buffer. If a call * is made to vsnprintf, however, the message will be limited to 1024 bytes. * (inc. newline) * */ static char *vxstrfmt(const char *fmt, va_list ap) { char *buf = NULL; char *p = NULL; size_t len = (size_t) 0; char tmp[LINEBUFSIZE]; int unprocessed = 0; int long_long = 0; while (*fmt != '\0') { if ((p = (char *)strchr(fmt, '%')) == NULL) { /* no more format chars */ xstrcat(buf, fmt); break; } else { /* *p == '%' */ /* take difference from fmt to just before `%' */ len = (size_t) ((long)(p) - (long)fmt); /* append from fmt to p into buf if there's * anythere there */ if (len > 0) xstrncat(buf, fmt, len); switch (*(++p)) { case '%': /* "%%" => "%" */ xstrcatchar(buf, '%'); break; case 'm': /* "%m" => strerror(errno) */ xslurm_strerrorcat(buf); break; case 't': /* "%t" => locally preferred date/time*/ xstrftimecat(buf, "%x %X"); break; case 'T': /* "%T" => "dd, Mon yyyy hh:mm:ss off" */ xstrftimecat(buf, "%a, %d %b %Y %H:%M:%S %z"); break; case 'M': if (!log) xiso8601timecat(buf, true); else { switch (log->fmt) { case LOG_FMT_ISO8601_MS: /* "%M" => "yyyy-mm-ddThh:mm:ss.fff" */ xiso8601timecat(buf, true); break; case LOG_FMT_ISO8601: /* "%M" => "yyyy-mm-ddThh:mm:ss.fff" */ xiso8601timecat(buf, false); break; case LOG_FMT_RFC5424_MS: /* "%M" => "yyyy-mm-ddThh:mm:ss.fff(+/-)hh:mm" */ xrfc5424timecat(buf, true); break; case LOG_FMT_RFC5424: /* "%M" => "yyyy-mm-ddThh:mm:ss.fff(+/-)hh:mm" */ xrfc5424timecat(buf, false); break; case LOG_FMT_CLOCK: /* "%M" => "usec" */ #if defined(__FreeBSD__) snprintf(tmp, sizeof(tmp), "%d", clock()); #else snprintf(tmp, sizeof(tmp), "%ld", clock()); #endif xstrcat(buf, tmp); break; case LOG_FMT_SHORT: /* "%M" => "Mon DD hh:mm:ss" */ xstrftimecat(buf, "%b %d %T"); break; case LOG_FMT_THREAD_ID: set_idbuf(tmp); xstrcat(buf, tmp); break; } } break; case 's': /* "%s" => append string */ /* we deal with this case for efficiency */ if (unprocessed == 0) xstrcat(buf, va_arg(ap, char *)); else xstrcat(buf, "%s"); break; case 'f': /* "%f" => append double */ /* again, we only handle this for efficiency */ if (unprocessed == 0) { snprintf(tmp, sizeof(tmp), "%f", va_arg(ap, double)); xstrcat(buf, tmp); } else xstrcat(buf, "%f"); break; case 'd': if (unprocessed == 0) { snprintf(tmp, sizeof(tmp), "%d", va_arg(ap, int)); xstrcat(buf, tmp); } else
/* * return a heap allocated string formed from fmt and ap arglist * returned string is allocated with xmalloc, so must free with xfree. * * args are like printf, with the addition of the following format chars: * - %m expands to strerror(errno) * - %M expand to time stamp, format is configuration dependent * - %t expands to strftime("%x %X") [ locally preferred short date/time ] * - %T expands to rfc2822 date time [ "dd, Mon yyyy hh:mm:ss GMT offset" ] * * these formats are expanded first, leaving all others to be passed to * vsnprintf() to complete the expansion using the ap arglist. */ static char *vxstrfmt(const char *fmt, va_list ap) { char *intermediate_fmt = NULL; char *out_string = NULL; char *p; int found_other_formats = 0; while (*fmt != '\0') { int is_our_format = 0; p = (char *)strchr(fmt, '%'); if (p == NULL) { /* * no more format sequences, append the rest of * fmt and exit the loop: */ xstrcat(intermediate_fmt, fmt); break; } /* * make sure it's one of our format specifiers, skipping * any that aren't: */ do { switch (*(p + 1)) { case 'm': case 't': case 'T': case 'M': is_our_format = 1; break; default: found_other_formats = 1; break; } } while (!is_our_format && (p = (char *)strchr(p + 1, '%'))); if (is_our_format) { char *substitute = NULL; char substitute_on_stack[256]; int should_xfree = 1; /* * p points to the leading % of one of our formats; * append anything from fmt up to p to the intermediate * format string: */ xstrncat(intermediate_fmt, fmt, p - fmt); fmt = p + 1; /* * fill the substitute buffer with whatever text we want * to substitute for the format sequence in question: */ switch (*fmt) { case 'm': /* "%m" => strerror(errno) */ substitute = slurm_strerror(errno); should_xfree = 0; break; case 't': /* "%t" => locally preferred date/time*/ xstrftimecat(substitute, "%x %X"); break; case 'T': /* "%T" => "dd, Mon yyyy hh:mm:ss off" */ xstrftimecat(substitute, "%a, %d %b %Y %H:%M:%S %z"); break; case 'M': if (!log) { xiso8601timecat(substitute, true); break; } switch (log->fmt) { case LOG_FMT_ISO8601_MS: /* "%M" => "yyyy-mm-ddThh:mm:ss.fff" */ xiso8601timecat(substitute, true); break; case LOG_FMT_ISO8601: /* "%M" => "yyyy-mm-ddThh:mm:ss.fff" */ xiso8601timecat(substitute, false); break; case LOG_FMT_RFC5424_MS: /* "%M" => "yyyy-mm-ddThh:mm:ss.fff(+/-)hh:mm" */ xrfc5424timecat(substitute, true); break; case LOG_FMT_RFC5424: /* "%M" => "yyyy-mm-ddThh:mm:ss.fff(+/-)hh:mm" */ xrfc5424timecat(substitute, false); break; case LOG_FMT_CLOCK: /* "%M" => "usec" */ #if defined(__FreeBSD__) snprintf(substitute_on_stack, sizeof(substitute_on_stack), "%d", clock()); #else snprintf(substitute_on_stack, sizeof(substitute_on_stack), "%ld", clock()); #endif substitute = substitute_on_stack; should_xfree = 0; break; case LOG_FMT_SHORT: /* "%M" => "Mon DD hh:mm:ss" */ xstrftimecat(substitute, "%b %d %T"); break; case LOG_FMT_THREAD_ID: set_idbuf(substitute_on_stack); substitute = substitute_on_stack; should_xfree = 0; break; } break; } fmt++; if (substitute) { char *s = substitute; while (*s && (p = (char *)strchr(s, '%'))) { /* append up through the '%' */ xstrncat(intermediate_fmt, s, p - s); xstrcat(intermediate_fmt, "%%"); s = p + 1; } if (*s) { /* append whatever's left of the substitution: */ xstrcat(intermediate_fmt, s); } /* deallocate substitute if necessary: */ if (should_xfree) { xfree(substitute); } } } else { /* * no more format sequences for us, append the rest of * fmt and exit the loop: */ xstrcat(intermediate_fmt, fmt); break; } } if (intermediate_fmt && found_other_formats) { char tmp[LINEBUFSIZE]; int actual_len; va_list ap_copy; va_copy(ap_copy, ap); actual_len = vsnprintf(tmp, sizeof(tmp), intermediate_fmt, ap_copy); va_end(ap_copy); if (actual_len >= 0) { if (actual_len < sizeof(tmp)) { out_string = xstrdup(tmp); } else { /* * our C library's vsnprintf() was nice enough * to return the necessary size of the buffer! */ out_string = xmalloc(actual_len + 1); if (out_string) { va_copy(ap_copy, ap); vsnprintf(out_string, actual_len + 1, intermediate_fmt, ap_copy); va_end(ap_copy); } } } else { size_t growable_tmp_size = LINEBUFSIZE; char *growable_tmp = NULL; /* * our C library's vsnprintf() doesn't return the * necessary buffer size on overflow, it considers that * an error condition. So we need to iteratively grow * a buffer until it accommodates the vsnprintf() call */ do { growable_tmp_size += LINEBUFSIZE; growable_tmp = xrealloc(growable_tmp, growable_tmp_size); if (!growable_tmp) break; va_copy(ap_copy, ap); actual_len = vsnprintf(growable_tmp, growable_tmp_size, intermediate_fmt, ap_copy); va_end(ap_copy); } while (actual_len < 0); out_string = growable_tmp; } xfree(intermediate_fmt); } else if (intermediate_fmt) { /* * no additional format sequences, so we can just return the * intermediate_fmt string */ out_string = intermediate_fmt; } return out_string; }