Hello community, here is the log from the commit of package logwarn for openSUSE:Factory checked in at 2016-06-20 11:07:38 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/logwarn (Old) and /work/SRC/openSUSE:Factory/.logwarn.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "logwarn" Changes: -------- --- /work/SRC/openSUSE:Factory/logwarn/logwarn.changes 2016-05-29 03:14:32.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.logwarn.new/logwarn.changes 2016-06-20 11:07:44.000000000 +0200 @@ -1,0 +2,6 @@ +Sun Jun 19 22:36:00 UTC 2016 - [email protected] + +- Upgrade to release 1.0.13 + + Added `-T' flag for frequency filtering + +------------------------------------------------------------------- Old: ---- logwarn-1.0.12.tar.gz New: ---- logwarn-1.0.13.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ logwarn.spec ++++++ --- /var/tmp/diff_new_pack.dMrbQ0/_old 2016-06-20 11:07:46.000000000 +0200 +++ /var/tmp/diff_new_pack.dMrbQ0/_new 2016-06-20 11:07:46.000000000 +0200 @@ -18,7 +18,7 @@ Name: logwarn -Version: 1.0.12 +Version: 1.0.13 Release: 0 Summary: Utility for finding interesting messages in log files License: Apache-2.0 ++++++ logwarn-1.0.12.tar.gz -> logwarn-1.0.13.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logwarn-1.0.12/CHANGES new/logwarn-1.0.13/CHANGES --- old/logwarn-1.0.12/CHANGES 2016-05-25 04:04:38.000000000 +0200 +++ new/logwarn-1.0.13/CHANGES 2016-06-19 23:16:59.000000000 +0200 @@ -1,4 +1,8 @@ +Version 1.0.13 Released June 19, 2016 + + - Added `-T num/secs' for frequency filtering + Version 1.0.12 Released May 24, 2016 - Added note to man page that the default behavior is to match diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logwarn-1.0.12/README.md new/logwarn-1.0.13/README.md --- old/logwarn-1.0.12/README.md 2015-05-04 20:21:56.000000000 +0200 +++ new/logwarn-1.0.13/README.md 2016-06-19 01:01:27.000000000 +0200 @@ -11,3 +11,5 @@ A [Nagios](http://www.nagios.org/) plugin is also included: see the [NagiosPlugin](https://github.com/archiecobbs/logwarn/wiki/NagiosPlugin) wiki page for more info. You can view the [ManPage](https://github.com/archiecobbs/logwarn/wiki/ManPage) online. + +Downloads available [here](https://github.com/archiecobbs/logwarn/wiki/Downloads). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logwarn-1.0.12/configure new/logwarn-1.0.13/configure --- old/logwarn-1.0.12/configure 2016-05-25 04:15:47.000000000 +0200 +++ new/logwarn-1.0.13/configure 2016-06-19 23:20:32.000000000 +0200 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for logwarn Utility for finding interesting messages in log files 1.0.12. +# Generated by GNU Autoconf 2.69 for logwarn Utility for finding interesting messages in log files 1.0.13. # # Report bugs to <https://github.com/archiecobbs/logwarn/>. # @@ -580,8 +580,8 @@ # Identity of this package. PACKAGE_NAME='logwarn Utility for finding interesting messages in log files' PACKAGE_TARNAME='logwarn' -PACKAGE_VERSION='1.0.12' -PACKAGE_STRING='logwarn Utility for finding interesting messages in log files 1.0.12' +PACKAGE_VERSION='1.0.13' +PACKAGE_STRING='logwarn Utility for finding interesting messages in log files 1.0.13' PACKAGE_BUGREPORT='https://github.com/archiecobbs/logwarn/' PACKAGE_URL='' @@ -1274,7 +1274,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures logwarn Utility for finding interesting messages in log files 1.0.12 to adapt to many kinds of systems. +\`configure' configures logwarn Utility for finding interesting messages in log files 1.0.13 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1340,7 +1340,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of logwarn Utility for finding interesting messages in log files 1.0.12:";; + short | recursive ) echo "Configuration of logwarn Utility for finding interesting messages in log files 1.0.13:";; esac cat <<\_ACEOF @@ -1437,7 +1437,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -logwarn Utility for finding interesting messages in log files configure 1.0.12 +logwarn Utility for finding interesting messages in log files configure 1.0.13 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1693,7 +1693,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by logwarn Utility for finding interesting messages in log files $as_me 1.0.12, which was +It was created by logwarn Utility for finding interesting messages in log files $as_me 1.0.13, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2556,7 +2556,7 @@ # Define the identity of the package. PACKAGE='logwarn' - VERSION='1.0.12' + VERSION='1.0.13' cat >>confdefs.h <<_ACEOF @@ -4838,7 +4838,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by logwarn Utility for finding interesting messages in log files $as_me 1.0.12, which was +This file was extended by logwarn Utility for finding interesting messages in log files $as_me 1.0.13, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -4904,7 +4904,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -logwarn Utility for finding interesting messages in log files config.status 1.0.12 +logwarn Utility for finding interesting messages in log files config.status 1.0.13 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logwarn-1.0.12/configure.ac new/logwarn-1.0.13/configure.ac --- old/logwarn-1.0.12/configure.ac 2015-08-25 21:47:56.000000000 +0200 +++ new/logwarn-1.0.13/configure.ac 2016-06-19 23:19:48.000000000 +0200 @@ -16,7 +16,7 @@ # limitations under the License. # -AC_INIT([logwarn Utility for finding interesting messages in log files], [1.0.12], [https://github.com/archiecobbs/logwarn/], [logwarn]) +AC_INIT([logwarn Utility for finding interesting messages in log files], [1.0.13], [https://github.com/archiecobbs/logwarn/], [logwarn]) AC_CONFIG_AUX_DIR(scripts) AM_INIT_AUTOMAKE dnl AM_MAINTAINER_MODE diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logwarn-1.0.12/gitrev.c new/logwarn-1.0.13/gitrev.c --- old/logwarn-1.0.12/gitrev.c 2016-05-25 04:15:51.000000000 +0200 +++ new/logwarn-1.0.13/gitrev.c 2016-06-19 23:20:36.000000000 +0200 @@ -1 +1 @@ -const char *const logwarn_version = "1.0.12"; +const char *const logwarn_version = "1.0.13"; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logwarn-1.0.12/logwarn.1.in new/logwarn-1.0.13/logwarn.1.in --- old/logwarn-1.0.12/logwarn.1.in 2015-08-25 21:48:15.000000000 +0200 +++ new/logwarn-1.0.13/logwarn.1.in 2016-06-19 22:52:49.000000000 +0200 @@ -33,6 +33,7 @@ .Op Fl M Ar maxprint .Op Fl N Ar maxerrors .Ar logfile +.Op Fl T Ar num/secs .Ar [!]pattern ... .Ek .Pp @@ -268,6 +269,38 @@ When this flag is given, the last one is chosen instead. .Pp This option is appropriate when the suffix is formatted as a timestamp. +.It Fl T +Suppress output until +.Ar num +matching lines appear within a +.Ar secs +second interval. +.Pp +This flag comes after the +.Ar logfile . +Each time it occurs, it applies to all subsequent patterns, or up until the next occurrence. +Use +.Fl T Ar 1/0 +to revert to normal behavior, in which every matching occurrence is reported. +.Pp +The affected patterns are treated as a single group for counting purposes, i.e., +a single counter tracks lines matching any of the patterns. +When the last of +.Ar num +matching lines is seen in a +.Ar secs +second window, it is printed, and the associated tracking is reset, so at least another +.Ar num +lines must be seen before another match is printed. +.Pp +Note that the calculated timing of occurrences is whenever +.Nm +reads the file. +Therefore +.Nm +should be invoked more frequently than your smallest +.Ar secs +time interval, to provide the required time resolution. .It Fl v Output version information and exit. .It Fl z @@ -345,6 +378,17 @@ .Pp Show lines not containing `retrying' but containing `ERROR', as well as any subsequent lines in a multi-line log message, assuming the `myprog: ' prefix marks the start of each new log message. +For example: +.It logwarn /var/log/warn -T 3/60 pat1 -T 10/300 pat2 pat3 +.Pp +Match three or more occurrences of +.Ar pat1 +within a one minute interval, +or ten or more occurrences of either +.Ar pat2 +or +.Ar pat3 +within a five minute interval. .El .Sh RETURN VALUES .Nm diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logwarn-1.0.12/logwarn.h new/logwarn-1.0.13/logwarn.h --- old/logwarn-1.0.12/logwarn.h 2015-08-25 21:47:43.000000000 +0200 +++ new/logwarn-1.0.13/logwarn.h 2016-06-19 22:29:56.000000000 +0200 @@ -20,12 +20,22 @@ // Maximum line length #define MAX_LINE_LENGTH 100000 +// Pattern repeat state +struct repeat { + unsigned int hash; // xor of hashes of pattern string(s) + unsigned int num; // number required in interval + unsigned int secs; // interval duration in seconds + unsigned long *occurrences; // timestamps of up to `num' occurrences, most recent first +}; + // Log scan state struct scan_state { ino_t inode; // file inode number unsigned long line; // # lines read + 1 long pos; // seek position in file unsigned char matching; // within matching entry + unsigned int num_repeats; // number of repeats + struct repeat *repeats; // repeat state }; // Exit values @@ -37,9 +47,11 @@ extern const char *const logwarn_version; // Global functions +extern void reset_state(struct scan_state *state); extern int load_state(const char *state_file, struct scan_state *state); extern void save_state(const char *state_file, const char *logfile, const struct scan_state *state); extern void dump_state(FILE *fp, const char *logfile, const struct scan_state *state); extern void init_state_from_logfile(const char *logfile, struct scan_state *state); extern void state_file_name(const char *state_dir, const char *logfile, char *buf, size_t max); +extern struct repeat *find_repeat(struct scan_state *state, unsigned int hash); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logwarn-1.0.12/main.c new/logwarn-1.0.13/main.c --- old/logwarn-1.0.12/main.c 2015-08-25 21:48:08.000000000 +0200 +++ new/logwarn-1.0.13/main.c 2016-06-19 22:37:01.000000000 +0200 @@ -31,6 +31,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <time.h> #include <unistd.h> #include "logwarn.h" @@ -48,6 +49,7 @@ const char *string; regex_t regex; unsigned char negate; + struct repeat *repeat; }; // Global variables @@ -88,8 +90,17 @@ int ignore_nonexistent = 0; int eflags = 0; int initialize = 0; + int envset; int i; + // Initialize state + memset(&state, 0, sizeof(state)); + state.line = 1; + + // Make getopt() stop at the first non-flag argument + if ((envset = (getenv("POSIXLY_CORRECT") == NULL))) + setenv("POSIXLY_CORRECT", "", 1); + // Parse command line while ((i = getopt(argc, argv, "acd:f:hilL:m:M:N:npqRr:tvz")) != -1) { switch (i) { @@ -165,6 +176,8 @@ exit(EXIT_ERROR); } } + if (envset) + unsetenv("POSIXLY_CORRECT"); if (mpat != NULL) parse_pattern(&log_pattern, mpat, eflags); argv += optind; @@ -185,19 +198,56 @@ exit(EXIT_ERROR); } if (num_match_patterns > 0) { - if ((match_patterns = malloc(num_match_patterns * sizeof(*match_patterns))) == NULL) { + if ((match_patterns = malloc(num_match_patterns * sizeof(*match_patterns))) == NULL + || (state.repeats = malloc(num_match_patterns * sizeof(*state.repeats))) == NULL) { fprintf(stderr, "%s: %s: %s\n", PACKAGE, "malloc", strerror(errno)); exit(EXIT_ERROR); } memset(match_patterns, 0, num_match_patterns * sizeof(*match_patterns)); + memset(state.repeats, 0, num_match_patterns * sizeof(*state.repeats)); for (i = 0; i < num_match_patterns; i++) { struct repat *const pat = &match_patterns[i]; - const char *patstr = argv[i]; + char *patstr = argv[i]; + + // Add new repeat? + if (strcmp(patstr, "-T") == 0) { + struct repeat *const repeat = &state.repeats[state.num_repeats++]; + if (i > num_match_patterns - 3 || sscanf(argv[++i], "%u/%u", &repeat->num, &repeat->secs) != 2) { + usage(); + exit(EXIT_ERROR); + } + if (repeat->num == 0) { + fprintf(stderr, "%s: invalid zero repeat count in \"-T %s\"", PACKAGE, argv[i]); + exit(EXIT_ERROR); + } + if ((repeat->occurrences = malloc(repeat->num * sizeof(repeat->occurrences))) == NULL) { + fprintf(stderr, "%s: %s: %s\n", PACKAGE, "malloc", strerror(errno)); + exit(EXIT_ERROR); + } + memset(repeat->occurrences, 0, repeat->num * sizeof(*repeat->occurrences)); + patstr = argv[++i]; + } + + // Check for negation if (*patstr == '!') { patstr++; pat->negate = 1; } + + // Add (positive) pattern to the current repeat, if any + if (*patstr != '!' && state.num_repeats > 0) { + struct repeat *const current_repeat = &state.repeats[state.num_repeats - 1]; + unsigned int hash = 0; + const char *s; + + for (s = patstr; *s != '\0'; s++) + hash = hash * 37 + (unsigned char)*s; + current_repeat->hash ^= hash; + pat->repeat = current_repeat; + } + + // Parse pattern parse_pattern(pat, patstr, eflags); } } @@ -222,10 +272,6 @@ // Parse rotated file pattern parse_pattern(&rot_pattern, rotpat, 0); - // Initialize state - memset(&state, 0, sizeof(state)); - state.line = 1; - // Check if logfile exists if (logfile != NULL && stat(logfile, &sb) == -1) { switch (errno) { @@ -350,9 +396,6 @@ // Now scan the logfile itself scan_file(logfile, &state); - // Save state - save_state(state_file, logfile, &state); - // Done exit(any_matches ? EXIT_MATCHES : EXIT_OK); } @@ -448,19 +491,15 @@ } // End of file? - if (len == 0 || (len < MAX_LINE_LENGTH - 1 && !newline)) { - save_state(state_file, logfile, state); + if (len == 0 || (len < MAX_LINE_LENGTH - 1 && !newline)) break; - } // Is this a new log entry or a continuation line? continuation = log_pattern.string != NULL && regexec(&log_pattern.regex, line, 0, NULL, 0) != 0; // If this is not a continuation, check if we have reached our limit on the number of errors processed - if (!continuation && error_count >= max_errors_processed) { - save_state(state_file, logfile, state); + if (!continuation && error_count >= max_errors_processed) break; - } // Bump position and number of lines read state->pos += len; @@ -473,8 +512,40 @@ // Determine if this line matches for (i = 0; i < num_match_patterns; i++) { const struct repat *const pat = &match_patterns[i]; + struct repeat *const repeat = pat->repeat; if (regexec(&pat->regex, line, 0, NULL, 0) == 0) { + + // Check for repeat suppression + if (repeat != NULL) { + time_t now; + int count; + + // Update timestamps by adding the current timestamp to the front of the array + time(&now); + memmove(repeat->occurrences + 1, repeat->occurrences, (repeat->num - 1) * sizeof(*repeat->occurrences)); + repeat->occurrences[0] = (unsigned long)now; + + // Check whether the repeat threshold has been exceeded + for (count = 0; count < repeat->num && repeat->occurrences[count] != 0; count++) { + const unsigned int age = repeat->occurrences[0] - repeat->occurrences[count]; + + if (age > repeat->secs) + break; + } + + // If not, treat like a non-matching line + if (count < repeat->num) { + matches = 0; + break; + } + + // If so, reset occurrence history for this pattern group + memset(repeat->occurrences, 0, repeat->num * sizeof(*repeat->occurrences)); + break; + } + + // No repeat suppression matches = pat->negate ? 0 : 1; break; } @@ -512,6 +583,9 @@ } } + // Save updated state + save_state(state_file, logfile, state); + // Free buffer free(line); @@ -547,7 +621,7 @@ usage(void) { fprintf(stderr, "Usage:\n"); - fprintf(stderr, " logwarn [-d dir | -f file] [-m firstpat] [-r sufpat] [-L maxlines] [-M maxprint] [-N maxerrors] [-achlnqpvz] logfile [!]pattern ...\n"); + fprintf(stderr, " logwarn [-d dir | -f file] [-m firstpat] [-r sufpat] [-L maxlines] [-M maxprint] [-N maxerrors] [-achlnqpvz] logfile [-T num/secs] [!]pattern ...\n"); fprintf(stderr, " logwarn [-d dir | -f file] -i logfile\n"); fprintf(stderr, "Options:\n"); fprintf(stderr, " -a Auto-init: force `-i' if no state file exists\n"); @@ -564,6 +638,7 @@ fprintf(stderr, " -n A nonexistent log file is not an error; treat as empty\n"); fprintf(stderr, " -q Don't output the matched log messages\n"); fprintf(stderr, " -r Specify rotated file suffix pattern; default \"%s\"\n", DEFAULT_ROTPAT); + fprintf(stderr, " -T Suppress until `num' occurrences within `secs' seconds"); fprintf(stderr, " -v Output version information and exit\n"); fprintf(stderr, " -z Always read from the beginning of the input\n"); fprintf(stderr, "A logfile of `-' means read from standard input (typically used with `-z')\n"); @@ -573,7 +648,7 @@ version(void) { fprintf(stderr, "%s version %s (%s)\n", PACKAGE, VERSION, logwarn_version); - fprintf(stderr, "Copyright (C) 2010-2013 Archie L. Cobbs\n"); + fprintf(stderr, "Copyright (C) 2010-2016 Archie L. Cobbs\n"); fprintf(stderr, "This is free software; see the source for copying conditions. There is NO\n"); fprintf(stderr, "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logwarn-1.0.12/state.c new/logwarn-1.0.13/state.c --- old/logwarn-1.0.12/state.c 2015-08-25 21:48:06.000000000 +0200 +++ new/logwarn-1.0.13/state.c 2016-06-19 22:30:30.000000000 +0200 @@ -37,6 +37,8 @@ #define LINENUM_NAME "LINENUM" #define POSITION_NAME "POSITION" #define MATCHING_NAME "MATCHING" +#define REPEAT_PREFIX "REPEAT_OCCURRENCES_" +#define REPEAT_PREFIX_LEN (sizeof(REPEAT_PREFIX) - 1) #define STDIN_LOGFILE_NAME "_stdin" int @@ -46,7 +48,7 @@ struct stat sb; FILE *fp; - memset(state, 0, sizeof(*state)); + reset_state(state); state->line = 1; if (stat(state_file, &sb) == -1 || S_ISDIR(sb.st_mode)) return -1; @@ -56,7 +58,7 @@ const char *s = buf; unsigned long value; const char *fname; - const char *fvalue; + char *fvalue; char *eptr; char *t; @@ -78,11 +80,30 @@ continue; *t = '\0'; + // Handle repeat lines + if (strncmp(fname, REPEAT_PREFIX, REPEAT_PREFIX_LEN) == 0) { + struct repeat *repeat; + unsigned int hash; + char *saveptr; + char *token; + int i = 0; + + if (sscanf(fname + REPEAT_PREFIX_LEN, "%x", &hash) != 1) + continue; + if ((repeat = find_repeat(state, hash)) == NULL) + continue; + for (token = strtok_r(fvalue, " ", &saveptr); token != NULL && i < repeat->num; token = strtok_r(NULL, " ", &saveptr)) { + if (sscanf(token, "%lu", &repeat->occurrences[i++]) != 1) + break; + } + continue; + } + // Handle "false" and "true" if (strcmp(fvalue, "false") == 0) - fvalue = "0"; + strcpy(fvalue, "0"); else if (strcmp(fvalue, "true") == 0) - fvalue = "1"; + strcpy(fvalue, "1"); // Decode numerical value if (((value = strtoul(fvalue, &eptr, 10)) == ULONG_MAX && errno == ERANGE) || *eptr != '\0') { @@ -104,6 +125,33 @@ return 0; } +struct repeat * +find_repeat(struct scan_state *state, unsigned int hash) { + int i; + + for (i = 0; i < state->num_repeats; i++) { + struct repeat *const repeat = &state->repeats[i]; + + if (hash == repeat->hash) + return repeat; + } + return NULL; +} + +void +reset_state(struct scan_state *state) +{ + unsigned int num_repeats_save; + struct repeat *repeats_save; + + // Reset state + num_repeats_save = state->num_repeats; + repeats_save = state->repeats; + memset(state, 0, sizeof(*state)); + state->num_repeats = num_repeats_save; + state->repeats = repeats_save; +} + void save_state(const char *state_file, const char *logfile, const struct scan_state *state) { @@ -124,6 +172,7 @@ dump_state(FILE *fp, const char *logfile, const struct scan_state *state) { struct scan_state stdin_state; + int i; if (logfile == NULL) { logfile = STDIN_LOGFILE_NAME; @@ -136,6 +185,20 @@ fprintf(fp, "%s=\"%lu\"\n", LINENUM_NAME, state->line); fprintf(fp, "%s=\"%lu\"\n", POSITION_NAME, state->pos); fprintf(fp, "%s=\"%s\"\n", MATCHING_NAME, state->matching ? "true" : "false"); + for (i = 0; i < state->num_repeats; i++) { + const struct repeat *repeat = &state->repeats[i]; + int j; + + if (repeat->occurrences[0] == 0) + continue; + fprintf(fp, "%s%08x=\"", REPEAT_PREFIX, repeat->hash); + for (j = 0; j < repeat->num && repeat->occurrences[j] != 0; j++) { + if (j > 0) + fputc(' ', fp); + fprintf(fp, "%lu", (unsigned long)repeat->occurrences[j]); + } + fprintf(fp, "\"\n"); + } } void @@ -145,7 +208,8 @@ FILE *fp; int ch; - memset(state, 0, sizeof(*state)); + // Read state file + reset_state(state); state->line = 1; if (logfile == NULL) return;
