From: Karel Zak Date: Mon, 1 Jul 2019 13:00:52 +0000 (+0200) Subject: scriptreplay: move all utils to script-playutils.{c,h} X-Git-Tag: v2.35-rc1~144 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=12352c967c6a069677a02883e58231194bd57ee0;p=thirdparty%2Futil-linux.git scriptreplay: move all utils to script-playutils.{c,h} Signed-off-by: Karel Zak --- diff --git a/term-utils/Makemodule.am b/term-utils/Makemodule.am index d0550f54a7..f2ea5bc530 100644 --- a/term-utils/Makemodule.am +++ b/term-utils/Makemodule.am @@ -20,7 +20,9 @@ endif # BUILD_SCRIPT if BUILD_SCRIPTREPLAY usrbin_exec_PROGRAMS += scriptreplay dist_man_MANS += term-utils/scriptreplay.1 -scriptreplay_SOURCES = term-utils/scriptreplay.c +scriptreplay_SOURCES = term-utils/scriptreplay.c \ + term-utils/script-playutils.c \ + term-utils/script-playutils.h scriptreplay_LDADD = $(LDADD) libcommon.la $(MATH_LIBS) endif # BUILD_SCRIPTREPLAY diff --git a/term-utils/script-playutils.c b/term-utils/script-playutils.c new file mode 100644 index 0000000000..9594aef20d --- /dev/null +++ b/term-utils/script-playutils.c @@ -0,0 +1,484 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "c.h" +#include "xalloc.h" +#include "closestream.h" +#include "nls.h" +#include "strutils.h" +#include "script-playutils.h" + +UL_DEBUG_DEFINE_MASK(scriptreplay); +UL_DEBUG_DEFINE_MASKNAMES(scriptreplay) = UL_DEBUG_EMPTY_MASKNAMES; + +#define DBG(m, x) __UL_DBG(scriptreplay, SCRIPTREPLAY_DEBUG_, m, x) +#define ON_DBG(m, x) __UL_DBG_CALL(scriptreplay, SCRIPTREPLAY_DEBUG_, m, x) + +/* + * The script replay is driven by timing file where each entry describes one + * step in the replay. The timing step may refer input or output (or + * signal, extra informations, etc.) + * + * The step data are stored in log files, the right log file for the step is + * selected from replay_setup. + */ +enum { + REPLAY_TIMING_SIMPLE, /* timing info in classic " " format */ + REPLAY_TIMING_MULTI /* multiple streams in format " */ +}; + +struct replay_log { + const char *streams; /* 'I'nput, 'O'utput or both */ + const char *filename; + FILE *fp; + + unsigned int noseek; /* do not seek in this log */ +}; + +struct replay_step { + char type; /* 'I'nput, 'O'utput, ... */ + double delay; + size_t size; + + char *name; /* signals / heders */ + char *value; + + struct replay_log *data; +}; + +struct replay_setup { + struct replay_log *logs; + size_t nlogs; + + struct replay_step step; /* current step */ + + FILE *timing_fp; + const char *timing_filename; + int timing_format; + int timing_line; + + char default_type; /* type for REPLAY_TIMING_SIMPLE */ + int crmode; +}; + +void replay_init_debug(void) +{ + __UL_INIT_DEBUG_FROM_ENV(scriptreplay, SCRIPTREPLAY_DEBUG_, 0, SCRIPTREPLAY_DEBUG); +} + +static int ignore_line(FILE *f) +{ + int c; + + while((c = fgetc(f)) != EOF && c != '\n'); + if (ferror(f)) + return -errno; + + DBG(LOG, ul_debug(" ignore line")); + return 0; +} + +struct replay_setup *replay_new_setup(void) +{ + return xcalloc(1, sizeof(struct replay_setup)); +} + +/* if timing file does not contains types of entries (old format) than use this + * type as the default */ +int replay_set_default_type(struct replay_setup *stp, char type) +{ + assert(stp); + stp->default_type = type; + + return 0; +} + +int replay_set_crmode(struct replay_setup *stp, int mode) +{ + assert(stp); + stp->crmode = mode; + + return 0; +} + +static struct replay_log *replay_new_log(struct replay_setup *stp, + const char *streams, + const char *filename, + FILE *f) +{ + struct replay_log *log; + + assert(stp); + assert(streams); + assert(filename); + + stp->logs = xrealloc(stp->logs, (stp->nlogs + 1) * sizeof(*log)); + log = &stp->logs[stp->nlogs]; + stp->nlogs++; + + log->filename = filename; + log->streams = streams; + log->fp = f; + + return log; +} + +int replay_set_timing_file(struct replay_setup *stp, const char *filename) +{ + int c, rc = 0; + + assert(stp); + assert(filename); + + stp->timing_filename = filename; + stp->timing_line = 0; + + stp->timing_fp = fopen(filename, "r"); + if (!stp->timing_fp) + rc = -errno; + else { + /* detect timing file format */ + c = fgetc(stp->timing_fp); + if (c != EOF) { + if (isdigit((unsigned int) c)) + stp->timing_format = REPLAY_TIMING_SIMPLE; + else + stp->timing_format = REPLAY_TIMING_MULTI; + ungetc(c, stp->timing_fp); + } else if (ferror(stp->timing_fp)) + rc = -errno; + } + + if (rc && stp->timing_fp) { + fclose(stp->timing_fp); + stp->timing_fp = NULL; + } + + /* create quasi-log for signals, headers, etc. */ + if (rc == 0 && stp->timing_format == REPLAY_TIMING_MULTI) { + struct replay_log *log = replay_new_log(stp, "SH", + filename, stp->timing_fp); + if (!log) + rc = -ENOMEM; + else { + log->noseek = 1; + DBG(LOG, ul_debug("accociate log file '%s' with 'SH'", filename)); + } + } + + DBG(TIMING, ul_debug("timing file set to '%s' [rc=%d]", filename, rc)); + return rc; +} + +const char *replay_get_timing_file(struct replay_setup *setup) +{ + assert(setup); + return setup->timing_filename; +} + +int replay_get_timing_line(struct replay_setup *setup) +{ + assert(setup); + return setup->timing_line; +} + +int replay_associate_log(struct replay_setup *stp, + const char *streams, const char *filename) +{ + FILE *f; + int rc; + + assert(stp); + assert(streams); + assert(filename); + + /* open the file and skip the first line */ + f = fopen(filename, "r"); + rc = f == NULL ? -errno : ignore_line(f); + + if (rc == 0) + replay_new_log(stp, streams, filename, f); + + DBG(LOG, ul_debug("accociate log file '%s' with '%s' [rc=%d]", filename, streams, rc)); + return rc; +} + +static int is_wanted_stream(char type, const char *streams) +{ + if (streams == NULL) + return 1; + if (strchr(streams, type)) + return 1; + return 0; +} + +static void replay_reset_step(struct replay_step *step) +{ + assert(step); + + step->size = 0; + step->delay = 0; + step->data = NULL; + step->type = 0; +} + +double replay_step_get_delay(struct replay_step *step) +{ + assert(step); + return step->delay; +} + +/* current data log file */ +const char *replay_step_get_filename(struct replay_step *step) +{ + assert(step); + return step->data->filename; +} + +static int read_multistream_step(struct replay_step *step, FILE *f, char type) +{ + int rc = 0; + char nl; + + + switch (type) { + case 'O': /* output */ + case 'I': /* input */ + rc = fscanf(f, "%lf %zu%c\n", &step->delay, &step->size, &nl); + if (rc != 3 || nl != '\n') + rc = -EINVAL; + else + rc = 0; + break; + + case 'S': /* signal */ + case 'H': /* header */ + { + char buf[BUFSIZ]; + + rc = fscanf(f, "%lf ", &step->delay); /* delay */ + if (rc != 1) + break; + + rc = fscanf(f, "%s", buf); /* name */ + if (rc != 1) + break; + step->name = strrealloc(step->name, buf); + if (!step->name) + err_oom(); + + if (!fgets(buf, sizeof(buf), f)) { /* value */ + rc = -errno; + break; + } + if (*buf) { + strrem(buf, '\n'); + step->value = strrealloc(step->value, buf); + if (!step->value) + err_oom(); + } + rc = 0; + break; + } + default: + break; + } + + DBG(TIMING, ul_debug(" read step delay & size [rc=%d]", rc)); + return rc; +} + +static struct replay_log *replay_get_stream_log(struct replay_setup *stp, char stream) +{ + size_t i; + + for (i = 0; i < stp->nlogs; i++) { + struct replay_log *log = &stp->logs[i]; + + if (is_wanted_stream(stream, log->streams)) + return log; + } + return NULL; +} + +static int replay_seek_log(struct replay_log *log, size_t move) +{ + if (log->noseek) + return 0; + + DBG(LOG, ul_debug(" %s: seek ++ %zu", log->filename, move)); + return fseek(log->fp, move, SEEK_CUR) == (off_t) -1 ? -errno : 0; +} + +/* returns next step with pointer to the right log file for specified streams (e.g. + * "IOS" for in/out/signals) or all streams if stream is NULL. + * + * returns: 0 = success, <0 = error, 1 = done (EOF) + */ +int replay_get_next_step(struct replay_setup *stp, char *streams, struct replay_step **xstep) +{ + struct replay_step *step; + int rc; + double ignored_delay = 0; + + assert(stp); + assert(stp->timing_fp); + assert(xstep); + + step = &stp->step; + *xstep = NULL; + + do { + struct replay_log *log = NULL; + + rc = 1; /* done */ + if (feof(stp->timing_fp)) + break; + + DBG(TIMING, ul_debug("reading next step")); + + replay_reset_step(step); + stp->timing_line++; + + switch (stp->timing_format) { + case REPLAY_TIMING_SIMPLE: + /* old format is the same as new format, but without prefix */ + rc = read_multistream_step(step, stp->timing_fp, stp->default_type); + if (rc == 0) + step->type = stp->default_type; + break; + case REPLAY_TIMING_MULTI: + rc = fscanf(stp->timing_fp, "%c ", &step->type); + if (rc != 1) + rc = -EINVAL; + else + rc = read_multistream_step(step, + stp->timing_fp, + step->type); + break; + } + + if (rc) { + if (rc < 0 && feof(stp->timing_fp)) + rc = 1; + break; /* error or EOF */ + } + + DBG(TIMING, ul_debug(" step entry is '%c'", step->type)); + + log = replay_get_stream_log(stp, step->type); + if (log) { + if (is_wanted_stream(step->type, streams)) { + step->data = log; + *xstep = step; + DBG(LOG, ul_debug(" use %s as data source", log->filename)); + goto done; + } + /* The step entry is unwanted, but we keep the right + * position in the log file although the data are ignored. + */ + replay_seek_log(log, step->size); + } else + DBG(TIMING, ul_debug(" not found log for '%c' stream", step->type)); + + DBG(TIMING, ul_debug(" ignore step '%c' [delay=%f]", + step->type, step->delay)); + ignored_delay += step->delay; + } while (rc == 0); + +done: + if (ignored_delay) + step->delay += ignored_delay; + + DBG(TIMING, ul_debug("reading next step done [rc=%d delay=%f (ignored=%f) size=%zu]", + rc, step->delay, ignored_delay, step->size)); + return rc; +} + +/* return: 0 = success, <0 = error, 1 = done (EOF) */ +int replay_emit_step_data(struct replay_setup *stp, struct replay_step *step, int fd) +{ + size_t ct; + int rc = 0, cr2nl = 0; + char buf[BUFSIZ]; + + assert(stp); + assert(step); + switch (step->type) { + case 'S': + assert(step->name); + assert(step->value); + dprintf(fd, "%s %s\n", step->name, step->value); + DBG(LOG, ul_debug("log signal emited")); + return 0; + case 'H': + assert(step->name); + assert(step->value); + dprintf(fd, "%10s: %s\n", step->name, step->value); + DBG(LOG, ul_debug("log header emited")); + return 0; + default: + break; /* continue with real data */ + } + + assert(step->size); + assert(step->data); + assert(step->data->fp); + + switch (stp->crmode) { + case REPLAY_CRMODE_AUTO: + if (step->type == 'I') + cr2nl = 1; + break; + case REPLAY_CRMODE_NEVER: + cr2nl = 0; + break; + case REPLAY_CRMODE_ALWAYS: + cr2nl = 1; + break; + } + + for (ct = step->size; ct > 0; ) { + size_t len, cc; + + cc = ct > sizeof(buf) ? sizeof(buf): ct; + len = fread(buf, 1, cc, step->data->fp); + + if (!len) { + DBG(LOG, ul_debug("log data emit: failed to read log %m")); + break; + } + + if (cr2nl) { + size_t i; + + for (i = 0; i < len; i++) { + if (buf[i] == 0x0D) + buf[i] = '\n'; + } + } + + ct -= len; + cc = write(fd, buf, len); + if (cc != len) { + rc = -errno; + DBG(LOG, ul_debug("log data emit: failed write data %m")); + break; + } + } + + if (ct && ferror(step->data->fp)) + rc = -errno; + if (ct && feof(step->data->fp)) + rc = 1; + + DBG(LOG, ul_debug("log data emited [rc=%d size=%zu]", rc, step->size)); + return rc; +} diff --git a/term-utils/script-playutils.h b/term-utils/script-playutils.h new file mode 100644 index 0000000000..9e803fa334 --- /dev/null +++ b/term-utils/script-playutils.h @@ -0,0 +1,45 @@ +#ifndef UTIL_LINUX_SCRIPT_PLAYUTILS_H +#define UTIL_LINUX_SCRIPT_PLAYUTILS_H + +#include "c.h" +#include "debug.h" + +#define SCRIPTREPLAY_DEBUG_INIT (1 << 1) +#define SCRIPTREPLAY_DEBUG_TIMING (1 << 2) +#define SCRIPTREPLAY_DEBUG_LOG (1 << 3) +#define SCRIPTREPLAY_DEBUG_MISC (1 << 4) +#define SCRIPTREPLAY_DEBUG_ALL 0xFFFF + +UL_DEBUG_DECLARE_MASK(scriptreplay); + +#define DBG(m, x) __UL_DBG(scriptreplay, SCRIPTREPLAY_DEBUG_, m, x) +#define ON_DBG(m, x) __UL_DBG_CALL(scriptreplay, SCRIPTREPLAY_DEBUG_, m, x) + +/* CR to '\n' mode */ +enum { + REPLAY_CRMODE_AUTO = 0, + REPLAY_CRMODE_NEVER, + REPLAY_CRMODE_ALWAYS +}; + +struct replay_setup; +struct replay_step; + +void replay_init_debug(void); +struct replay_setup *replay_new_setup(void); + + +int replay_set_default_type(struct replay_setup *stp, char type); +int replay_set_crmode(struct replay_setup *stp, int mode); +int replay_set_timing_file(struct replay_setup *stp, const char *filename); +const char *replay_get_timing_file(struct replay_setup *setup); +int replay_get_timing_line(struct replay_setup *setup); +int replay_associate_log(struct replay_setup *stp, const char *streams, const char *filename); + +double replay_step_get_delay(struct replay_step *step); +const char *replay_step_get_filename(struct replay_step *step); +int replay_get_next_step(struct replay_setup *stp, char *streams, struct replay_step **xstep); + +int replay_emit_step_data(struct replay_setup *stp, struct replay_step *step, int fd); + +#endif /* UTIL_LINUX_SCRIPT_PLAYUTILS_H */ diff --git a/term-utils/scriptreplay.c b/term-utils/scriptreplay.c index 0693c17190..a6bbeaf814 100644 --- a/term-utils/scriptreplay.c +++ b/term-utils/scriptreplay.c @@ -28,473 +28,16 @@ #include #include - #include "c.h" -#include "debug.h" #include "xalloc.h" #include "closestream.h" #include "nls.h" #include "strutils.h" #include "optutils.h" - -static UL_DEBUG_DEFINE_MASK(scriptreplay); -UL_DEBUG_DEFINE_MASKNAMES(scriptreplay) = UL_DEBUG_EMPTY_MASKNAMES; - -#define SCRIPTREPLAY_DEBUG_INIT (1 << 1) -#define SCRIPTREPLAY_DEBUG_TIMING (1 << 2) -#define SCRIPTREPLAY_DEBUG_LOG (1 << 3) -#define SCRIPTREPLAY_DEBUG_MISC (1 << 4) -#define SCRIPTREPLAY_DEBUG_ALL 0xFFFF - -#define DBG(m, x) __UL_DBG(scriptreplay, SCRIPTREPLAY_DEBUG_, m, x) -#define ON_DBG(m, x) __UL_DBG_CALL(scriptreplay, SCRIPTREPLAY_DEBUG_, m, x) +#include "script-playutils.h" #define SCRIPT_MIN_DELAY 0.0001 /* from original sripreplay.pl */ -/* - * The script replay is driven by timing file where each entry describes one - * step in the replay. The timing step may refer input or output (or - * signal, extra informations, etc.) - * - * The step data are stored in log files, the right log file for the step is - * selected from replay_setup. - * - * TODO: move struct replay_{log,step,setup} to script-playutils.c to make it - * usable for scriptlive(1) code. - */ - -enum { - REPLAY_TIMING_SIMPLE, /* timing info in classic " " format */ - REPLAY_TIMING_MULTI /* multiple streams in format " */ -}; - -/* CR to '\n' mode */ -enum { - REPLAY_CRMODE_AUTO = 0, - REPLAY_CRMODE_NEVER, - REPLAY_CRMODE_ALWAYS -}; - -struct replay_log { - const char *streams; /* 'I'nput, 'O'utput or both */ - const char *filename; - FILE *fp; - - unsigned int noseek; /* do not seek in this log */ -}; - -struct replay_step { - char type; /* 'I'nput, 'O'utput, ... */ - double delay; - size_t size; - - char *name; /* signals / heders */ - char *value; - - struct replay_log *data; -}; - -struct replay_setup { - struct replay_log *logs; - size_t nlogs; - - struct replay_step step; /* current step */ - - FILE *timing_fp; - const char *timing_filename; - int timing_format; - int timing_line; - - char default_type; /* type for REPLAY_TIMING_SIMPLE */ - int crmode; -}; - -static void scriptreplay_init_debug(void) -{ - __UL_INIT_DEBUG_FROM_ENV(scriptreplay, SCRIPTREPLAY_DEBUG_, 0, SCRIPTREPLAY_DEBUG); -} - -static int ignore_line(FILE *f) -{ - int c; - - while((c = fgetc(f)) != EOF && c != '\n'); - if (ferror(f)) - return -errno; - - DBG(LOG, ul_debug(" ignore line")); - return 0; -} - -/* if timing file does not contains types of entries (old format) than use this - * type as the default */ -static int replay_set_default_type(struct replay_setup *stp, char type) -{ - assert(stp); - stp->default_type = type; - - return 0; -} - -static int replay_set_crmode(struct replay_setup *stp, int mode) -{ - assert(stp); - stp->crmode = mode; - - return 0; -} - -static struct replay_log *replay_new_log(struct replay_setup *stp, - const char *streams, - const char *filename, - FILE *f) -{ - struct replay_log *log; - - assert(stp); - assert(streams); - assert(filename); - - stp->logs = xrealloc(stp->logs, (stp->nlogs + 1) * sizeof(*log)); - log = &stp->logs[stp->nlogs]; - stp->nlogs++; - - log->filename = filename; - log->streams = streams; - log->fp = f; - - return log; -} - -static int replay_set_timing_file(struct replay_setup *stp, const char *filename) -{ - int c, rc = 0; - - assert(stp); - assert(filename); - - stp->timing_filename = filename; - stp->timing_line = 0; - - stp->timing_fp = fopen(filename, "r"); - if (!stp->timing_fp) - rc = -errno; - else { - /* detect timing file format */ - c = fgetc(stp->timing_fp); - if (c != EOF) { - if (isdigit((unsigned int) c)) - stp->timing_format = REPLAY_TIMING_SIMPLE; - else - stp->timing_format = REPLAY_TIMING_MULTI; - ungetc(c, stp->timing_fp); - } else if (ferror(stp->timing_fp)) - rc = -errno; - } - - if (rc && stp->timing_fp) { - fclose(stp->timing_fp); - stp->timing_fp = NULL; - } - - /* create quasi-log for signals, headers, etc. */ - if (rc == 0 && stp->timing_format == REPLAY_TIMING_MULTI) { - struct replay_log *log = replay_new_log(stp, "SH", - filename, stp->timing_fp); - if (!log) - rc = -ENOMEM; - else { - log->noseek = 1; - DBG(LOG, ul_debug("accociate log file '%s' with 'SH'", filename)); - } - } - - DBG(TIMING, ul_debug("timing file set to '%s' [rc=%d]", filename, rc)); - return rc; -} - - -static int replay_associate_log(struct replay_setup *stp, - const char *streams, const char *filename) -{ - FILE *f; - int rc; - - assert(stp); - assert(streams); - assert(filename); - - /* open the file and skip the first line */ - f = fopen(filename, "r"); - rc = f == NULL ? -errno : ignore_line(f); - - if (rc == 0) - replay_new_log(stp, streams, filename, f); - - DBG(LOG, ul_debug("accociate log file '%s' with '%s' [rc=%d]", filename, streams, rc)); - return rc; -} - -static int is_wanted_stream(char type, const char *streams) -{ - if (streams == NULL) - return 1; - if (strchr(streams, type)) - return 1; - return 0; -} - -static void replay_reset_step(struct replay_step *step) -{ - assert(step); - - step->size = 0; - step->delay = 0; - step->data = NULL; - step->type = 0; -} - -static int read_multistream_step(struct replay_step *step, FILE *f, char type) -{ - int rc = 0; - char nl; - - - switch (type) { - case 'O': /* output */ - case 'I': /* input */ - rc = fscanf(f, "%lf %zu%c\n", &step->delay, &step->size, &nl); - if (rc != 3 || nl != '\n') - rc = -EINVAL; - else - rc = 0; - break; - - case 'S': /* signal */ - case 'H': /* header */ - { - char buf[BUFSIZ]; - - rc = fscanf(f, "%lf ", &step->delay); /* delay */ - if (rc != 1) - break; - - rc = fscanf(f, "%s", buf); /* name */ - if (rc != 1) - break; - step->name = strrealloc(step->name, buf); - if (!step->name) - err_oom(); - - if (!fgets(buf, sizeof(buf), f)) { /* value */ - rc = -errno; - break; - } - if (*buf) { - strrem(buf, '\n'); - step->value = strrealloc(step->value, buf); - if (!step->value) - err_oom(); - } - rc = 0; - break; - } - default: - break; - } - - DBG(TIMING, ul_debug(" read step delay & size [rc=%d]", rc)); - return rc; -} - -static struct replay_log *replay_get_stream_log(struct replay_setup *stp, char stream) -{ - size_t i; - - for (i = 0; i < stp->nlogs; i++) { - struct replay_log *log = &stp->logs[i]; - - if (is_wanted_stream(stream, log->streams)) - return log; - } - return NULL; -} - -static int replay_seek_log(struct replay_log *log, size_t move) -{ - if (log->noseek) - return 0; - - DBG(LOG, ul_debug(" %s: seek ++ %zu", log->filename, move)); - return fseek(log->fp, move, SEEK_CUR) == (off_t) -1 ? -errno : 0; -} - -/* returns next step with pointer to the right log file for specified streams (e.g. - * "IOS" for in/out/signals) or all streams if stream is NULL. - * - * returns: 0 = success, <0 = error, 1 = done (EOF) - */ -static int replay_get_next_step(struct replay_setup *stp, char *streams, struct replay_step **xstep) -{ - struct replay_step *step; - int rc; - double ignored_delay = 0; - - assert(stp); - assert(stp->timing_fp); - assert(xstep); - - step = &stp->step; - *xstep = NULL; - - do { - struct replay_log *log = NULL; - - rc = 1; /* done */ - if (feof(stp->timing_fp)) - break; - - DBG(TIMING, ul_debug("reading next step")); - - replay_reset_step(step); - stp->timing_line++; - - switch (stp->timing_format) { - case REPLAY_TIMING_SIMPLE: - /* old format is the same as new format, but without prefix */ - rc = read_multistream_step(step, stp->timing_fp, stp->default_type); - if (rc == 0) - step->type = stp->default_type; - break; - case REPLAY_TIMING_MULTI: - rc = fscanf(stp->timing_fp, "%c ", &step->type); - if (rc != 1) - rc = -EINVAL; - else - rc = read_multistream_step(step, - stp->timing_fp, - step->type); - break; - } - - if (rc) { - if (rc < 0 && feof(stp->timing_fp)) - rc = 1; - break; /* error or EOF */ - } - - DBG(TIMING, ul_debug(" step entry is '%c'", step->type)); - - log = replay_get_stream_log(stp, step->type); - if (log) { - if (is_wanted_stream(step->type, streams)) { - step->data = log; - *xstep = step; - DBG(LOG, ul_debug(" use %s as data source", log->filename)); - goto done; - } - /* The step entry is unwanted, but we keep the right - * position in the log file although the data are ignored. - */ - replay_seek_log(log, step->size); - } else - DBG(TIMING, ul_debug(" not found log for '%c' stream", step->type)); - - DBG(TIMING, ul_debug(" ignore step '%c' [delay=%f]", - step->type, step->delay)); - ignored_delay += step->delay; - } while (rc == 0); - -done: - if (ignored_delay) - step->delay += ignored_delay; - - DBG(TIMING, ul_debug("reading next step done [rc=%d delay=%f (ignored=%f) size=%zu]", - rc, step->delay, ignored_delay, step->size)); - return rc; -} - -/* return: 0 = success, <0 = error, 1 = done (EOF) */ -static int replay_emit_step_data(struct replay_setup *stp, struct replay_step *step, int fd) -{ - size_t ct; - int rc = 0, cr2nl = 0; - char buf[BUFSIZ]; - - assert(stp); - assert(step); - switch (step->type) { - case 'S': - assert(step->name); - assert(step->value); - dprintf(fd, "%s %s\n", step->name, step->value); - DBG(LOG, ul_debug("log signal emited")); - return 0; - case 'H': - assert(step->name); - assert(step->value); - dprintf(fd, "%10s: %s\n", step->name, step->value); - DBG(LOG, ul_debug("log header emited")); - return 0; - default: - break; /* continue with real data */ - } - - assert(step->size); - assert(step->data); - assert(step->data->fp); - - switch (stp->crmode) { - case REPLAY_CRMODE_AUTO: - if (step->type == 'I') - cr2nl = 1; - break; - case REPLAY_CRMODE_NEVER: - cr2nl = 0; - break; - case REPLAY_CRMODE_ALWAYS: - cr2nl = 1; - break; - } - - for (ct = step->size; ct > 0; ) { - size_t len, cc; - - cc = ct > sizeof(buf) ? sizeof(buf): ct; - len = fread(buf, 1, cc, step->data->fp); - - if (!len) { - DBG(LOG, ul_debug("log data emit: failed to read log %m")); - break; - } - - if (cr2nl) { - size_t i; - - for (i = 0; i < len; i++) { - if (buf[i] == 0x0D) - buf[i] = '\n'; - } - } - - ct -= len; - cc = write(fd, buf, len); - if (cc != len) { - rc = -errno; - DBG(LOG, ul_debug("log data emit: failed write data %m")); - break; - } - } - - if (ct && ferror(step->data->fp)) - rc = -errno; - if (ct && feof(step->data->fp)) - rc = 1; - - DBG(LOG, ul_debug("log data emited [rc=%d size=%zu]", rc, step->size)); - return rc; -} - static void __attribute__((__noreturn__)) usage(void) { @@ -580,7 +123,7 @@ static void appendchr(char *buf, size_t bufsz, int c) int main(int argc, char *argv[]) { - struct replay_setup setup = { .nlogs = 0 }; + struct replay_setup *setup = NULL; struct replay_step *step = NULL; char streams[6] = {0}; /* IOSI - in, out, signal,info */ const char *log_out = NULL, @@ -625,7 +168,7 @@ main(int argc, char *argv[]) textdomain(PACKAGE); close_stdout_atexit(); - scriptreplay_init_debug(); + replay_init_debug(); while ((ch = getopt_long(argc, argv, "B:c:I:O:t:s:d:m:x:Vh", longopts, NULL)) != -1) { @@ -708,16 +251,18 @@ main(int argc, char *argv[]) if (!(log_out || log_in || log_io)) errx(EXIT_FAILURE, _("data log file not specified")); - if (replay_set_timing_file(&setup, log_tm) != 0) + setup = replay_new_setup(); + + if (replay_set_timing_file(setup, log_tm) != 0) err(EXIT_FAILURE, _("cannot open %s"), log_tm); - if (log_out && replay_associate_log(&setup, "O", log_out) != 0) + if (log_out && replay_associate_log(setup, "O", log_out) != 0) err(EXIT_FAILURE, _("cannot open %s"), log_out); - if (log_in && replay_associate_log(&setup, "I", log_in) != 0) + if (log_in && replay_associate_log(setup, "I", log_in) != 0) err(EXIT_FAILURE, _("cannot open %s"), log_in); - if (log_io && replay_associate_log(&setup, "IO", log_io) != 0) + if (log_io && replay_associate_log(setup, "IO", log_io) != 0) err(EXIT_FAILURE, _("cannot open %s"), log_io); if (!*streams) { @@ -728,32 +273,34 @@ main(int argc, char *argv[]) appendchr(streams, sizeof(streams), 'I'); } - replay_set_default_type(&setup, + replay_set_default_type(setup, *streams && streams[1] == '\0' ? *streams : 'O'); - replay_set_crmode(&setup, crmode); + replay_set_crmode(setup, crmode); do { - rc = replay_get_next_step(&setup, streams, &step); + rc = replay_get_next_step(setup, streams, &step); if (rc) break; if (!summary) { - step->delay /= divi; - if (maxdelayopt && step->delay > maxdelay) - step->delay = maxdelay; - if (step->delay > SCRIPT_MIN_DELAY) - delay_for(step->delay); + double delay = replay_step_get_delay(step); + + delay /= divi; + if (maxdelayopt && delay > maxdelay) + delay = maxdelay; + if (delay > SCRIPT_MIN_DELAY) + delay_for(delay); } - rc = replay_emit_step_data(&setup, step, STDOUT_FILENO); + rc = replay_emit_step_data(setup, step, STDOUT_FILENO); } while (rc == 0); if (step && rc < 0) - err(EXIT_FAILURE, _("%s: log file error"), step->data->filename); + err(EXIT_FAILURE, _("%s: log file error"), replay_step_get_filename(step)); else if (rc < 0) err(EXIT_FAILURE, _("%s: line %d: timing file error"), - setup.timing_filename, - setup.timing_line); + replay_get_timing_file(setup), + replay_get_timing_line(setup)); printf("\n"); exit(EXIT_SUCCESS); }