Compare commits

...

8 Commits

Author SHA1 Message Date
Dianne Skoll
078dba1e98 Prepare for 05.02.01 release.
All checks were successful
Remind unit tests / tests (push) Successful in 28s
2024-12-16 19:37:31 -05:00
Dianne Skoll
8ebec9584c Fix incorrect comparison code. 2024-12-16 19:33:41 -05:00
Dianne Skoll
2504b39be2 Remove const qualifier.
All checks were successful
Remind unit tests / tests (push) Successful in 32s
2024-12-16 17:10:45 -05:00
Dianne Skoll
e394f402f8 Set release date.
All checks were successful
Remind unit tests / tests (push) Successful in 32s
2024-12-16 09:31:02 -05:00
Dianne Skoll
5a2914f6c7 Start hash tables with 7 buckets instead of 17; print more detailed hash stats with -ds; consistently use ErrFp instead of stderr
All checks were successful
Remind unit tests / tests (push) Successful in 32s
2024-12-14 11:52:16 -05:00
Dianne Skoll
a19b009f7c Fix man page typo and cppcheck warnings.
All checks were successful
Remind unit tests / tests (push) Successful in 32s
2024-12-13 15:22:46 -05:00
Dianne Skoll
6373ae8ca5 Update release notes. 2024-12-13 15:08:54 -05:00
Dianne Skoll
b8c4786b33 Allow INCLUDE/DO/SYSINCLUDE to take a QuotedString argument. This allows for filenames with spaces in them. 2024-12-13 10:38:34 -05:00
22 changed files with 194 additions and 120 deletions

18
configure vendored
View File

@@ -1,6 +1,6 @@
#! /bin/sh #! /bin/sh
# Guess values for system-dependent variables and create Makefiles. # Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.71 for remind 05.02.00. # Generated by GNU Autoconf 2.71 for remind 05.02.01.
# #
# #
# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation, # Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation,
@@ -608,8 +608,8 @@ MAKEFLAGS=
# Identity of this package. # Identity of this package.
PACKAGE_NAME='remind' PACKAGE_NAME='remind'
PACKAGE_TARNAME='remind' PACKAGE_TARNAME='remind'
PACKAGE_VERSION='05.02.00' PACKAGE_VERSION='05.02.01'
PACKAGE_STRING='remind 05.02.00' PACKAGE_STRING='remind 05.02.01'
PACKAGE_BUGREPORT='' PACKAGE_BUGREPORT=''
PACKAGE_URL='https://dianne.skoll.ca/projects/remind/' PACKAGE_URL='https://dianne.skoll.ca/projects/remind/'
@@ -1265,7 +1265,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing. # 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. # This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF cat <<_ACEOF
\`configure' configures remind 05.02.00 to adapt to many kinds of systems. \`configure' configures remind 05.02.01 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]... Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1327,7 +1327,7 @@ fi
if test -n "$ac_init_help"; then if test -n "$ac_init_help"; then
case $ac_init_help in case $ac_init_help in
short | recursive ) echo "Configuration of remind 05.02.00:";; short | recursive ) echo "Configuration of remind 05.02.01:";;
esac esac
cat <<\_ACEOF cat <<\_ACEOF
@@ -1415,7 +1415,7 @@ fi
test -n "$ac_init_help" && exit $ac_status test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then if $ac_init_version; then
cat <<\_ACEOF cat <<\_ACEOF
remind configure 05.02.00 remind configure 05.02.01
generated by GNU Autoconf 2.71 generated by GNU Autoconf 2.71
Copyright (C) 2021 Free Software Foundation, Inc. Copyright (C) 2021 Free Software Foundation, Inc.
@@ -1865,7 +1865,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake. running configure, to aid debugging if configure makes a mistake.
It was created by remind $as_me 05.02.00, which was It was created by remind $as_me 05.02.01, which was
generated by GNU Autoconf 2.71. Invocation command line was generated by GNU Autoconf 2.71. Invocation command line was
$ $0$ac_configure_args_raw $ $0$ac_configure_args_raw
@@ -4710,7 +4710,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their # report actual input values of CONFIG_FILES etc. instead of their
# values after options handling. # values after options handling.
ac_log=" ac_log="
This file was extended by remind $as_me 05.02.00, which was This file was extended by remind $as_me 05.02.01, which was
generated by GNU Autoconf 2.71. Invocation command line was generated by GNU Autoconf 2.71. Invocation command line was
CONFIG_FILES = $CONFIG_FILES CONFIG_FILES = $CONFIG_FILES
@@ -4775,7 +4775,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config='$ac_cs_config_escaped' ac_cs_config='$ac_cs_config_escaped'
ac_cs_version="\\ ac_cs_version="\\
remind config.status 05.02.00 remind config.status 05.02.01
configured by $0, generated by GNU Autoconf 2.71, configured by $0, generated by GNU Autoconf 2.71,
with options \\"\$ac_cs_config\\" with options \\"\$ac_cs_config\\"

View File

@@ -1,6 +1,6 @@
dnl Process this file with autoconf to produce a configure script. dnl Process this file with autoconf to produce a configure script.
AC_INIT(remind, 05.02.00, , , https://dianne.skoll.ca/projects/remind/) AC_INIT(remind, 05.02.01, , , https://dianne.skoll.ca/projects/remind/)
AC_CONFIG_SRCDIR([src/queue.c]) AC_CONFIG_SRCDIR([src/queue.c])
cat <<'EOF' cat <<'EOF'

View File

@@ -1,10 +1,21 @@
CHANGES TO REMIND CHANGES TO REMIND
* VERSION 5.2 Patch 0 - ????-??=?? * VERSION 5.2 Patch 1 - 2024-12-16
- BUG FIX: remind: Fix a logic error that only showed itself on big-endian
architectures. Found thanks to Debian testing and a notification from
Jochen Sprickerhof.
* VERSION 5.2 Patch 0 - 2024-12-16
- MAJOR NEW FEATURE: remind: Add the TRANSLATE command, the _() - MAJOR NEW FEATURE: remind: Add the TRANSLATE command, the _()
built-in function and the %(...) substitution sequence. These allow built-in function and the %(...) substitution sequence. These allow
you to localize your reminder files more easily. you to localize your reminder files more easily. The translation table
is also made available to back-ends like rem2pdf and tkremind,
which they can use as they see fit.
- MINOR FEATURE: tkremind, rem2html: Localize the names of the moon
phases.
- MAJOR CHANGE: remind: Remind used to support compile-time localization - MAJOR CHANGE: remind: Remind used to support compile-time localization
into different languages (French, English, etc.) That compile-time into different languages (French, English, etc.) That compile-time
@@ -27,6 +38,10 @@ CHANGES TO REMIND
INCLUDE [$SysInclude]/foo/bar.rem INCLUDE [$SysInclude]/foo/bar.rem
- MINOR IMPROVEMENT: Allow INCLUDE, DO and SYSINCLUDE to include files with
spaces in their names; in this case, you have to put the filename inside
double-quotes.
- IMPROVEMENT: remind: Refuse to open subdirectories named "*.rem" - IMPROVEMENT: remind: Refuse to open subdirectories named "*.rem"
under a top-level directory rather than trying and failing with a under a top-level directory rather than trying and failing with a
confusing error. confusing error.
@@ -43,8 +58,12 @@ CHANGES TO REMIND
- MINOR FIXES: remind: Fix typos in comments; use memcpy to copy OMIT - MINOR FIXES: remind: Fix typos in comments; use memcpy to copy OMIT
contexts internally. contexts internally.
- BUG FIX: Actually allow the documented 9 levels of INCLUDE rather than - BUG FIX: remind: Actually allow the documented 9 levels of INCLUDE
8. rather than 8.
- BUG FIX: remind: If an INCLUDE statement failed inside an IF statement,
Remind would print spurious errors about unmatched IF/ENDIF. This has
been fixed.
* VERSION 5.1 Patch 1 - 2024-11-18 * VERSION 5.1 Patch 1 - 2024-11-18

View File

@@ -1927,19 +1927,18 @@ commands.
.SH THE DO, INCLUDE AND SYSINCLUDE COMMANDS .SH THE DO, INCLUDE AND SYSINCLUDE COMMANDS
.PP .PP
\fBRemind\fR allows you to include other files in your reminder script, \fBRemind\fR allows you to include other files in your reminder script,
similar to the C preprocessor #include directive. For example, your similar to the C preprocessor #include directive. For example, you
system administrator may maintain a file of holidays or system-wide might organize different reminders into different files like this:
reminders. You can include these in your reminder script as follows:
.PP .PP
.nf .nf
INCLUDE /usr/share/remind/holidays INCLUDE holidays.rem
INCLUDE /usr/share/remind/reminders INCLUDE birthdays.rem
INCLUDE "quote files with spaces.rem"
.fi .fi
.PP .PP
(The actual pathnames vary from system to system - ask your system \fBINCLUDE\fR files can be nested up to a depth of 8. As shown above, if a
administrator.) filename has spaces in it (not recommended!) you can use double-quotes
.PP around the filename.
\fBINCLUDE\fR files can be nested up to a depth of 8.
.PP .PP
If you specify a filename of "-" in the \fBINCLUDE\fR command, \fBRemind\fR If you specify a filename of "-" in the \fBINCLUDE\fR command, \fBRemind\fR
will begin reading from standard input. will begin reading from standard input.
@@ -1983,7 +1982,7 @@ symbolic links to files.
.PP .PP
The \fBSYSINCLUDE\fR command is similar to \fBDO\fR, but it looks for The \fBSYSINCLUDE\fR command is similar to \fBDO\fR, but it looks for
relative pathnames under the system directory containing standard reminder relative pathnames under the system directory containing standard reminder
scripts. For thie version of \fBRemind\fR, the system directory is scripts. For this version of \fBRemind\fR, the system directory is
"@prefix@/share/remind". "@prefix@/share/remind".
.PP .PP
.SH THE RUN COMMAND .SH THE RUN COMMAND

View File

@@ -1272,7 +1272,7 @@ static void PrintLeft(char const *s, int width, char pad)
buf = calloc(len+1, sizeof(wchar_t)); buf = calloc(len+1, sizeof(wchar_t));
if (!buf) { if (!buf) {
/* Uh-oh... cannot recover */ /* Uh-oh... cannot recover */
fprintf(stderr, "%s\n", GetErr(E_NO_MEM)); fprintf(ErrFp, "%s\n", GetErr(E_NO_MEM));
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
@@ -1357,7 +1357,7 @@ static void PrintCentered(char const *s, int width, char *pad)
buf = calloc(len+1, sizeof(wchar_t)); buf = calloc(len+1, sizeof(wchar_t));
if (!buf) { if (!buf) {
/* Uh-oh... cannot recover */ /* Uh-oh... cannot recover */
fprintf(stderr, "%s\n", GetErr(E_NO_MEM)); fprintf(ErrFp, "%s\n", GetErr(E_NO_MEM));
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }

View File

@@ -167,17 +167,13 @@ InitDedupeTable(void)
if (hash_table_init(&DedupeTable, if (hash_table_init(&DedupeTable,
offsetof(DedupeEntry, link), offsetof(DedupeEntry, link),
DedupeHashFunc, CompareDedupes) < 0) { DedupeHashFunc, CompareDedupes) < 0) {
fprintf(stderr, "Unable to initialize function hash table: Out of memory. Exiting.\n"); fprintf(ErrFp, "Unable to initialize function hash table: Out of memory. Exiting.\n");
exit(1); exit(1);
} }
} }
void void
get_dedupe_hash_stats(int *total, int *maxlen, double *avglen) dump_dedupe_hash_stats(void)
{ {
struct hash_table_stats s; hash_table_dump_stats(&DedupeTable, ErrFp);
hash_table_get_stats(&DedupeTable, &s);
*total = s.num_entries;
*maxlen = s.max_len;
*avglen = s.avg_len;
} }

View File

@@ -3088,10 +3088,10 @@ int DoCoerce(char type, Value *v)
/***************************************************************/ /***************************************************************/
void print_expr_nodes_stats(void) void print_expr_nodes_stats(void)
{ {
fprintf(stderr, " Expression nodes allocated: %d\n", ExprNodesAllocated); fprintf(ErrFp, " Expression nodes allocated: %d\n", ExprNodesAllocated);
fprintf(stderr, "Expression nodes high-water: %d\n", ExprNodesHighWater); fprintf(ErrFp, "Expression nodes high-water: %d\n", ExprNodesHighWater);
fprintf(stderr, " Expression nodes leaked: %d\n", ExprNodesUsed); fprintf(ErrFp, " Expression nodes leaked: %d\n", ExprNodesUsed);
fprintf(stderr, " Parse level high-water: %d\n", parse_level_high_water); fprintf(ErrFp, " Parse level high-water: %d\n", parse_level_high_water);
} }
/* Return 1 if a value is "true" for its type, 0 if "false" */ /* Return 1 if a value is "true" for its type, 0 if "false" */

View File

@@ -608,7 +608,7 @@ int DoInclude(ParsePtr p, enum TokTypes tok)
DBufInit(&buf); DBufInit(&buf);
DBufInit(&fullname); DBufInit(&fullname);
DBufInit(&path); DBufInit(&path);
if ( (r=ParseToken(p, &buf)) ) return r; if ( (r=ParseTokenOrQuotedString(p, &buf)) ) return r;
e = VerifyEoln(p); e = VerifyEoln(p);
if (e) Eprint("%s", GetErr(e)); if (e) Eprint("%s", GetErr(e));

View File

@@ -3265,11 +3265,11 @@ static int setenv(char const *varname, char const *val, int overwrite)
{ {
static char tzbuf[256]; static char tzbuf[256];
if (strcmp(varname, "TZ")) { if (strcmp(varname, "TZ")) {
fprintf(stderr, "built-in setenv can only be used with TZ\n"); fprintf(ErrFp, "built-in setenv can only be used with TZ\n");
abort(); abort();
} }
if (!overwrite) { if (!overwrite) {
fprintf(stderr, "built-in setenv must have overwrite=1\n"); fprintf(ErrFp, "built-in setenv must have overwrite=1\n");
abort(); abort();
} }
@@ -3287,7 +3287,7 @@ static void unsetenv(char const *varname)
{ {
static char tzbuf[8]; static char tzbuf[8];
if (strcmp(varname, "TZ")) { if (strcmp(varname, "TZ")) {
fprintf(stderr, "built-in unsetenv can only be used with TZ\n"); fprintf(ErrFp, "built-in unsetenv can only be used with TZ\n");
abort(); abort();
} }
sprintf(tzbuf, "%s", varname); sprintf(tzbuf, "%s", varname);

View File

@@ -63,7 +63,7 @@
* These are used as choices for the number of hash buckets in the table * These are used as choices for the number of hash buckets in the table
*/ */
static size_t bucket_choices[] = { static size_t bucket_choices[] = {
17, 37, 79, 163, 331, 673, 1361, 2729, 5471, 10949, 21911, 43853, 87719, 7, 17, 37, 79, 163, 331, 673, 1361, 2729, 5471, 10949, 21911, 43853, 87719,
175447, 350899, 701819, 1403641, 2807303, 5614657, 11229331, 22458671, 175447, 350899, 701819, 1403641, 2807303, 5614657, 11229331, 22458671,
44917381, 89834777, 179669557, 359339171, 718678369, 1437356741 }; 44917381, 89834777, 179669557, 359339171, 718678369, 1437356741 };
@@ -108,6 +108,8 @@ hash_table_init(hash_table *t,
t->hashfunc = hashfunc; t->hashfunc = hashfunc;
t->compare = compare; t->compare = compare;
t->buckets = malloc(sizeof(void *) * bucket_choices[0]); t->buckets = malloc(sizeof(void *) * bucket_choices[0]);
t->num_growths = 0;
t->num_shrinks = 0;
if (!t->buckets) { if (!t->buckets) {
return -1; return -1;
} }
@@ -216,6 +218,11 @@ hash_table_resize(hash_table *t, int dir)
/* Out of memory... just don't resize? */ /* Out of memory... just don't resize? */
return 0; return 0;
} }
if (dir == 1) {
t->num_growths++;
} else {
t->num_shrinks++;
}
for (size_t j=0; j<num_new_buckets; j++) { for (size_t j=0; j<num_new_buckets; j++) {
new_buckets[j] = NULL; new_buckets[j] = NULL;
} }

View File

@@ -29,6 +29,8 @@ struct hash_link {
*/ */
typedef struct { typedef struct {
unsigned int bucket_choice_index; /**< Index into array of possible bucket counts */ unsigned int bucket_choice_index; /**< Index into array of possible bucket counts */
size_t num_growths; /**< How many times have we grown the hash table? */
size_t num_shrinks; /**< How many times have we grown the hash table? */
size_t num_entries; /**< Number of entries in the hash table */ size_t num_entries; /**< Number of entries in the hash table */
size_t hash_link_offset; /**< Offset of the struct hash_link in the container */ size_t hash_link_offset; /**< Offset of the struct hash_link in the container */
void **buckets; /**< Array of buckets */ void **buckets; /**< Array of buckets */
@@ -45,6 +47,8 @@ struct hash_table_stats {
size_t num_nonempty_buckets; /**< Number of non-emptry buckets */ size_t num_nonempty_buckets; /**< Number of non-emptry buckets */
size_t max_len; /**< Length of longest chain in the hash table */ size_t max_len; /**< Length of longest chain in the hash table */
size_t min_len; /**< Length of the shortest chain in the hash table */ size_t min_len; /**< Length of the shortest chain in the hash table */
size_t num_growths; /**< How many times have we grown the hash table? */
size_t num_shrinks; /**< How many times have we grown the hash table? */
double avg_len; /**< Average chain length */ double avg_len; /**< Average chain length */
double avg_nonempty_len; /**< Average chain length of non-empty bucket */ double avg_nonempty_len; /**< Average chain length of non-empty bucket */
double stddev; /**< Standard deviation of chain lengths */ double stddev; /**< Standard deviation of chain lengths */

View File

@@ -33,14 +33,15 @@ hash_table_dump_stats(hash_table *t, FILE *fp)
{ {
struct hash_table_stats stat; struct hash_table_stats stat;
hash_table_get_stats(t, &stat); hash_table_get_stats(t, &stat);
fprintf(fp, "#Entries: %lu\n#Buckets: %lu\n#Non-empty Buckets: %lu\n", fprintf(fp, " Entries: %lu; Buckets: %lu; Non-empty Buckets: %lu\n",
(unsigned long) stat.num_entries, (unsigned long) stat.num_entries,
(unsigned long) stat.num_buckets, (unsigned long) stat.num_buckets,
(unsigned long) stat.num_nonempty_buckets); (unsigned long) stat.num_nonempty_buckets);
fprintf(fp, "Max len: %lu\nMin len: %lu\nAvg len: %.4f\nStd dev: %.4f\nAvg nonempty len: %.4f\n", fprintf(fp, " Maxlen: %lu; Minlen: %lu; Avglen: %.3f; Stddev: %.3f; Avg nonempty len: %.3f\n",
(unsigned long) stat.max_len, (unsigned long) stat.max_len,
(unsigned long) stat.min_len, (unsigned long) stat.min_len,
stat.avg_len, stat.stddev, stat.avg_nonempty_len); stat.avg_len, stat.stddev, stat.avg_nonempty_len);
fprintf(fp, " Growths: %lu; Shrinks: %lu\n", (unsigned long) stat.num_growths, (unsigned long) stat.num_shrinks);
} }
/** /**
@@ -67,6 +68,8 @@ hash_table_get_stats(hash_table *t, struct hash_table_stats *stat)
stat->stddev = 0.0; stat->stddev = 0.0;
stat->num_nonempty_buckets = 0; stat->num_nonempty_buckets = 0;
stat->avg_nonempty_len = 0.0; stat->avg_nonempty_len = 0.0;
stat->num_growths = t->num_growths;
stat->num_shrinks = t->num_shrinks;
double sum = 0.0; double sum = 0.0;
double sumsq = 0.0; double sumsq = 0.0;

View File

@@ -149,7 +149,7 @@ static char const *DefaultFilename(void)
s = getenv("HOME"); s = getenv("HOME");
if (!s) { if (!s) {
fprintf(stderr, "HOME environment variable not set. Unable to determine reminder file.\n"); fprintf(ErrFp, "HOME environment variable not set. Unable to determine reminder file.\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
DBufPuts(&default_filename_buf, s); DBufPuts(&default_filename_buf, s);
@@ -237,7 +237,7 @@ void InitRemind(int argc, char const *argv[])
InvokedAsRem = 1; InvokedAsRem = 1;
} }
} else { } else {
fprintf(stderr, "Invoked with a NULL argv[0]; bailing because that's just plain bizarre.\n"); fprintf(ErrFp, "Invoked with a NULL argv[0]; bailing because that's just plain bizarre.\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@@ -577,7 +577,7 @@ void InitRemind(int argc, char const *argv[])
/* -wt means get width from /dev/tty */ /* -wt means get width from /dev/tty */
ttyfd = open("/dev/tty", O_RDONLY); ttyfd = open("/dev/tty", O_RDONLY);
if (ttyfd < 0) { if (ttyfd < 0) {
fprintf(stderr, "%s: `-wt': Cannot open /dev/tty: %s\n", fprintf(ErrFp, "%s: `-wt': Cannot open /dev/tty: %s\n",
argv[0], strerror(errno)); argv[0], strerror(errno));
} else { } else {
InitCalWidthAndFormWidth(ttyfd); InitCalWidthAndFormWidth(ttyfd);
@@ -728,7 +728,7 @@ void InitRemind(int argc, char const *argv[])
default: default:
if (tok.type == T_Illegal && tok.val < 0) { if (tok.type == T_Illegal && tok.val < 0) {
fprintf(stderr, "%s: `%s'\n", GetErr(-tok.val), arg); fprintf(ErrFp, "%s: `%s'\n", GetErr(-tok.val), arg);
Usage(); Usage();
} }
Usage(); Usage();
@@ -1018,7 +1018,7 @@ AddTrustedUser(char const *username)
{ {
struct passwd *pwent; struct passwd *pwent;
if (NumTrustedUsers >= MAX_TRUSTED_USERS) { if (NumTrustedUsers >= MAX_TRUSTED_USERS) {
fprintf(stderr, "Too many trusted users (%d max)\n", fprintf(ErrFp, "Too many trusted users (%d max)\n",
MAX_TRUSTED_USERS); MAX_TRUSTED_USERS);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }

View File

@@ -60,19 +60,21 @@ exitfunc(void)
/* Kill any execution-time-limiter process */ /* Kill any execution-time-limiter process */
unlimit_execution_time(); unlimit_execution_time();
int maxlen, total;
double avglen;
if (DebugFlag & DB_PARSE_EXPR) { if (DebugFlag & DB_PARSE_EXPR) {
fflush(stdout); fflush(stdout);
fflush(stderr); fflush(ErrFp);
get_var_hash_stats(&total, &maxlen, &avglen); fprintf(ErrFp, "Variable hash table statistics:\n");
fprintf(stderr, " Var hash: total = %d; maxlen = %d; avglen = %.3f\n", total, maxlen, avglen); dump_var_hash_stats();
get_userfunc_hash_stats(&total, &maxlen, &avglen);
fprintf(stderr, " Func hash: total = %d; maxlen = %d; avglen = %.3f\n", total, maxlen, avglen); fprintf(ErrFp, "Function hash table statistics:\n");
get_dedupe_hash_stats(&total, &maxlen, &avglen); dump_userfunc_hash_stats();
fprintf(stderr, "Dedup hash: total = %d; maxlen = %d; avglen = %.3f\n", total, maxlen, avglen);
get_translation_hash_stats(&total, &maxlen, &avglen); fprintf(ErrFp, "Dedupe hash table statistics:\n");
fprintf(stderr, "Trans hash: total = %d; maxlen = %d; avglen = %.3f\n", total, maxlen, avglen); dump_dedupe_hash_stats();
fprintf(ErrFp, "Translation hash table statistics:\n");
dump_translation_hash_stats();
UnsetAllUserFuncs(); UnsetAllUserFuncs();
print_expr_nodes_stats(); print_expr_nodes_stats();
} }
@@ -128,7 +130,7 @@ int main(int argc, char *argv[])
sigemptyset(&act.sa_mask); sigemptyset(&act.sa_mask);
act.sa_flags = SA_RESTART; act.sa_flags = SA_RESTART;
if (sigaction(SIGALRM, &act, NULL) < 0) { if (sigaction(SIGALRM, &act, NULL) < 0) {
fprintf(stderr, "%s: sigaction() failed: %s\n", fprintf(ErrFp, "%s: sigaction() failed: %s\n",
argv[0], strerror(errno)); argv[0], strerror(errno));
exit(1); exit(1);
} }
@@ -137,7 +139,7 @@ int main(int argc, char *argv[])
act.sa_flags = SA_RESTART; act.sa_flags = SA_RESTART;
sigemptyset(&act.sa_mask); sigemptyset(&act.sa_mask);
if (sigaction(SIGXCPU, &act, NULL) < 0) { if (sigaction(SIGXCPU, &act, NULL) < 0) {
fprintf(stderr, "%s: sigaction() failed: %s\n", fprintf(ErrFp, "%s: sigaction() failed: %s\n",
argv[0], strerror(errno)); argv[0], strerror(errno));
exit(1); exit(1);
} }
@@ -600,6 +602,24 @@ int ParseNonSpaceChar(ParsePtr p, int *err, int peek)
return ch; return ch;
} }
/***************************************************************/
/* */
/* ParseTokenOrQuotedString */
/* */
/* Parse either a token or a double-quote-delimited string. */
/* */
/***************************************************************/
int ParseTokenOrQuotedString(ParsePtr p, DynamicBuffer *dbuf)
{
int c, err;
c = ParseNonSpaceChar(p, &err, 1);
if (err) return err;
if (c != '"') {
return ParseToken(p, dbuf);
}
return ParseQuotedString(p, dbuf);
}
/***************************************************************/ /***************************************************************/
/* */ /* */
/* ParseQuotedString */ /* ParseQuotedString */
@@ -1980,7 +2000,7 @@ get_day_name(int wkday)
if (wkday < 0 || wkday > 6) { if (wkday < 0 || wkday > 6) {
return "INVALID_WKDAY"; return "INVALID_WKDAY";
} }
return t(DayName[wkday]); return tr(DayName[wkday]);
} }
char const * char const *
@@ -1989,7 +2009,7 @@ get_month_name(int mon)
if (mon < 0 || mon > 11) { if (mon < 0 || mon > 11) {
return "INVALID_MON"; return "INVALID_MON";
} }
return t(MonthName[mon]); return tr(MonthName[mon]);
} }
static int GetOnceDateFromFile(void) static int GetOnceDateFromFile(void)

View File

@@ -88,6 +88,7 @@ int JulianToGregorianOffset(int y, int m);
int ParseChar (ParsePtr p, int *err, int peek); int ParseChar (ParsePtr p, int *err, int peek);
int ParseToken (ParsePtr p, DynamicBuffer *dbuf); int ParseToken (ParsePtr p, DynamicBuffer *dbuf);
int ParseQuotedString (ParsePtr p, DynamicBuffer *dbuf); int ParseQuotedString (ParsePtr p, DynamicBuffer *dbuf);
int ParseTokenOrQuotedString (ParsePtr p, DynamicBuffer *dbuf);
int ParseIdentifier (ParsePtr p, DynamicBuffer *dbuf); int ParseIdentifier (ParsePtr p, DynamicBuffer *dbuf);
expr_node * ParseExpr(ParsePtr p, int *r); expr_node * ParseExpr(ParsePtr p, int *r);
void print_expr_nodes_stats(void); void print_expr_nodes_stats(void);
@@ -255,10 +256,10 @@ void print_builtinfunc_tokens(void);
void print_remind_tokens(void); void print_remind_tokens(void);
/* Stats for -ds output */ /* Stats for -ds output */
void get_var_hash_stats(int *total, int *maxlen, double *avglen); void dump_var_hash_stats(void);
void get_userfunc_hash_stats(int *total, int *maxlen, double *avglen); void dump_userfunc_hash_stats(void);
void get_dedupe_hash_stats(int *total, int *maxlen, double *avglen); void dump_dedupe_hash_stats(void);
void get_translation_hash_stats(int *total, int *maxlen, double *avglen); void dump_translation_hash_stats(void);
/* Dedupe code */ /* Dedupe code */
int ShouldDedupe(int trigger_date, int trigger_time, char const *body); int ShouldDedupe(int trigger_date, int trigger_time, char const *body);
@@ -271,6 +272,5 @@ void InitTranslationTable(void);
char const *GetTranslatedString(char const *orig); char const *GetTranslatedString(char const *orig);
int GetTranslatedStringTryingVariants(char const *orig, DynamicBuffer *out); int GetTranslatedStringTryingVariants(char const *orig, DynamicBuffer *out);
char const *GetErr(int r); char const *GetErr(int r);
char const *t(char const *s);
char const *tr(char const *s); char const *tr(char const *s);
void print_escaped_string(FILE *fp, char const *s); void print_escaped_string(FILE *fp, char const *s);

View File

@@ -202,7 +202,7 @@ InitTranslationTable(void)
{ {
if (hash_table_init(&TranslationTable, offsetof(XlateItem, link), if (hash_table_init(&TranslationTable, offsetof(XlateItem, link),
HashXlateItem, CompareXlateItems) < 0) { HashXlateItem, CompareXlateItems) < 0) {
fprintf(stderr, "Unable to initialize translation hash table: Out of memory. Exiting.\n"); fprintf(ErrFp, "Unable to initialize translation hash table: Out of memory. Exiting.\n");
exit(1); exit(1);
} }
InsertTranslation("LANGID", "en"); InsertTranslation("LANGID", "en");
@@ -313,7 +313,7 @@ GetTranslatedStringTryingVariants(char const *orig, DynamicBuffer *out)
return 0; return 0;
} }
char const *t(char const *orig) char const *tr(char const *orig)
{ {
char const *n = GetTranslatedString(orig); char const *n = GetTranslatedString(orig);
if (n) { if (n) {
@@ -322,12 +322,6 @@ char const *t(char const *orig)
return orig; return orig;
} }
/* If another "t" is in scope... */
char const *tr(char const *orig)
{
return t(orig);
}
int int
DoTranslate(ParsePtr p) DoTranslate(ParsePtr p)
{ {
@@ -392,12 +386,8 @@ DoTranslate(ParsePtr p)
} }
void void
get_translation_hash_stats(int *total, int *maxlen, double *avglen) dump_translation_hash_stats(void)
{ {
struct hash_table_stats s; hash_table_dump_stats(&TranslationTable, ErrFp);
hash_table_get_stats(&TranslationTable, &s);
*total = s.num_entries;
*maxlen = s.max_len;
*avglen = s.avg_len;
} }

View File

@@ -286,7 +286,7 @@ typedef struct {
char const *name; char const *name;
char modifiable; char modifiable;
int type; int type;
void const *value; void *value;
int min; /* Or const-value */ int min; /* Or const-value */
int max; int max;
} SysVar; } SysVar;

View File

@@ -56,7 +56,7 @@ InitUserFunctions(void)
offsetof(UserFunc, link), offsetof(UserFunc, link),
HashUserFunc, HashUserFunc,
CompareUserFuncs) < 0) { CompareUserFuncs) < 0) {
fprintf(stderr, "Unable to initialize function hash table: Out of memory. Exiting.\n"); fprintf(ErrFp, "Unable to initialize function hash table: Out of memory. Exiting.\n");
exit(1); exit(1);
} }
} }
@@ -506,12 +506,8 @@ RenameUserFunc(char const *oldname, char const *newname)
} }
void void
get_userfunc_hash_stats(int *total, int *maxlen, double *avglen) dump_userfunc_hash_stats(void)
{ {
struct hash_table_stats s; hash_table_dump_stats(&FuncHash, ErrFp);
hash_table_get_stats(&FuncHash, &s);
*total = s.num_entries;
*maxlen = s.max_len;
*avglen = s.avg_len;
} }

View File

@@ -56,7 +56,7 @@ InitVars(void)
{ {
if (hash_table_init(&VHashTbl, offsetof(Var, link), if (hash_table_init(&VHashTbl, offsetof(Var, link),
VarHashFunc, VarCompareFunc) < 0) { VarHashFunc, VarCompareFunc) < 0) {
fprintf(stderr, "Unable to initialize variable hash table: Out of memory. Exiting.\n"); fprintf(ErrFp, "Unable to initialize variable hash table: Out of memory. Exiting.\n");
exit(1); exit(1);
} }
} }
@@ -723,7 +723,7 @@ int DoDump(ParsePtr p)
/* */ /* */
/* DumpVarTable */ /* DumpVarTable */
/* */ /* */
/* Dump the variable table to stderr. */ /* Dump the variable table to ErrFp. */
/* */ /* */
/***************************************************************/ /***************************************************************/
void DumpVarTable(void) void DumpVarTable(void)
@@ -968,7 +968,7 @@ static int SetTranslatableVariable(SysVar *v, Value *value)
static int GetTranslatableVariable(SysVar *v, Value *value) static int GetTranslatableVariable(SysVar *v, Value *value)
{ {
char const *translated = t((char const *) v->value); char const *translated = tr((char const *) v->value);
if (translated) { if (translated) {
value->v.str = StrDup(translated); value->v.str = StrDup(translated);
} else { } else {
@@ -1005,7 +1005,7 @@ static int SetSysVarHelper(SysVar *v, Value *value)
if (v->type == STR_TYPE) { if (v->type == STR_TYPE) {
/* If it's already the same, don't bother doing anything */ /* If it's already the same, don't bother doing anything */
if (!strcmp(value->v.str, (char const *) v->value)) { if (!strcmp(value->v.str, * (char const **) v->value)) {
DestroyValue(*value); DestroyValue(*value);
return OK; return OK;
} }
@@ -1249,11 +1249,7 @@ print_sysvar_tokens(void)
} }
void void
get_var_hash_stats(int *total, int *maxlen, double *avglen) dump_var_hash_stats(void)
{ {
struct hash_table_stats s; hash_table_dump_stats(&VHashTbl, ErrFp);
hash_table_get_stats(&VHashTbl, &s);
*total = s.num_entries;
*maxlen = s.max_len;
*avglen = s.avg_len;
} }

View File

@@ -1047,7 +1047,7 @@ set a057 value("a05"+"6")
"a05" + "6" => "a056" "a05" + "6" => "a056"
value("a056") => "SDFJHSDF KSJDFH KJSDFH KSJDFH" value("a056") => "SDFJHSDF KSJDFH KJSDFH KSJDFH"
set a058 version() set a058 version()
version() => "05.02.00" version() => "05.02.01"
set a059 wkday(today()) set a059 wkday(today())
today() => 1991-02-16 today() => 1991-02-16
wkday(1991-02-16) => "Saturday" wkday(1991-02-16) => "Saturday"
@@ -2611,7 +2611,7 @@ a056 "SDFJHSDF KSJDFH KJSDFH KSJDFH"
a007 "1991-02-16" a007 "1991-02-16"
a057 "SDFJHSDF KSJDFH KJSDFH KSJDFH" a057 "SDFJHSDF KSJDFH KJSDFH KSJDFH"
a008 "11:44" a008 "11:44"
a058 "05.02.00" a058 "05.02.01"
a059 "Saturday" a059 "Saturday"
a010 12 a010 12
a060 6 a060 6
@@ -5615,8 +5615,8 @@ REM SATISFY ""
REM SATISFY [version() > "01.00.00"] REM SATISFY [version() > "01.00.00"]
../tests/test.rem(1050): SATISFY: expression has no reference to trigdate() or $T... ../tests/test.rem(1050): SATISFY: expression has no reference to trigdate() or $T...
../tests/test.rem(1050): Trig = Saturday, 16 February, 1991 ../tests/test.rem(1050): Trig = Saturday, 16 February, 1991
version() => "05.02.00" version() => "05.02.01"
"05.02.00" > "01.00.00" => 1 "05.02.01" > "01.00.00" => 1
../tests/test.rem(1050): Trig(satisfied) = Saturday, 16 February, 1991 ../tests/test.rem(1050): Trig(satisfied) = Saturday, 16 February, 1991
REM SATISFY [max(x, max(x, 1, 2, 3), 4, 5, 6) * 5] REM SATISFY [max(x, max(x, 1, 2, 3), 4, 5, 6) * 5]
../tests/test.rem(1051): SATISFY: expression has no reference to trigdate() or $T... ../tests/test.rem(1051): SATISFY: expression has no reference to trigdate() or $T...
@@ -16127,8 +16127,8 @@ TRANSLATE "January" "translated-January"
TRANSLATE "Tuesday" "translated-Tuesday" TRANSLATE "Tuesday" "translated-Tuesday"
TRANSLATE "November" "translated-November" TRANSLATE "November" "translated-November"
TRANSLATE "tomorrow" "translated-Tomorrow" TRANSLATE "tomorrow" "translated-Tomorrow"
TRANSLATE "today" "translated-Today"
TRANSLATE "is" "translated-Is" TRANSLATE "is" "translated-Is"
TRANSLATE "today" "translated-Today"
TRANSLATE "from now" "translated-Fromnow" TRANSLATE "from now" "translated-Fromnow"
TRANSLATE "Friday" "translated-Friday" TRANSLATE "Friday" "translated-Friday"
TRANSLATE "am" "translated-Am" TRANSLATE "am" "translated-Am"
@@ -16139,8 +16139,8 @@ TRANSLATE "pm" "translated-Pm"
TRANSLATE "August" "translated-August" TRANSLATE "August" "translated-August"
TRANSLATE "May" "translated-May" TRANSLATE "May" "translated-May"
TRANSLATE "February" "translated-February" TRANSLATE "February" "translated-February"
TRANSLATE "on" "translated-On"
TRANSLATE "now" "translated-Now" TRANSLATE "now" "translated-Now"
TRANSLATE "on" "translated-On"
$Ago is otherway-Ago $Ago is otherway-Ago
$Am is otherway-Am $Am is otherway-Am
$And is otherway-And $And is otherway-And
@@ -16232,11 +16232,28 @@ IF 1
../tests/test.rem(1435): Can't open file: /non/existent/file/should/not/work/wookie ../tests/test.rem(1435): Can't open file: /non/existent/file/should/not/work/wookie
ENDIF ENDIF
do "with space.rem"
REM MSG D'oh, a file whose name has spaces! [filename()]
D'oh, a file whose name has spaces! ../tests/with space.rem
DEBUG -e DEBUG -e
Var hash: total = 100141; maxlen = 5; avglen = 1.142 Variable hash table statistics:
Func hash: total = 100016; maxlen = 5; avglen = 1.140 Entries: 100141; Buckets: 87719; Non-empty Buckets: 66299
Dedup hash: total = 10000; maxlen = 7; avglen = 1.828 Maxlen: 5; Minlen: 0; Avglen: 1.142; Stddev: 0.878; Avg nonempty len: 1.510
Trans hash: total = 1; maxlen = 1; avglen = 0.059 Growths: 13; Shrinks: 0
Function hash table statistics:
Entries: 100016; Buckets: 87719; Non-empty Buckets: 63572
Maxlen: 5; Minlen: 0; Avglen: 1.140; Stddev: 0.934; Avg nonempty len: 1.573
Growths: 13; Shrinks: 0
Dedupe hash table statistics:
Entries: 10000; Buckets: 5471; Non-empty Buckets: 4752
Maxlen: 7; Minlen: 0; Avglen: 1.828; Stddev: 1.302; Avg nonempty len: 2.104
Growths: 9; Shrinks: 0
Translation hash table statistics:
Entries: 1; Buckets: 7; Non-empty Buckets: 1
Maxlen: 1; Minlen: 0; Avglen: 0.143; Stddev: 0.350; Avg nonempty len: 1.000
Growths: 0; Shrinks: 0
Expression nodes allocated: 300096 Expression nodes allocated: 300096
Expression nodes high-water: 300073 Expression nodes high-water: 300073
Expression nodes leaked: 0 Expression nodes leaked: 0
@@ -23150,7 +23167,7 @@ SECURITY: Won't read world-writable file or directory!
Error reading include_dir/ww: Can't open file Error reading include_dir/ww: Can't open file
SECURITY: Won't read world-writable file or directory! SECURITY: Won't read world-writable file or directory!
Error reading include_dir/ww: No files matching *.rem Error reading include_dir/ww: No files matching *.rem
05.02.00 05.02.01
NOTE JSONQUEUE NOTE JSONQUEUE
[{"priority":2,"eventstart":"VOLATILE","time":"23:59","nexttime":"23:59","tdelta":0,"trep":0,"rundisabled":0,"ntrig":1,"filename":"../tests/queue2.rem","lineno":1,"type":"MSG_TYPE","body":"XXXX"},{"priority":999,"eventstart":"VOLATILE","time":"23:58","nexttime":"23:58","tdelta":0,"trep":0,"rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":5,"type":"MSG_TYPE","body":"quux"},{"priority":42,"eventstart":"VOLATILE","time":"23:57","nexttime":"23:57","tdelta":0,"trep":0,"rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":4,"type":"MSG_TYPE","body":"bar"},{"priority":5000,"eventstart":"VOLATILE","time":"23:56","nexttime":"23:56","tdelta":0,"trep":0,"rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":3,"type":"MSG_TYPE","body":"foo"}] [{"priority":2,"eventstart":"VOLATILE","time":"23:59","nexttime":"23:59","tdelta":0,"trep":0,"rundisabled":0,"ntrig":1,"filename":"../tests/queue2.rem","lineno":1,"type":"MSG_TYPE","body":"XXXX"},{"priority":999,"eventstart":"VOLATILE","time":"23:58","nexttime":"23:58","tdelta":0,"trep":0,"rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":5,"type":"MSG_TYPE","body":"quux"},{"priority":42,"eventstart":"VOLATILE","time":"23:57","nexttime":"23:57","tdelta":0,"trep":0,"rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":4,"type":"MSG_TYPE","body":"bar"},{"priority":5000,"eventstart":"VOLATILE","time":"23:56","nexttime":"23:56","tdelta":0,"trep":0,"rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":3,"type":"MSG_TYPE","body":"foo"}]
NOTE ENDJSONQUEUE NOTE ENDJSONQUEUE
@@ -23702,10 +23719,22 @@ Parsed expression: isany("foo", 1 + 1, 2:00 + 1, '2021-01-01' + 1, '2021-01-01@1
"f" + "oo" => "foo" "f" + "oo" => "foo"
isany("foo", 2, 02:01, 2021-01-02, 2021-01-01@14:01, "foo", ?) => 1 isany("foo", 2, 02:01, 2021-01-02, 2021-01-01@14:01, "foo", ?) => 1
No reminders. No reminders.
Var hash: total = 1; maxlen = 1; avglen = 0.059 Variable hash table statistics:
Func hash: total = 0; maxlen = 0; avglen = 0.000 Entries: 1; Buckets: 7; Non-empty Buckets: 1
Dedup hash: total = 0; maxlen = 0; avglen = 0.000 Maxlen: 1; Minlen: 0; Avglen: 0.143; Stddev: 0.350; Avg nonempty len: 1.000
Trans hash: total = 1; maxlen = 1; avglen = 0.059 Growths: 0; Shrinks: 0
Function hash table statistics:
Entries: 0; Buckets: 7; Non-empty Buckets: 0
Maxlen: 0; Minlen: 0; Avglen: 0.000; Stddev: 0.000; Avg nonempty len: 0.000
Growths: 0; Shrinks: 0
Dedupe hash table statistics:
Entries: 0; Buckets: 7; Non-empty Buckets: 0
Maxlen: 0; Minlen: 0; Avglen: 0.000; Stddev: 0.000; Avg nonempty len: 0.000
Growths: 0; Shrinks: 0
Translation hash table statistics:
Entries: 1; Buckets: 7; Non-empty Buckets: 1
Maxlen: 1; Minlen: 0; Avglen: 0.143; Stddev: 0.350; Avg nonempty len: 1.000
Growths: 0; Shrinks: 0
Expression nodes allocated: 512 Expression nodes allocated: 512
Expression nodes high-water: 499 Expression nodes high-water: 499
Expression nodes leaked: 0 Expression nodes leaked: 0
@@ -24229,10 +24258,22 @@ $Uy
$Was $Was
$Wednesday $Wednesday
No reminders. No reminders.
Var hash: total = 1; maxlen = 1; avglen = 0.059 Variable hash table statistics:
Func hash: total = 1; maxlen = 1; avglen = 0.059 Entries: 1; Buckets: 7; Non-empty Buckets: 1
Dedup hash: total = 0; maxlen = 0; avglen = 0.000 Maxlen: 1; Minlen: 0; Avglen: 0.143; Stddev: 0.350; Avg nonempty len: 1.000
Trans hash: total = 2; maxlen = 1; avglen = 0.118 Growths: 13; Shrinks: 13
Function hash table statistics:
Entries: 1; Buckets: 7; Non-empty Buckets: 1
Maxlen: 1; Minlen: 0; Avglen: 0.143; Stddev: 0.350; Avg nonempty len: 1.000
Growths: 13; Shrinks: 13
Dedupe hash table statistics:
Entries: 0; Buckets: 7; Non-empty Buckets: 0
Maxlen: 0; Minlen: 0; Avglen: 0.000; Stddev: 0.000; Avg nonempty len: 0.000
Growths: 0; Shrinks: 0
Translation hash table statistics:
Entries: 2; Buckets: 7; Non-empty Buckets: 1
Maxlen: 2; Minlen: 0; Avglen: 0.286; Stddev: 0.700; Avg nonempty len: 2.000
Growths: 13; Shrinks: 13
Expression nodes allocated: 300032 Expression nodes allocated: 300032
Expression nodes high-water: 300000 Expression nodes high-water: 300000
Expression nodes leaked: 0 Expression nodes leaked: 0

View File

@@ -1435,6 +1435,8 @@ IF 1
INCLUDE /non/existent/file/should/not/work/wookie INCLUDE /non/existent/file/should/not/work/wookie
ENDIF ENDIF
do "with space.rem"
DEBUG -e DEBUG -e
# Output expression-node stats # Output expression-node stats

1
tests/with space.rem Normal file
View File

@@ -0,0 +1 @@
REM MSG D'oh, a file whose name has spaces! [filename()]