dump_fmtstr(fmt, buf, fullsubs)
const char *fmt;
char *buf;
boolean fullsubs; /* True -> full substitution for file name, False ->
                   * partial substitution for '--showpaths' feedback
                   * where there's no game in progress when executed */
{
    const char *fp = fmt;
    char *bp = buf;
    int slen, len = 0;
    char tmpbuf[BUFSZ];
    char verbuf[BUFSZ];
    long uid;
    time_t now;

    now = dumplog_now;
    uid = (long) getuid();

    /*
     * Note: %t and %T assume that time_t is a 'long int' number of
     * seconds since some epoch value.  That's quite iffy....  The
     * unit of time might be different and the datum size might be
     * some variant of 'long long int'.  [Their main purpose is to
     * construct a unique file name rather than record the date and
     * time; violating the 'long seconds since base-date' assumption
     * may or may not interfere with that usage.]
     */

    while (fp && *fp && len < BUFSZ - 1) {
        if (*fp == '%') {
            fp++;
            switch (*fp) {
            default:
                goto finish;
            case '\0': /* fallthrough */
            case '%':  /* literal % */
                Sprintf(tmpbuf, "%%");
                break;
            case 't': /* game start, timestamp */
                if (fullsubs)
                    Sprintf(tmpbuf, "%lu", (unsigned long) ubirthday);
                else
                    Strcpy(tmpbuf, "{game start cookie}");
                break;
            case 'T': /* current time, timestamp */
                if (fullsubs)
                    Sprintf(tmpbuf, "%lu", (unsigned long) now);
                else
                    Strcpy(tmpbuf, "{current time cookie}");
                break;
            case 'd': /* game start, YYYYMMDDhhmmss */
                if (fullsubs)
                    Sprintf(tmpbuf, "%08ld%06ld",
                            yyyymmdd(ubirthday), hhmmss(ubirthday));
                else
                    Strcpy(tmpbuf, "{game start date+time}");
                break;
            case 'D': /* current time, YYYYMMDDhhmmss */
                if (fullsubs)
                    Sprintf(tmpbuf, "%08ld%06ld", yyyymmdd(now), hhmmss(now));
                else
                    Strcpy(tmpbuf, "{current date+time}");
                break;
            case 'v': /* version, eg. "3.6.5-0" */
                Sprintf(tmpbuf, "%s", version_string(verbuf));
                break;
            case 'u': /* UID */
                Sprintf(tmpbuf, "%ld", uid);
                break;
            case 'n': /* player name */
                if (fullsubs)
                    Sprintf(tmpbuf, "%s", *plname ? plname : "unknown");
                else
                    Strcpy(tmpbuf, "{hero name}");
                break;
            case 'N': /* first character of player name */
                if (fullsubs)
                    Sprintf(tmpbuf, "%c", *plname ? *plname : 'u');
                else
                    Strcpy(tmpbuf, "{hero initial}");
                break;
            }
            if (fullsubs) {
                /* replace potentially troublesome characters (including
                   <space> even though it might be an acceptable file name
                   character); user shouldn't be able to get ' ' or '/'
                   or '\\' into plname[] but play things safe */
                (void) strNsubst(tmpbuf, " ", "_", 0);
                (void) strNsubst(tmpbuf, "/", "_", 0);
                (void) strNsubst(tmpbuf, "\\", "_", 0);
                /* note: replacements are only done on field substitutions,
                   not on the template (from sysconf or DUMPLOG_FILE) */
            }

            slen = (int) strlen(tmpbuf);
            if (len + slen < BUFSZ - 1) {
                len += slen;
                Sprintf(bp, "%s", tmpbuf);
                bp += slen;
                if (*fp)
                    fp++;
            } else
                break;
        } else {
            *bp = *fp;
            bp++;
            fp++;
            len++;
        }
    }
 finish:
    *bp = '\0';
    return buf;
}