mirror of
https://salsa.debian.org/dskoll/remind.git
synced 2026-05-06 07:37:58 +02:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e0b0d043c6 | |||
| fe4499ab72 | |||
| e50d583659 | |||
| 6b05d772f0 | |||
| 84dd73f023 | |||
| 00dca8b70f | |||
| c4bc145cd9 | |||
| bd614c1cde | |||
| 1446ac0552 | |||
| 26ded447ab | |||
| a4ccb0738e | |||
| 27a1b449bd | |||
| 1443282859 | |||
| 4a2d707654 | |||
| fd2a61928c | |||
| a05d9eefc9 | |||
| 6f230e81bd | |||
| 973019c4c7 | |||
| cb712ad7e7 | |||
| be7c67b6fd |
@@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.71 for remind 05.01.01.
|
||||
# Generated by GNU Autoconf 2.71 for remind 05.02.00.
|
||||
#
|
||||
#
|
||||
# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation,
|
||||
@@ -608,8 +608,8 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='remind'
|
||||
PACKAGE_TARNAME='remind'
|
||||
PACKAGE_VERSION='05.01.01'
|
||||
PACKAGE_STRING='remind 05.01.01'
|
||||
PACKAGE_VERSION='05.02.00'
|
||||
PACKAGE_STRING='remind 05.02.00'
|
||||
PACKAGE_BUGREPORT=''
|
||||
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.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
cat <<_ACEOF
|
||||
\`configure' configures remind 05.01.01 to adapt to many kinds of systems.
|
||||
\`configure' configures remind 05.02.00 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@@ -1327,7 +1327,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of remind 05.01.01:";;
|
||||
short | recursive ) echo "Configuration of remind 05.02.00:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@@ -1415,7 +1415,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
remind configure 05.01.01
|
||||
remind configure 05.02.00
|
||||
generated by GNU Autoconf 2.71
|
||||
|
||||
Copyright (C) 2021 Free Software Foundation, Inc.
|
||||
@@ -1865,7 +1865,7 @@ cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
It was created by remind $as_me 05.01.01, which was
|
||||
It was created by remind $as_me 05.02.00, which was
|
||||
generated by GNU Autoconf 2.71. Invocation command line was
|
||||
|
||||
$ $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
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by remind $as_me 05.01.01, which was
|
||||
This file was extended by remind $as_me 05.02.00, which was
|
||||
generated by GNU Autoconf 2.71. Invocation command line was
|
||||
|
||||
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
|
||||
ac_cs_config='$ac_cs_config_escaped'
|
||||
ac_cs_version="\\
|
||||
remind config.status 05.01.01
|
||||
remind config.status 05.02.00
|
||||
configured by $0, generated by GNU Autoconf 2.71,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
dnl Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_INIT(remind, 05.01.01, , , https://dianne.skoll.ca/projects/remind/)
|
||||
AC_INIT(remind, 05.02.00, , , https://dianne.skoll.ca/projects/remind/)
|
||||
AC_CONFIG_SRCDIR([src/queue.c])
|
||||
|
||||
cat <<'EOF'
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
"NOQUEUE" "OMIT" "OMITFUNC" "ONCE" "POP" "POP-OMIT-CONTEXT" "PRESERVE"
|
||||
"PRIORITY" "PS" "PSFILE" "PUSH" "PUSH-OMIT-CONTEXT" "REM" "RUN"
|
||||
"SATISFY" "SCAN" "SCANFROM" "SCHED" "SECOND" "SET" "SKIP" "SPECIAL"
|
||||
"TAG" "THIRD" "THROUGH" "UNSET" "UNTIL" "WARN")
|
||||
"TAG" "THIRD" "THROUGH" "TRANSLATE" "TRANS" "UNSET" "UNTIL" "WARN")
|
||||
#'(lambda (a b) (> (length a) (length b)))))
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@
|
||||
|
||||
(defconst remind-builtin-functions
|
||||
(sort
|
||||
(list "abs" "access" "adawn" "adusk" "ampm" "ansicolor" "args" "asc"
|
||||
(list "_" "abs" "access" "adawn" "adusk" "ampm" "ansicolor" "args" "asc"
|
||||
"baseyr" "char" "choose" "coerce" "columns" "current" "date"
|
||||
"datepart" "datetime" "dawn" "day" "daysinmon" "defined" "dosubst"
|
||||
"dusk" "easterdate" "evaltrig" "filedate" "filedatetime" "filedir"
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
CHANGES TO REMIND
|
||||
|
||||
* VERSION 5.2 Patch 0 - ????-??=??
|
||||
|
||||
- MAJOR NEW FEATURE: remind: Add the TRANSLATE command and the _()
|
||||
built-in function. This allows you to localize your reminder files
|
||||
more easily.
|
||||
|
||||
- UPDATE: Update national holidays following update to upstream Python
|
||||
library.
|
||||
|
||||
- IMPROVEMENT: remind: Refuse to open subdirectories named "*.rem"
|
||||
under a top-level directory rather than trying and failing with a
|
||||
confusing error.
|
||||
|
||||
- IMPROVEMENT: remind: Remind used to have three completely separate
|
||||
hash table implementations. Replace them all with one piece of code.
|
||||
|
||||
- MINOR FIXES: remind: Fix typos in comments; use memcpy to copy OMIT
|
||||
contexts internally.
|
||||
|
||||
- BUG FIX: Actually allow the documented 9 levels of INCLUDE rather than
|
||||
8.
|
||||
|
||||
* VERSION 5.1 Patch 1 - 2024-11-18
|
||||
|
||||
- BUG FIX: Fix a bug in test-rem that could have caused test failures.
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
REM 1 Feb 2022 MSG Chinese New Year (Tiger)
|
||||
REM 22 Jan 2023 MSG Chinese New Year (Rabbit)
|
||||
REM 10 Feb 2024 MSG Chinese New Year (Dragon)
|
||||
REM 29 Jan 2025 MSG Chinese New Year (Snake)
|
||||
REM 17 Feb 2026 MSG Chinese New Year (Horse)
|
||||
REM 6 Feb 2027 MSG Chinese New Year (Goat)
|
||||
REM 26 Jan 2028 MSG Chinese New Year (Monkey)
|
||||
REM 13 Feb 2029 MSG Chinese New Year (Rooster)
|
||||
REM 3 Feb 2030 MSG Chinese New Year (Dog)
|
||||
REM 23 Jan 2031 MSG Chinese New Year (Pig)
|
||||
REM 11 Feb 2032 MSG Chinese New Year (Rat)
|
||||
REM 31 Jan 2033 MSG Chinese New Year (Ox)
|
||||
REM 19 Feb 2034 MSG Chinese New Year (Tiger)
|
||||
REM 8 Feb 2035 MSG Chinese New Year (Rabbit)
|
||||
REM 28 Jan 2036 MSG Chinese New Year (Dragon)
|
||||
REM 15 Feb 2037 MSG Chinese New Year (Snake)
|
||||
REM 4 Feb 2038 MSG Chinese New Year (Horse)
|
||||
REM 24 Jan 2039 MSG Chinese New Year (Goat)
|
||||
REM 12 Feb 2040 MSG Chinese New Year (Monkey)
|
||||
REM 1 Feb 2041 MSG Chinese New Year (Rooster)
|
||||
REM 22 Jan 2042 MSG Chinese New Year (Dog)
|
||||
REM 10 Feb 2043 MSG Chinese New Year (Pig)
|
||||
REM 30 Jan 2044 MSG Chinese New Year (Rat)
|
||||
REM 17 Feb 2045 MSG Chinese New Year (Ox)
|
||||
REM 6 Feb 2046 MSG Chinese New Year (Tiger)
|
||||
REM 26 Jan 2047 MSG Chinese New Year (Rabbit)
|
||||
REM 14 Feb 2048 MSG Chinese New Year (Dragon)
|
||||
REM 2 Feb 2049 MSG Chinese New Year (Snake)
|
||||
REM 23 Jan 2050 MSG Chinese New Year (Horse)
|
||||
REM 1 Feb 2022 MSG [_("Chinese New Year")] ([_("Tiger")])
|
||||
REM 22 Jan 2023 MSG [_("Chinese New Year")] ([_("Rabbit")])
|
||||
REM 10 Feb 2024 MSG [_("Chinese New Year")] ([_("Dragon")])
|
||||
REM 29 Jan 2025 MSG [_("Chinese New Year")] ([_("Snake")])
|
||||
REM 17 Feb 2026 MSG [_("Chinese New Year")] ([_("Horse")])
|
||||
REM 6 Feb 2027 MSG [_("Chinese New Year")] ([_("Goat")])
|
||||
REM 26 Jan 2028 MSG [_("Chinese New Year")] ([_("Monkey")])
|
||||
REM 13 Feb 2029 MSG [_("Chinese New Year")] ([_("Rooster")])
|
||||
REM 3 Feb 2030 MSG [_("Chinese New Year")] ([_("Dog")])
|
||||
REM 23 Jan 2031 MSG [_("Chinese New Year")] ([_("Pig")])
|
||||
REM 11 Feb 2032 MSG [_("Chinese New Year")] ([_("Rat")])
|
||||
REM 31 Jan 2033 MSG [_("Chinese New Year")] ([_("Ox")])
|
||||
REM 19 Feb 2034 MSG [_("Chinese New Year")] ([_("Tiger")])
|
||||
REM 8 Feb 2035 MSG [_("Chinese New Year")] ([_("Rabbit")])
|
||||
REM 28 Jan 2036 MSG [_("Chinese New Year")] ([_("Dragon")])
|
||||
REM 15 Feb 2037 MSG [_("Chinese New Year")] ([_("Snake")])
|
||||
REM 4 Feb 2038 MSG [_("Chinese New Year")] ([_("Horse")])
|
||||
REM 24 Jan 2039 MSG [_("Chinese New Year")] ([_("Goat")])
|
||||
REM 12 Feb 2040 MSG [_("Chinese New Year")] ([_("Monkey")])
|
||||
REM 1 Feb 2041 MSG [_("Chinese New Year")] ([_("Rooster")])
|
||||
REM 22 Jan 2042 MSG [_("Chinese New Year")] ([_("Dog")])
|
||||
REM 10 Feb 2043 MSG [_("Chinese New Year")] ([_("Pig")])
|
||||
REM 30 Jan 2044 MSG [_("Chinese New Year")] ([_("Rat")])
|
||||
REM 17 Feb 2045 MSG [_("Chinese New Year")] ([_("Ox")])
|
||||
REM 6 Feb 2046 MSG [_("Chinese New Year")] ([_("Tiger")])
|
||||
REM 26 Jan 2047 MSG [_("Chinese New Year")] ([_("Rabbit")])
|
||||
REM 14 Feb 2048 MSG [_("Chinese New Year")] ([_("Dragon")])
|
||||
REM 2 Feb 2049 MSG [_("Chinese New Year")] ([_("Snake")])
|
||||
REM 23 Jan 2050 MSG [_("Chinese New Year")] ([_("Horse")])
|
||||
|
||||
@@ -54,3 +54,24 @@ FSET subst_minutes(m) iif(m==1, "1 minuut", m + " minuten")
|
||||
FSET subst_hours(h) iif(h==1, "1 uur", h + " uren")
|
||||
|
||||
FSET subst_bx(a, d, t) "over " + (d-today()) + " dagen"
|
||||
|
||||
TRANSLATE "New Moon" "Nieuwe maan"
|
||||
TRANSLATE "First Quarter" "Eerste kwartier"
|
||||
TRANSLATE "Full Moon" "Volle maan"
|
||||
TRANSLATE "Last Quarter" "Laatste kwartier"
|
||||
|
||||
TRANSLATE "Vernal Equiniox" "Lente-equinox"
|
||||
TRANSLATE "Summer Solstice" "Zomerzonnewend"
|
||||
TRANSLATE "Autumnal Equinox" "Herfst-equinox"
|
||||
TRANSLATE "Winter Solstice" "Winterzonnewend"
|
||||
|
||||
TRANSLATE "Chinese New Year" "Chinees Nieuwjaar"
|
||||
TRANSLATE "Snake" "Slang"
|
||||
TRANSLATE "Horse" "Paard"
|
||||
TRANSLATE "Goat" "Geit"
|
||||
TRANSLATE "Monkey" "Aap"
|
||||
TRANSLATE "Rooster" "Haan"
|
||||
TRANSLATE "Dog" "Hond"
|
||||
TRANSLATE "Pig" "Varken"
|
||||
TRANSLATE "Rat" "Rat"
|
||||
TRANSLATE "Ox" "Os"
|
||||
|
||||
@@ -7,8 +7,8 @@ IF $CalMode || $PsCal
|
||||
REM [moondate(2)] SPECIAL MOON 2 -1 -1 [moontime(2)]
|
||||
REM [moondate(3)] SPECIAL MOON 3 -1 -1 [moontime(3)]
|
||||
ELSE
|
||||
REM NOQUEUE [moondatetime(0)] MSG New Moon (%2)
|
||||
REM NOQUEUE [moondatetime(1)] MSG First Quarter (%2)
|
||||
REM NOQUEUE [moondatetime(2)] MSG Full Moon (%2)
|
||||
REM NOQUEUE [moondatetime(3)] MSG Last Quarter (%2)
|
||||
REM NOQUEUE [moondatetime(0)] MSG [_("New Moon")] (%2)
|
||||
REM NOQUEUE [moondatetime(1)] MSG [_("First Quarter")] (%2)
|
||||
REM NOQUEUE [moondatetime(2)] MSG [_("Full Moon")] (%2)
|
||||
REM NOQUEUE [moondatetime(3)] MSG [_("Last Quarter")] (%2)
|
||||
ENDIF
|
||||
|
||||
+8
-8
@@ -3,14 +3,14 @@
|
||||
|
||||
IF $LatDeg >= 0
|
||||
# Northern Hemisphere
|
||||
REM NOQUEUE [soleq(0)] MSG %"Vernal Equinox%" is %3.
|
||||
REM NOQUEUE [soleq(1)] MSG %"Summer Solstice%" is %3.
|
||||
REM NOQUEUE [soleq(2)] MSG %"Autumnal Equinox%" is %3.
|
||||
REM NOQUEUE [soleq(3)] MSG %"Winter Solstice%" is %3.
|
||||
REM NOQUEUE [soleq(0)] MSG %"[_("Vernal Equinox")]%" [$Is] %3.
|
||||
REM NOQUEUE [soleq(1)] MSG %"[_("Summer Solstice")]%" [$Is] %3.
|
||||
REM NOQUEUE [soleq(2)] MSG %"[_("Autumnal Equinox")]%" [$Is] %3.
|
||||
REM NOQUEUE [soleq(3)] MSG %"[_("Winter Solstice")]%" [$Is] %3.
|
||||
ELSE
|
||||
# Southern Hemisphere
|
||||
REM NOQUEUE [soleq(0)] MSG %"Autumnal Equinox%" is %3.
|
||||
REM NOQUEUE [soleq(1)] MSG %"Winter Solstice%" is %3.
|
||||
REM NOQUEUE [soleq(2)] MSG %"Vernal Equinox%" is %3.
|
||||
REM NOQUEUE [soleq(3)] MSG %"Summer Solstice%" is %3.
|
||||
REM NOQUEUE [soleq(0)] MSG %"[_("Autumnal Equinox")]%" [$Is] %3.
|
||||
REM NOQUEUE [soleq(1)] MSG %"[_("Winter Solstice")]%" [$Is] %3.
|
||||
REM NOQUEUE [soleq(2)] MSG %"[_("Vernal Equinox")]%" [$Is] %3.
|
||||
REM NOQUEUE [soleq(3)] MSG %"[_("Summer Solstice")]%" [$Is] %3.
|
||||
ENDIF
|
||||
|
||||
+69
-4
@@ -1196,7 +1196,7 @@ The command:
|
||||
REM ... whatever ... ADDOMIT MSG Foo
|
||||
.fi
|
||||
.PP
|
||||
is identical in behaviour to the sequence:
|
||||
is identical in behavior to the sequence:
|
||||
.PP
|
||||
.nf
|
||||
REM ... whatever ... SATISFY 1
|
||||
@@ -2039,7 +2039,7 @@ Note that if RUN is disabled, then INCLUDECMD will fail with the error
|
||||
message "RUN disabled"
|
||||
.PP
|
||||
INCLUDECMD passes the rest of the line to \fBpopen\fR(3), meaning that
|
||||
the command is executed by the shell. As such, shell metacharacters
|
||||
the command is executed by the shell. As such, shell meta-characters
|
||||
may need escaping or arguments quoting, depending on what you're trying
|
||||
to do. Remind itself does not perform any modification of the command
|
||||
line (apart from the normal [expr] expression-pasting mechanism).
|
||||
@@ -2767,7 +2767,7 @@ rules apply to \fB$Latitude\fR, \fB$LatDeg\fR, \fB$LatMin\fR and \fB$LatSec\fR.
|
||||
This variable controls how \fBRemind\fR reacts to a computer being suspended
|
||||
and then woken. Normally, if a timed reminder is queued and then the
|
||||
computer suspended, and then the computer is woken \fIafter\fR the
|
||||
timed reminder's trigger time, \fBRemind\fR will triger the timer anyway,
|
||||
timed reminder's trigger time, \fBRemind\fR will trigger the timer anyway,
|
||||
despite the fact that the trigger time has already passed.
|
||||
.RS
|
||||
.PP
|
||||
@@ -3018,6 +3018,22 @@ an underscore and an identifier naming the argument.
|
||||
.PP
|
||||
The built-in functions are:
|
||||
.TP
|
||||
.B _(s_message)
|
||||
Returns the translation table entry for \fImessage\fR. If there is no
|
||||
such translation table entry, then returns \fImessage\fR unmodified.
|
||||
For example, consider this sequence:
|
||||
.RS
|
||||
.PP
|
||||
.nf
|
||||
TRANSLATE "Goodbye" "Tot ziens"
|
||||
SET a _("Goodbye")
|
||||
.fi
|
||||
.PP
|
||||
After those two lines have been executed, the variable \fBa\fR will be
|
||||
set to "Tot ziens". See the section THE TRANSLATION TABLE for more
|
||||
information.
|
||||
.RE
|
||||
.TP
|
||||
.B abs(i_num)
|
||||
Returns the absolute value of \fInum\fR.
|
||||
.TP
|
||||
@@ -5206,6 +5222,55 @@ You can also define a function on the command line by using:
|
||||
\fB\-i\fR\fIfunc\fR(\fIargs\fR)=\fIdefinition\fR
|
||||
.PP
|
||||
Be sure to protect special characters from shell interpretation.
|
||||
.SH THE TRANSLATION TABLE
|
||||
.PP
|
||||
To assist with localizing reminder files, \fBRemind\fR maintains a
|
||||
table of translations. This is simple a lookup table that maps one
|
||||
string (the original string) to a new string (the translated string.)
|
||||
When \fBRemind\fR starts executing, the translation table is empty.
|
||||
.PP
|
||||
To add a message to the translation table, use the \fBTRANSLATE\fR
|
||||
command (which may be abbreviated to \fBTRANS\fR.) The \fBTRANSLATE\fR
|
||||
command must be followed by two quoted strings, separated from each
|
||||
other and from the command by whitespace. For example, a Dutch
|
||||
language file might contain something like this:
|
||||
.PP
|
||||
.nf
|
||||
TRANSLATE "New Moon" "Nieuwe maan"
|
||||
TRANSLATE "First Quarter" "Eerste kwartier"
|
||||
TRANSLATE "Full Moon" "Volle maan"
|
||||
TRANSLATE "Last Quarter" "Laatste kwartier"
|
||||
.fi
|
||||
.PP
|
||||
To actually use the translation table, make use of the \fB_\fR built-in
|
||||
function, as follows:
|
||||
.PP
|
||||
.nf
|
||||
REM NOQUEUE [moondatetime(0)] MSG [_("New Moon")] (%2)
|
||||
REM NOQUEUE [moondatetime(1)] MSG [_("First Quarter")] (%2)
|
||||
REM NOQUEUE [moondatetime(2)] MSG [_("Full Moon")] (%2)
|
||||
REM NOQUEUE [moondatetime(3)] MSG [_("Last Quarter")] (%2)
|
||||
.fi
|
||||
.PP
|
||||
By using \fBTRANSLATE\fR and \fB_\fR judiciously, you can make your
|
||||
reminder files easy to translate.
|
||||
.PP
|
||||
\fBTRANSLATE\fR has three additional forms: If it is followed
|
||||
by \fIone\fR quoted string instead of two, then \fBRemind\fR deletes the
|
||||
translation table entry for that string. If it is followed by
|
||||
the keyword \fBDUMP\fR, then \fBRemind\fR dumps all translation table entries
|
||||
to standard output. And if it is followed by \fBCLEAR\fR, then
|
||||
\fBRemind\fR deletes all of the translation table entries.
|
||||
.PP
|
||||
Note that if you \fBSET\fR various translation-related system
|
||||
variables such as \fB$Monday\fR, \fB$December\fR, \fB$Ago\fR, etc,
|
||||
then \fBRemind\fR \fIalso\fR makes a corresponding translation
|
||||
table entry automatically. This is done for all of the translation-related
|
||||
system variables \fIexcept for\fR \fB$Hplu\fR and \fB$Mplu\fR.
|
||||
.PP
|
||||
The converse does not apply; creating a translation table entry for
|
||||
"December" does not automatically set \fB$December\fR.
|
||||
|
||||
.SH MORE ABOUT POSTSCRIPT
|
||||
.PP
|
||||
The \fBPS\fR and \fBPSFILE\fR reminders pass PostScript code directly
|
||||
@@ -5552,7 +5617,7 @@ day's name in your language. Strings must be valid UTF-8 strings.
|
||||
Set each of these system variables to a string representing the corresponding
|
||||
month's name in your language. Strings must be valid UTF-8 strings.
|
||||
.TP
|
||||
.B $Ago, $Am, $And, $At, $Hour, $Is, $Minute, $Now, $On, $Pm, $Was
|
||||
.B $Ago, $Am, $And, $At, $Hour, $Is, $Minute, $Now, $On, $Pm, $Today, $Tomorrow, $Was
|
||||
Set each of these system variables to the translation of the corresponding
|
||||
English word into your language. Note that \fB$Am\fR and \fB$Pm\fR should
|
||||
be the translations of "AM" and "PM" (morning and afternoon time indicators)
|
||||
|
||||
+5
-5
@@ -27,12 +27,12 @@ MANS= $(srcdir)/../man/rem2ps.1 $(srcdir)/../man/remind.1 \
|
||||
.SUFFIXES: .c .o
|
||||
|
||||
REMINDSRCS= calendar.c dedupe.c dynbuf.c dorem.c dosubst.c expr.c \
|
||||
files.c funcs.c globals.c hbcal.c init.c main.c md5.c \
|
||||
moon.c omit.c queue.c sort.c token.c trigger.c \
|
||||
userfns.c utils.c var.c
|
||||
files.c funcs.c globals.c hashtab.c hashtab_stats.c \
|
||||
hbcal.c init.c main.c md5.c moon.c omit.c queue.c \
|
||||
sort.c token.c trans.c trigger.c userfns.c utils.c var.c
|
||||
|
||||
REMINDHDRS=config.h custom.h dynbuf.h err.h globals.h lang.h \
|
||||
md5.h protos.h rem2ps.h types.h version.h
|
||||
REMINDHDRS=config.h custom.h dynbuf.h err.h globals.h hashtab.h \
|
||||
lang.h md5.h protos.h rem2ps.h types.h version.h
|
||||
REMINDOBJS= $(REMINDSRCS:.c=.o)
|
||||
|
||||
all: remind rem2ps
|
||||
|
||||
@@ -1711,6 +1711,7 @@ static void GenerateCalEntries(int col)
|
||||
case T_Push: r=PushOmitContext(&p); break;
|
||||
case T_Preserve: r=DoPreserve(&p); break;
|
||||
case T_Expr: r = DoExpr(&p); break;
|
||||
case T_Translate: r = DoTranslate(&p); break;
|
||||
case T_RemType: if (tok.val == RUN_TYPE) {
|
||||
r=DoRun(&p);
|
||||
break;
|
||||
|
||||
+51
-67
@@ -16,16 +16,37 @@
|
||||
#include "protos.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define DEDUPE_HASH_SLOTS 31
|
||||
typedef struct dedupe_entry {
|
||||
struct dedupe_entry *next;
|
||||
struct hash_link link;
|
||||
int trigger_date;
|
||||
int trigger_time;
|
||||
char const *body;
|
||||
} DedupeEntry;
|
||||
|
||||
static DedupeEntry *DedupeTable[DEDUPE_HASH_SLOTS];
|
||||
static hash_table DedupeTable;
|
||||
|
||||
static unsigned int DedupeHashFunc(void *x)
|
||||
{
|
||||
DedupeEntry *e = (DedupeEntry *) x;
|
||||
unsigned int hashval = (unsigned int) e->trigger_date;
|
||||
if (e->trigger_time != NO_TIME) {
|
||||
hashval += (unsigned int) e->trigger_time;
|
||||
}
|
||||
hashval += HashVal_preservecase(e->body);
|
||||
return hashval;
|
||||
}
|
||||
|
||||
static int CompareDedupes(void *x, void *y)
|
||||
{
|
||||
DedupeEntry *a = (DedupeEntry *) x;
|
||||
DedupeEntry *b = (DedupeEntry *) y;
|
||||
if (a->trigger_date != b->trigger_date) return a->trigger_date - b->trigger_date;
|
||||
if (a->trigger_time != b->trigger_time) return a->trigger_time - b->trigger_time;
|
||||
return strcmp(a->body, b->body);
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
@@ -43,24 +64,6 @@ FreeDedupeEntry(DedupeEntry *e)
|
||||
free(e);
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* GetDedupeBucket */
|
||||
/* */
|
||||
/* Get the bucket for a given date and body */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
static unsigned int
|
||||
GetDedupeBucket(int trigger_date, int trigger_time, char const *body)
|
||||
{
|
||||
unsigned int bucket = trigger_date;
|
||||
if (trigger_time != NO_TIME) {
|
||||
bucket += trigger_time;
|
||||
}
|
||||
bucket += HashVal(body);
|
||||
return bucket % DEDUPE_HASH_SLOTS;
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* FindDedupeEntry */
|
||||
@@ -72,19 +75,12 @@ static DedupeEntry *
|
||||
FindDedupeEntry(int trigger_date, int trigger_time, char const *body)
|
||||
{
|
||||
DedupeEntry *e;
|
||||
|
||||
unsigned int bucket = GetDedupeBucket(trigger_date, trigger_time, body);
|
||||
|
||||
e = DedupeTable[bucket];
|
||||
while(e) {
|
||||
if (e->trigger_date == trigger_date &&
|
||||
e->trigger_time == trigger_time &&
|
||||
!strcmp(body, e->body)) {
|
||||
return e;
|
||||
}
|
||||
e = e->next;
|
||||
}
|
||||
return NULL;
|
||||
DedupeEntry candidate;
|
||||
candidate.body = body;
|
||||
candidate.trigger_date = trigger_date;
|
||||
candidate.trigger_time = trigger_time;
|
||||
e = hash_table_find(&DedupeTable, &candidate);
|
||||
return e;
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
@@ -99,8 +95,6 @@ InsertDedupeEntry(int trigger_date, int trigger_time, char const *body)
|
||||
{
|
||||
DedupeEntry *e;
|
||||
|
||||
unsigned int bucket = GetDedupeBucket(trigger_date, trigger_time, body);
|
||||
|
||||
e = malloc(sizeof(DedupeEntry));
|
||||
if (!e) {
|
||||
return; /* No error checking... what can we do? */
|
||||
@@ -113,8 +107,7 @@ InsertDedupeEntry(int trigger_date, int trigger_time, char const *body)
|
||||
return;
|
||||
}
|
||||
|
||||
e->next = DedupeTable[bucket];
|
||||
DedupeTable[bucket] = e;
|
||||
hash_table_insert(&DedupeTable, e);
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
@@ -149,16 +142,18 @@ void
|
||||
ClearDedupeTable(void)
|
||||
{
|
||||
DedupeEntry *e, *next;
|
||||
for (int i=0; i<DEDUPE_HASH_SLOTS; i++) {
|
||||
e = DedupeTable[i];
|
||||
while (e) {
|
||||
next = e->next;
|
||||
FreeDedupeEntry(e);
|
||||
e = next;
|
||||
}
|
||||
DedupeTable[i] = NULL;
|
||||
|
||||
e = hash_table_next(&DedupeTable, NULL);
|
||||
while(e) {
|
||||
next = hash_table_next(&DedupeTable, e);
|
||||
hash_table_delete_no_resize(&DedupeTable, e);
|
||||
FreeDedupeEntry(e);
|
||||
e = next;
|
||||
}
|
||||
hash_table_free(&DedupeTable);
|
||||
InitDedupeTable();
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* InitDedupeTable */
|
||||
@@ -169,31 +164,20 @@ ClearDedupeTable(void)
|
||||
void
|
||||
InitDedupeTable(void)
|
||||
{
|
||||
for (int i=0; i<DEDUPE_HASH_SLOTS; i++) {
|
||||
DedupeTable[i] = NULL;
|
||||
if (hash_table_init(&DedupeTable,
|
||||
offsetof(DedupeEntry, link),
|
||||
DedupeHashFunc, CompareDedupes) < 0) {
|
||||
fprintf(stderr, "Unable to initialize function hash table: Out of memory. Exiting.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
get_dedupe_hash_stats(int *total, int *maxlen, double *avglen)
|
||||
{
|
||||
int len;
|
||||
int i;
|
||||
DedupeEntry *e;
|
||||
|
||||
*maxlen = 0;
|
||||
*total = 0;
|
||||
for (i=0; i<DEDUPE_HASH_SLOTS; i++) {
|
||||
len = 0;
|
||||
e = DedupeTable[i];
|
||||
while (e) {
|
||||
len++;
|
||||
(*total)++;
|
||||
e = e->next;
|
||||
}
|
||||
if (len > *maxlen) {
|
||||
*maxlen = len;
|
||||
}
|
||||
}
|
||||
*avglen = (double) *total / (double) DEDUPE_HASH_SLOTS;
|
||||
struct hash_table_stats s;
|
||||
hash_table_get_stats(&DedupeTable, &s);
|
||||
*total = s.num_entries;
|
||||
*maxlen = s.max_len;
|
||||
*avglen = s.avg_len;
|
||||
}
|
||||
|
||||
+1
-1
@@ -2019,7 +2019,7 @@ static int make_atom(expr_node *atom, Var *locals)
|
||||
atom->u.arg = i;
|
||||
return OK;
|
||||
}
|
||||
v = v->next;
|
||||
v = v->link.next;
|
||||
i++;
|
||||
}
|
||||
if (strlen(s) < SHORT_NAME_BUF) {
|
||||
|
||||
+19
-1
@@ -68,6 +68,7 @@ static int
|
||||
solstice_equinox_for_year(int y, int which);
|
||||
|
||||
/* Function prototypes */
|
||||
static int F_ (func_info *);
|
||||
static int FADawn (func_info *);
|
||||
static int FADusk (func_info *);
|
||||
static int FAbs (func_info *);
|
||||
@@ -226,7 +227,7 @@ static int CacheHebYear, CacheHebMon, CacheHebDay;
|
||||
/* The array holding the built-in functions. */
|
||||
BuiltinFunc Func[] = {
|
||||
/* Name minargs maxargs is_constant func newfunc*/
|
||||
|
||||
{ "_", 1, 1, 0, F_, NULL },
|
||||
{ "abs", 1, 1, 1, FAbs, NULL },
|
||||
{ "access", 2, 2, 0, FAccess, NULL },
|
||||
{ "adawn", 0, 1, 0, FADawn, NULL},
|
||||
@@ -372,6 +373,23 @@ static int RetStrVal(char const *s, func_info *info)
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* F_ - look up a string in the translation table */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
static int F_(func_info *info)
|
||||
{
|
||||
char const *translated;
|
||||
ASSERT_TYPE(0, STR_TYPE);
|
||||
translated = GetTranslatedString(ARGSTR(0));
|
||||
if (!translated) {
|
||||
DCOPYVAL(RetVal, ARG(0));
|
||||
return OK;
|
||||
}
|
||||
return RetStrVal(translated, info);
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* FStrlen - string length */
|
||||
|
||||
+450
@@ -0,0 +1,450 @@
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* HASHTAB_STATS.C */
|
||||
/* */
|
||||
/* Implementation of hash table. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
|
||||
/**
|
||||
* \file hashtab.c
|
||||
*
|
||||
* \brief Implementation of hash table
|
||||
*
|
||||
* A hash table manages an array of buckets, each of which is the
|
||||
* head of a singly-linked list. A given hash table can store items
|
||||
* of a given type. The items in a hash table must be structs, and one
|
||||
* of their members must be a struct hash_link object. For example,
|
||||
* a hash table containing integers might have the hash objects
|
||||
* defined as:
|
||||
*
|
||||
* struct int_object {
|
||||
* int value;
|
||||
* struct hash_link link;
|
||||
* };
|
||||
*
|
||||
* When you initialize the hash table, you pass in the offset to the hash
|
||||
* link. For example, to initialize a hash table designed to hold
|
||||
* int_objects, you'd do something like:
|
||||
*
|
||||
* unsigned int hash_int_obj(void *x) {
|
||||
* return (unsigned int) ((int_object *) x)->value;
|
||||
* }
|
||||
* int compare_int_obj(void *a, void *b) {
|
||||
* return ((int_object *)a)->value - ((int_object *)b)->value;
|
||||
* }
|
||||
*
|
||||
* hash_table tab;
|
||||
* hash_table_init(&tab, offsetof(struct int_object, link), hash_int_obj, compare_int_obj);
|
||||
*
|
||||
* An item can be in multiple hash tables at once; just declare multiple
|
||||
* hash_link members and pass in the appropriate offset to each hash
|
||||
* table.
|
||||
*/
|
||||
|
||||
#include "hashtab.h"
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
/*
|
||||
* The number of buckets should be a prime number.
|
||||
* Use these numbers of buckets to grow or shrink the hash table.
|
||||
* Yes, OK, the list below is probably excessive.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief A list of prime numbers from 17 to about 1.4 billion, approximately
|
||||
* doubling with each successive number.
|
||||
*
|
||||
* These are used as choices for the number of hash buckets in the table
|
||||
*/
|
||||
static size_t bucket_choices[] = {
|
||||
17, 37, 79, 163, 331, 673, 1361, 2729, 5471, 10949, 21911, 43853, 87719,
|
||||
175447, 350899, 701819, 1403641, 2807303, 5614657, 11229331, 22458671,
|
||||
44917381, 89834777, 179669557, 359339171, 718678369, 1437356741 };
|
||||
|
||||
#define NUM_BUCKET_CHOICES (sizeof(bucket_choices) / sizeof(bucket_choices[0]))
|
||||
|
||||
#define NUM_BUCKETS(t) (bucket_choices[t->bucket_choice_index])
|
||||
|
||||
#define LINK(t, p) ( (struct hash_link *) (( ((char *) p) + t->hash_link_offset)) )
|
||||
|
||||
/**
|
||||
* \brief Initialize a hash table
|
||||
*
|
||||
* Initializes a hash table. A given hash table can contain a collection
|
||||
* of items, all of which must be the same. An item in a hash table is
|
||||
* a structure and one of the elements in the structure must be a
|
||||
* struct hash_link object. For example, if you are storing a collection
|
||||
* of integers in a hash table, your item might look like this:
|
||||
*
|
||||
* struct item {
|
||||
* int value;
|
||||
* struct hash_link link;
|
||||
* };
|
||||
*
|
||||
* \param t Pointer to a hash_table object
|
||||
* \param link_offset The offset to the struct hash_link object within the object being put in the hash table. In the example above, it would be
|
||||
* offsetof(struct item, link)
|
||||
* \param hashfunc A pointer to a function that computes a hash given a pointer to an object. This function must return an unsigned int.
|
||||
* \param compare A pointer to a function that compares two objects. It must
|
||||
* return 0 if they compare equal and non-zero if they do not.
|
||||
*
|
||||
* \return 0 on success, -1 on failure (and errno is set appropriately)
|
||||
*/
|
||||
int
|
||||
hash_table_init(hash_table *t,
|
||||
size_t link_offset,
|
||||
unsigned int (*hashfunc)(void *x),
|
||||
int (*compare)(void *a, void *b))
|
||||
{
|
||||
t->bucket_choice_index = 0;
|
||||
t->num_entries = 0;
|
||||
t->hash_link_offset = link_offset;
|
||||
t->hashfunc = hashfunc;
|
||||
t->compare = compare;
|
||||
t->buckets = malloc(sizeof(void *) * bucket_choices[0]);
|
||||
if (!t->buckets) {
|
||||
return -1;
|
||||
}
|
||||
for (size_t i=0; i<bucket_choices[0]; i++) {
|
||||
t->buckets[i] = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Free memory used by a hash table
|
||||
*
|
||||
* \param t Pointer to a hash_table object
|
||||
*/
|
||||
void
|
||||
hash_table_free(hash_table *t)
|
||||
{
|
||||
free(t->buckets);
|
||||
t->buckets = NULL;
|
||||
t->bucket_choice_index = -1;
|
||||
t->num_entries = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return the number of items in a hash table
|
||||
*
|
||||
* \param t Pointer to a hash_table object
|
||||
*
|
||||
* \return The number of items in the hash table
|
||||
*/
|
||||
size_t
|
||||
hash_table_num_entries(hash_table *t)
|
||||
{
|
||||
return t->num_entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return the number of buckets in a hash table
|
||||
*
|
||||
* \param t Pointer to a hash_table object
|
||||
*
|
||||
* \return The number of buckets in the hash table
|
||||
*/
|
||||
size_t
|
||||
hash_table_num_buckets(hash_table *t)
|
||||
{
|
||||
if (t->bucket_choice_index >= NUM_BUCKET_CHOICES) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return NUM_BUCKETS(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return the length of the i'th bucket chain
|
||||
*
|
||||
* If i >= num_buckets, returns (size_t) -1
|
||||
*
|
||||
* \param t Pointer to a hash_table object
|
||||
* \param i The bucket whose length we want (0 to num_buckets-1)
|
||||
* \return The length of the i'th bucket chain
|
||||
*/
|
||||
size_t
|
||||
hash_table_chain_len(hash_table *t, size_t i)
|
||||
{
|
||||
if (i >= hash_table_num_buckets(t)) {
|
||||
return (size_t) -1;
|
||||
}
|
||||
size_t len = 0;
|
||||
void *ptr = t->buckets[i];
|
||||
while(ptr) {
|
||||
len++;
|
||||
ptr = LINK(t, ptr)->next;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Resize a hash table
|
||||
*
|
||||
* Resizes (either grows or shrinks) a hash table's bucket array
|
||||
*
|
||||
* \param t Pointer to a hash_table object
|
||||
* \param dir Must be either 1 (to increase the bucket array size) or
|
||||
* -1 (to decrease it).
|
||||
* \return 0 on success, non-zero if resizing fails. NOTE: Currently, resizing
|
||||
* cannot fail; if we fail to allocate memory for the new bucket array,
|
||||
* we just keep the existing array. This behaviour may change in future.
|
||||
*/
|
||||
static int
|
||||
hash_table_resize(hash_table *t, int dir)
|
||||
{
|
||||
if (dir != 1 && dir != -1) {
|
||||
return 0;
|
||||
}
|
||||
if ((dir == -1 && t->bucket_choice_index == 0) ||
|
||||
(dir == 1 && t->bucket_choice_index == NUM_BUCKET_CHOICES-1)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t num_old_buckets = bucket_choices[t->bucket_choice_index];
|
||||
size_t num_new_buckets = bucket_choices[t->bucket_choice_index + dir];
|
||||
|
||||
void **new_buckets = malloc(sizeof(void *) * num_new_buckets);
|
||||
if (!new_buckets) {
|
||||
/* Out of memory... just don't resize? */
|
||||
return 0;
|
||||
}
|
||||
for (size_t j=0; j<num_new_buckets; j++) {
|
||||
new_buckets[j] = NULL;
|
||||
}
|
||||
|
||||
/* Move everything from the old buckets into the new */
|
||||
for (size_t i=0; i<num_old_buckets; i++) {
|
||||
if (!t->buckets[i]) {
|
||||
continue;
|
||||
}
|
||||
void *p = t->buckets[i];
|
||||
while(p) {
|
||||
struct hash_link *l = LINK(t, p);
|
||||
void *nxt = l->next;
|
||||
size_t j = l->hashval % num_new_buckets;
|
||||
l->next = new_buckets[j];
|
||||
new_buckets[j] = p;
|
||||
p = nxt;
|
||||
}
|
||||
}
|
||||
free(t->buckets);
|
||||
t->buckets = new_buckets;
|
||||
t->bucket_choice_index += dir;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Insert an item into a hash table
|
||||
*
|
||||
* Inserts an item into a hash table. The item MUST NOT be freed as
|
||||
* long as it is in a hash table
|
||||
*
|
||||
* \param t Pointer to a hash_table object
|
||||
* \param item Pointer to the item to insert
|
||||
*
|
||||
* \return 0 on success, -1 on failure (and errno is set appropriately)
|
||||
*/
|
||||
int
|
||||
hash_table_insert(hash_table *t, void *item)
|
||||
{
|
||||
if (!item) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned int v = t->hashfunc(item);
|
||||
|
||||
struct hash_link *l = LINK(t, item);
|
||||
l->hashval = v;
|
||||
|
||||
v = v % NUM_BUCKETS(t);
|
||||
|
||||
l->next = t->buckets[v];
|
||||
t->buckets[v] = item;
|
||||
t->num_entries++;
|
||||
|
||||
/* Grow table for load factor > 2 */
|
||||
if (t->bucket_choice_index < NUM_BUCKET_CHOICES-1 &&
|
||||
t->num_entries > 2 * NUM_BUCKETS(t)) {
|
||||
return hash_table_resize(t, 1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Find an item in a hash table
|
||||
*
|
||||
* \param t Pointer to a hash_table object
|
||||
* \param candidate Pointer to an object to be sought in the table
|
||||
*
|
||||
* \return A pointer to the object if one that matches candidate is found. NULL if not found
|
||||
*/
|
||||
void *
|
||||
hash_table_find(hash_table *t, void *candidate)
|
||||
{
|
||||
if (!candidate) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned int v = t->hashfunc(candidate);
|
||||
|
||||
void *ptr = t->buckets[v % NUM_BUCKETS(t)];
|
||||
|
||||
while(ptr) {
|
||||
if (!t->compare(candidate, ptr)) {
|
||||
return ptr;
|
||||
}
|
||||
ptr = LINK(t, ptr)->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Find the next item in a hash table
|
||||
*
|
||||
* \param t Pointer to a hash table object
|
||||
* \param obj Pointer to an object that was perviously returned by
|
||||
* hash_table_find() or hash_table_find_next().
|
||||
*
|
||||
* \return A pointer to the next object matching obj, or NULL if
|
||||
* no more exist
|
||||
*/
|
||||
void *
|
||||
hash_table_find_next(hash_table *t, void *obj)
|
||||
{
|
||||
if (!obj) {
|
||||
return NULL;
|
||||
}
|
||||
void *ptr = LINK(t, obj)->next;
|
||||
while(ptr) {
|
||||
if (!t->compare(obj, ptr)) {
|
||||
return ptr;
|
||||
}
|
||||
ptr = LINK(t, ptr)->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Delete an item from a hash table
|
||||
*
|
||||
* \param t Pointer to a hash_table object
|
||||
* \param candidate Pointer to an object that is in the table and must be removed from it
|
||||
* \param resize_ok If non-zero, then it's OK to resize the hash table.
|
||||
*
|
||||
* \return 0 on success, -1 on failure
|
||||
*/
|
||||
int
|
||||
hash_table_delete_helper(hash_table *t, void *item, int resize_ok)
|
||||
{
|
||||
if (!item) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct hash_link *l = LINK(t, item);
|
||||
unsigned int v = l->hashval;
|
||||
|
||||
v = v % NUM_BUCKETS(t);
|
||||
|
||||
if (t->buckets[v] == item) {
|
||||
t->buckets[v] = l->next;
|
||||
t->num_entries--;
|
||||
if (resize_ok) {
|
||||
/* Shrink table for load factor < 1 */
|
||||
if (t->bucket_choice_index > 0 &&
|
||||
t->num_entries < NUM_BUCKETS(t) / 2) {
|
||||
return hash_table_resize(t, -1);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *ptr = t->buckets[v];
|
||||
while(ptr) {
|
||||
struct hash_link *l2 = LINK(t, ptr);
|
||||
if (l2->next == item) {
|
||||
l2->next = l->next;
|
||||
t->num_entries--;
|
||||
/* Shrink table for load factor < 1 */
|
||||
if (resize_ok) {
|
||||
if (t->bucket_choice_index > 0 &&
|
||||
t->num_entries < NUM_BUCKETS(t) / 2) {
|
||||
return hash_table_resize(t, -1);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
ptr = l2->next;
|
||||
}
|
||||
|
||||
/* Item not found in hash table */
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
hash_table_delete(hash_table *t, void *item)
|
||||
{
|
||||
return hash_table_delete_helper(t, item, 1);
|
||||
}
|
||||
|
||||
int
|
||||
hash_table_delete_no_resize(hash_table *t, void *item)
|
||||
{
|
||||
return hash_table_delete_helper(t, item, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Iterate to the next item in a hash table
|
||||
*
|
||||
* Acts as an iterator. Given a pointer to an item in the hash
|
||||
* table, returns the next item, or NULL if no more items. If the
|
||||
* existing-item pointer is supplied as NULL, returns a pointer to the
|
||||
* first item in the hash table. You can therefore iterate across the
|
||||
* hash table like this*
|
||||
*
|
||||
* void *item = NULL;
|
||||
* while ( (item = hash_table_next(&table, item) ) != NULL) {
|
||||
* // Do something with item
|
||||
* }
|
||||
*
|
||||
* NOTE that you MUST NOT modify the hash table while iterating over it.
|
||||
*
|
||||
* \param t Pointer to a hash_table object
|
||||
* \param cur The current item. Supply as NULL to get the first item
|
||||
*
|
||||
* \return A pointer to the next item in the hash table, or NULL if there
|
||||
* are no more items
|
||||
*/
|
||||
void *
|
||||
hash_table_next(hash_table *t, void *cur)
|
||||
{
|
||||
size_t n_buckets = NUM_BUCKETS(t);
|
||||
|
||||
size_t start_bucket = 0;
|
||||
if (cur) {
|
||||
struct hash_link *l = LINK(t, cur);
|
||||
if (l->next) {
|
||||
return l->next;
|
||||
}
|
||||
/* End of this chain; start searching at the next bucket */
|
||||
start_bucket = (l->hashval % n_buckets) + 1;
|
||||
}
|
||||
|
||||
for (size_t i=start_bucket; i<n_buckets; i++) {
|
||||
if (t->buckets[i]) {
|
||||
return t->buckets[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* HASHTAB.H */
|
||||
/* */
|
||||
/* Header file for hash-table related functions. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
|
||||
/* For size_t */
|
||||
#include <stdio.h>
|
||||
|
||||
/**
|
||||
* \brief A structure for holding hash table chain links.
|
||||
*
|
||||
* This structure is embedded in a container structure to make up
|
||||
* a hash table entry
|
||||
*/
|
||||
struct hash_link {
|
||||
void *next; /**< Link to next item in the chain */
|
||||
unsigned int hashval; /**< Cached hash function value */
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief A hash table
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned int bucket_choice_index; /**< Index into array of possible bucket counts */
|
||||
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 */
|
||||
void **buckets; /**< Array of buckets */
|
||||
unsigned int (*hashfunc)(void *x); /**< Pointer to the hashing function */
|
||||
int (*compare)(void *a, void *b); /**< Pointer to the comparison function */
|
||||
} hash_table;
|
||||
|
||||
/**
|
||||
* \brief Data type to hold statistics about a hash table
|
||||
*/
|
||||
struct hash_table_stats {
|
||||
size_t num_entries; /**< Number of items in the hash table */
|
||||
size_t num_buckets; /**< Number of buckets in the hash table */
|
||||
size_t num_nonempty_buckets; /**< Number of non-emptry buckets */
|
||||
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 */
|
||||
double avg_len; /**< Average chain length */
|
||||
double avg_nonempty_len; /**< Average chain length of non-empty bucket */
|
||||
double stddev; /**< Standard deviation of chain lengths */
|
||||
};
|
||||
|
||||
int hash_table_init(hash_table *t,
|
||||
size_t link_offset,
|
||||
unsigned int (*hashfunc)(void *x),
|
||||
int (*compare)(void *a, void *b));
|
||||
void hash_table_free(hash_table *t);
|
||||
size_t hash_table_num_entries(hash_table *t);
|
||||
size_t hash_table_num_buckets(hash_table *t);
|
||||
size_t hash_table_chain_len(hash_table *t, size_t i);
|
||||
int hash_table_insert(hash_table *t, void *item);
|
||||
void *hash_table_find(hash_table *t, void *candidate);
|
||||
void *hash_table_find_next(hash_table *t, void *obj);
|
||||
int hash_table_delete(hash_table *t, void *item);
|
||||
int hash_table_delete_no_resize(hash_table *t, void *item);
|
||||
void *hash_table_next(hash_table *t, void *cur);
|
||||
void hash_table_dump_stats(hash_table *t, FILE *fp);
|
||||
void hash_table_get_stats(hash_table *t, struct hash_table_stats *stat);
|
||||
|
||||
/**
|
||||
* \brief Iterate over all items in a hash table
|
||||
*
|
||||
* This macro iterates over all items in a hash table. Here is an
|
||||
* example of how to use it:
|
||||
*
|
||||
* hash_table tab;
|
||||
* void *item;
|
||||
* hash_table_for_each(item, &tab) {
|
||||
* // Do something with item
|
||||
* }
|
||||
*/
|
||||
#define hash_table_for_each(item, t) \
|
||||
for ((item) = hash_table_next((t), NULL); \
|
||||
(item); \
|
||||
(item) = hash_table_next((t), (item)))
|
||||
|
||||
/**
|
||||
* \brief Iterate over all items in a hash table that match a candidate
|
||||
*
|
||||
* This macro iterates over all items in a hash table that match a
|
||||
* candidate object. (In general, a hash table may contain multiple
|
||||
* objects with the same key.) Here is an example assuming that the hash
|
||||
* table holds objects of type struct int_object:
|
||||
*
|
||||
* struct int_object {
|
||||
* int value;
|
||||
* struct hash_link link;
|
||||
* }
|
||||
*
|
||||
* hash_table tab;
|
||||
* int_object candidate;
|
||||
*
|
||||
* candidate.value = 7;
|
||||
* int_object *item;
|
||||
* hash_table_for_each_matching(item, &candidate, &tab) {
|
||||
* // Do something with item, which will match "7"
|
||||
* }
|
||||
*/
|
||||
#define hash_table_for_each_matching(item, candidate, t) \
|
||||
for ((item) = hash_table_find((t), (candidate)); \
|
||||
(item); \
|
||||
(item) = hash_table_find_next((t), (item)))
|
||||
@@ -0,0 +1,96 @@
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* HASHTAB_STATS.C */
|
||||
/* */
|
||||
/* Utility function to print hash table stats. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
|
||||
/**
|
||||
* \file hashtab_stats.c
|
||||
* \brief Obtain or print statistics about a hash table
|
||||
*
|
||||
* NOTE: Use of any of the functions in this file will require linking
|
||||
* with the math library to pull in the sqrt() function.
|
||||
*/
|
||||
|
||||
#include "hashtab.h"
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
/**
|
||||
* \brief Dump hash table statistics to a stdio FILE
|
||||
*
|
||||
* \param t A pointer to a hash_table object
|
||||
* \param fp A stdio file pointer that is writable
|
||||
*/
|
||||
void
|
||||
hash_table_dump_stats(hash_table *t, FILE *fp)
|
||||
{
|
||||
struct hash_table_stats stat;
|
||||
hash_table_get_stats(t, &stat);
|
||||
fprintf(fp, "#Entries: %lu\n#Buckets: %lu\n#Non-empty Buckets: %lu\n",
|
||||
(unsigned long) stat.num_entries,
|
||||
(unsigned long) stat.num_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",
|
||||
(unsigned long) stat.max_len,
|
||||
(unsigned long) stat.min_len,
|
||||
stat.avg_len, stat.stddev, stat.avg_nonempty_len);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Obtain hash table statistics
|
||||
*
|
||||
* This function fills in the elements of a struct hash_table_stats object
|
||||
* with hash table statistics.
|
||||
*
|
||||
* \param t A pointer to a hash_table object
|
||||
* \param stat A pointer to a hash_table_stats object that will be filled in
|
||||
*/
|
||||
void
|
||||
hash_table_get_stats(hash_table *t, struct hash_table_stats *stat)
|
||||
{
|
||||
size_t n = hash_table_num_buckets(t);
|
||||
size_t max_len = 0;
|
||||
size_t min_len = 1000000000;
|
||||
|
||||
stat->num_buckets = n;
|
||||
stat->num_entries = hash_table_num_entries(t);
|
||||
stat->max_len = 0;
|
||||
stat->min_len = 0;
|
||||
stat->avg_len = 0.0;
|
||||
stat->stddev = 0.0;
|
||||
stat->num_nonempty_buckets = 0;
|
||||
stat->avg_nonempty_len = 0.0;
|
||||
double sum = 0.0;
|
||||
double sumsq = 0.0;
|
||||
|
||||
if (n == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i=0; i<n; i++) {
|
||||
size_t c = hash_table_chain_len(t, i);
|
||||
if (c != 0) {
|
||||
stat->num_nonempty_buckets++;
|
||||
}
|
||||
sum += (double) c;
|
||||
sumsq += (double) c * (double) c;
|
||||
if (c > max_len) max_len = c;
|
||||
if (c < min_len) min_len = c;
|
||||
}
|
||||
double avg_len = sum / (double) n;
|
||||
double stddev = sqrt( (sumsq / (double) n) - (avg_len * avg_len) );
|
||||
if (stat->num_nonempty_buckets > 0) {
|
||||
stat->avg_nonempty_len = sum / (double) stat->num_nonempty_buckets;
|
||||
}
|
||||
stat->max_len = max_len;
|
||||
stat->min_len = min_len;
|
||||
stat->avg_len = avg_len;
|
||||
stat->stddev = stddev;
|
||||
}
|
||||
@@ -179,6 +179,14 @@ void InitRemind(int argc, char const *argv[])
|
||||
|
||||
dse = NO_DATE;
|
||||
|
||||
/* Initialize variable hash table */
|
||||
InitVars();
|
||||
|
||||
/* Initialize user-defined functions hash table */
|
||||
InitUserFunctions();
|
||||
|
||||
InitTranslationTable();
|
||||
|
||||
/* If stdout is a terminal, initialize $FormWidth to terminal width-8,
|
||||
but clamp to [20, 500] */
|
||||
InitCalWidthAndFormWidth(STDOUT_FILENO);
|
||||
|
||||
+75
@@ -71,6 +71,8 @@ exitfunc(void)
|
||||
fprintf(stderr, " Func hash: total = %d; maxlen = %d; avglen = %.3f\n", total, maxlen, avglen);
|
||||
get_dedupe_hash_stats(&total, &maxlen, &avglen);
|
||||
fprintf(stderr, "Dedup hash: total = %d; maxlen = %d; avglen = %.3f\n", total, maxlen, avglen);
|
||||
get_translation_hash_stats(&total, &maxlen, &avglen);
|
||||
fprintf(stderr, "Trans hash: total = %d; maxlen = %d; avglen = %.3f\n", total, maxlen, avglen);
|
||||
UnsetAllUserFuncs();
|
||||
print_expr_nodes_stats();
|
||||
}
|
||||
@@ -352,6 +354,7 @@ static void DoReminders(void)
|
||||
case T_Preserve: r=DoPreserve(&p); break;
|
||||
case T_Push: r=PushOmitContext(&p); break;
|
||||
case T_Expr: r = DoExpr(&p); break;
|
||||
case T_Translate: r = DoTranslate(&p); break;
|
||||
case T_RemType: if (tok.val == RUN_TYPE) {
|
||||
r=DoRun(&p);
|
||||
} else {
|
||||
@@ -596,6 +599,78 @@ int ParseNonSpaceChar(ParsePtr p, int *err, int peek)
|
||||
return ch;
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* ParseQuotedString */
|
||||
/* */
|
||||
/* Parse a double-quote-delimited string. */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
int ParseQuotedString(ParsePtr p, DynamicBuffer *dbuf)
|
||||
{
|
||||
int c, err;
|
||||
DBufFree(dbuf);
|
||||
c = ParseNonSpaceChar(p, &err, 0);
|
||||
if (err) return err;
|
||||
if (!c) {
|
||||
return E_EOLN;
|
||||
}
|
||||
if (c != '"') {
|
||||
return E_MISS_QUOTE;
|
||||
}
|
||||
c = ParseChar(p, &err, 0);
|
||||
if (err) {
|
||||
DBufFree(dbuf);
|
||||
return err;
|
||||
}
|
||||
while (c != '"') {
|
||||
if (c == '\\') {
|
||||
c = ParseChar(p, &err, 0);
|
||||
if (err) {
|
||||
DBufFree(dbuf);
|
||||
return err;
|
||||
}
|
||||
switch(c) {
|
||||
case 'a':
|
||||
err = DBufPutc(dbuf, '\a');
|
||||
break;
|
||||
case 'b':
|
||||
err = DBufPutc(dbuf, '\b');
|
||||
break;
|
||||
case 'f':
|
||||
err = DBufPutc(dbuf, '\f');
|
||||
break;
|
||||
case 'n':
|
||||
err = DBufPutc(dbuf, '\n');
|
||||
break;
|
||||
case 'r':
|
||||
err = DBufPutc(dbuf, '\r');
|
||||
break;
|
||||
case 't':
|
||||
err = DBufPutc(dbuf, '\t');
|
||||
break;
|
||||
case 'v':
|
||||
err = DBufPutc(dbuf, '\v');
|
||||
break;
|
||||
default:
|
||||
err = DBufPutc(dbuf, c);
|
||||
}
|
||||
} else {
|
||||
err = DBufPutc(dbuf, c);
|
||||
}
|
||||
if (err) {
|
||||
DBufFree(dbuf);
|
||||
return err;
|
||||
}
|
||||
c = ParseChar(p, &err, 0);
|
||||
if (err) {
|
||||
DBufFree(dbuf);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* ParseToken */
|
||||
|
||||
+11
-1
@@ -87,6 +87,7 @@ void FromDSE (int dse, int *y, int *m, int *d);
|
||||
int JulianToGregorianOffset(int y, int m);
|
||||
int ParseChar (ParsePtr p, int *err, int peek);
|
||||
int ParseToken (ParsePtr p, DynamicBuffer *dbuf);
|
||||
int ParseQuotedString (ParsePtr p, DynamicBuffer *dbuf);
|
||||
int ParseIdentifier (ParsePtr p, DynamicBuffer *dbuf);
|
||||
expr_node * ParseExpr(ParsePtr p, int *r);
|
||||
void print_expr_nodes_stats(void);
|
||||
@@ -111,6 +112,8 @@ int DoDebug (ParsePtr p);
|
||||
int DoBanner (ParsePtr p);
|
||||
int DoRun (ParsePtr p);
|
||||
int DoExpr (ParsePtr p);
|
||||
int DoTranslate (ParsePtr p);
|
||||
int InsertTranslation(char const *orig, char const *translated);
|
||||
int DoErrMsg (ParsePtr p);
|
||||
int ClearGlobalOmits (void);
|
||||
int DoClear (ParsePtr p);
|
||||
@@ -158,7 +161,8 @@ int DoPreserve (Parser *p);
|
||||
int DoSatRemind (Trigger *trig, TimeTrig *tt, ParsePtr p);
|
||||
int DoMsgCommand (char const *cmd, char const *msg, int is_queued);
|
||||
int ParseNonSpaceChar (ParsePtr p, int *err, int peek);
|
||||
unsigned int HashVal (char const *str);
|
||||
unsigned int HashVal_ignorecase(char const *str);
|
||||
unsigned int HashVal_preservecase(char const *str);
|
||||
int DateOK (int y, int m, int d);
|
||||
BuiltinFunc *FindBuiltinFunc (char const *name);
|
||||
int InsertIntoSortBuffer (int dse, int tim, char const *body, int typ, int prio);
|
||||
@@ -253,8 +257,14 @@ void print_remind_tokens(void);
|
||||
void get_var_hash_stats(int *total, int *maxlen, double *avglen);
|
||||
void get_userfunc_hash_stats(int *total, int *maxlen, double *avglen);
|
||||
void get_dedupe_hash_stats(int *total, int *maxlen, double *avglen);
|
||||
void get_translation_hash_stats(int *total, int *maxlen, double *avglen);
|
||||
|
||||
/* Dedupe code */
|
||||
int ShouldDedupe(int trigger_date, int trigger_time, char const *body);
|
||||
void ClearDedupeTable(void);
|
||||
void InitDedupeTable(void);
|
||||
|
||||
void InitVars(void);
|
||||
void InitUserFunctions(void);
|
||||
void InitTranslationTable(void);
|
||||
char const *GetTranslatedString(char const *orig);
|
||||
|
||||
@@ -111,6 +111,7 @@ Token TokArray[] = {
|
||||
{ "third", 5, T_Ordinal, 2 },
|
||||
{ "through", 7, T_Through, 0 },
|
||||
{ "thursday", 3, T_WkDay, 3 },
|
||||
{ "translate", 5, T_Translate, 0 },
|
||||
{ "tuesday", 3, T_WkDay, 1 },
|
||||
{ "unset", 5, T_UnSet, 0 },
|
||||
{ "until", 5, T_Until, 0 },
|
||||
|
||||
+274
@@ -0,0 +1,274 @@
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* TRANS.C */
|
||||
/* */
|
||||
/* Functions to manage the translation table. Implements */
|
||||
/* the TRANSLATE keyword. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include "types.h"
|
||||
#include "globals.h"
|
||||
#include "protos.h"
|
||||
#include "err.h"
|
||||
|
||||
/* The structure of a translation item */
|
||||
typedef struct xlat {
|
||||
struct hash_link link;
|
||||
char *orig;
|
||||
char *translated;
|
||||
} XlateItem;
|
||||
|
||||
hash_table TranslationTable;
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* AllocateXlateItem - Allocate a new translation item */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
static XlateItem *
|
||||
AllocateXlateItem(char const *orig, char const *translated)
|
||||
{
|
||||
size_t s1 = sizeof(XlateItem);
|
||||
size_t s2 = strlen(orig)+1;
|
||||
size_t s3 = strlen(translated)+1;
|
||||
XlateItem *item;
|
||||
|
||||
char *blob = malloc(s1+s2+s3);
|
||||
if (!blob) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
item = (XlateItem *) blob;
|
||||
|
||||
/* Allocate the string space in ONE go! */
|
||||
item->orig = blob + s1;
|
||||
item->translated = item->orig + s2;
|
||||
strcpy(item->orig, orig);
|
||||
strcpy(item->translated, translated);
|
||||
return item;
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* FreeXlateItem - Free a translation item */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
static void
|
||||
FreeXlateItem(XlateItem *item)
|
||||
{
|
||||
if (!item) return;
|
||||
|
||||
free(item);
|
||||
}
|
||||
|
||||
static void
|
||||
RemoveTranslation(XlateItem *item)
|
||||
{
|
||||
hash_table_delete_no_resize(&TranslationTable, item);
|
||||
FreeXlateItem(item);
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* ClearTranslationTable - free all translation items */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
static void
|
||||
ClearTranslationTable(void)
|
||||
{
|
||||
XlateItem *item;
|
||||
XlateItem *next;
|
||||
|
||||
item = hash_table_next(&TranslationTable, NULL);
|
||||
while(item) {
|
||||
next = hash_table_next(&TranslationTable, item);
|
||||
RemoveTranslation(item);
|
||||
item = next;
|
||||
}
|
||||
hash_table_free(&TranslationTable);
|
||||
InitTranslationTable();
|
||||
}
|
||||
|
||||
static void
|
||||
print_escaped_string(FILE *fp, char const *s)
|
||||
{
|
||||
putc('"', fp);
|
||||
while(*s) {
|
||||
switch(*s) {
|
||||
case '\a': putc('\\', fp); putc('a', fp); break;
|
||||
case '\b': putc('\\', fp); putc('b', fp); break;
|
||||
case '\f': putc('\\', fp); putc('f', fp); break;
|
||||
case '\n': putc('\\', fp); putc('n', fp); break;
|
||||
case '\r': putc('\\', fp); putc('r', fp); break;
|
||||
case '\t': putc('\\', fp); putc('t', fp); break;
|
||||
case '\v': putc('\\', fp); putc('v', fp); break;
|
||||
case '"': putc('\\', fp); putc('"', fp); break;
|
||||
case '\\': putc('\\', fp); putc('\\', fp); break;
|
||||
default:
|
||||
if (*s < 32) {
|
||||
fprintf(fp, "\\x%02x", (unsigned int) *s);
|
||||
} else {
|
||||
putc(*s, fp); break;
|
||||
}
|
||||
}
|
||||
s++;
|
||||
}
|
||||
putc('"', fp);
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* DumpTranslationTable - Dump the table to a file descriptor */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
static void
|
||||
DumpTranslationTable(FILE *fp)
|
||||
{
|
||||
XlateItem *item;
|
||||
|
||||
item = hash_table_next(&TranslationTable, NULL);
|
||||
while(item) {
|
||||
print_escaped_string(fp, item->orig);
|
||||
fprintf(fp, " ");
|
||||
print_escaped_string(fp, item->translated);
|
||||
fprintf(fp, "\n");
|
||||
item = hash_table_next(&TranslationTable, item);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
HashXlateItem(void *x)
|
||||
{
|
||||
XlateItem *item = (XlateItem *) x;
|
||||
return HashVal_preservecase(item->orig);
|
||||
}
|
||||
|
||||
static int
|
||||
CompareXlateItems(void *a, void *b)
|
||||
{
|
||||
XlateItem *i = (XlateItem *) a;
|
||||
XlateItem *j = (XlateItem *) b;
|
||||
return strcmp(i->orig, j->orig);
|
||||
}
|
||||
|
||||
void
|
||||
InitTranslationTable(void)
|
||||
{
|
||||
if (hash_table_init(&TranslationTable, offsetof(XlateItem, link),
|
||||
HashXlateItem, CompareXlateItems) < 0) {
|
||||
fprintf(stderr, "Unable to initialize translation hash table: Out of memory. Exiting.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static XlateItem *
|
||||
FindTranslation(char const *orig)
|
||||
{
|
||||
XlateItem *item;
|
||||
XlateItem candidate;
|
||||
candidate.orig = (char *) orig;
|
||||
item = hash_table_find(&TranslationTable, &candidate);
|
||||
return item;
|
||||
}
|
||||
|
||||
int
|
||||
InsertTranslation(char const *orig, char const *translated)
|
||||
{
|
||||
XlateItem *item = FindTranslation(orig);
|
||||
if (item) {
|
||||
if (!strcmp(item->translated, translated)) {
|
||||
/* Translation is the same; do nothing */
|
||||
return OK;
|
||||
}
|
||||
RemoveTranslation(item);
|
||||
}
|
||||
item = AllocateXlateItem(orig, translated);
|
||||
if (!item) {
|
||||
return E_NO_MEM;
|
||||
}
|
||||
hash_table_insert(&TranslationTable, item);
|
||||
return OK;
|
||||
}
|
||||
|
||||
char const *
|
||||
GetTranslatedString(char const *orig)
|
||||
{
|
||||
XlateItem *item = FindTranslation(orig);
|
||||
if (!item) return NULL;
|
||||
return item->translated;
|
||||
}
|
||||
|
||||
int
|
||||
DoTranslate(ParsePtr p)
|
||||
{
|
||||
int r;
|
||||
DynamicBuffer orig, translated;
|
||||
DBufInit(&orig);
|
||||
DBufInit(&translated);
|
||||
int c;
|
||||
|
||||
c = ParseNonSpaceChar(p, &r, 1);
|
||||
if (r) return r;
|
||||
if (c != '"') {
|
||||
r = ParseToken(p, &orig);
|
||||
if (r) return r;
|
||||
if (!StrCmpi(DBufValue(&orig), "dump")) {
|
||||
DumpTranslationTable(stdout);
|
||||
return OK;
|
||||
}
|
||||
if (!StrCmpi(DBufValue(&orig), "clear")) {
|
||||
ClearTranslationTable();
|
||||
return OK;
|
||||
}
|
||||
return E_PARSE_ERR;
|
||||
}
|
||||
|
||||
if ( (r=ParseQuotedString(p, &orig)) ) {
|
||||
return r;
|
||||
}
|
||||
if ( (r=ParseQuotedString(p, &translated)) ) {
|
||||
if (r == E_EOLN) {
|
||||
XlateItem *item = FindTranslation(DBufValue(&orig));
|
||||
if (item) {
|
||||
RemoveTranslation(item);
|
||||
}
|
||||
r = OK;
|
||||
}
|
||||
DBufFree(&orig);
|
||||
return r;
|
||||
}
|
||||
|
||||
if ( (r=VerifyEoln(p)) ) {
|
||||
DBufFree(&orig);
|
||||
DBufFree(&translated);
|
||||
return r;
|
||||
}
|
||||
r = InsertTranslation(DBufValue(&orig), DBufValue(&translated));
|
||||
DBufFree(&orig);
|
||||
DBufFree(&translated);
|
||||
return r;
|
||||
}
|
||||
|
||||
void
|
||||
get_translation_hash_stats(int *total, int *maxlen, double *avglen)
|
||||
{
|
||||
struct hash_table_stats s;
|
||||
hash_table_get_stats(&TranslationTable, &s);
|
||||
*total = s.num_entries;
|
||||
*maxlen = s.max_len;
|
||||
*avglen = s.avg_len;
|
||||
}
|
||||
+4
-3
@@ -12,6 +12,7 @@
|
||||
|
||||
#include <limits.h>
|
||||
#include "dynbuf.h"
|
||||
#include "hashtab.h"
|
||||
|
||||
typedef struct udf_struct UserFunc;
|
||||
|
||||
@@ -99,7 +100,7 @@ typedef struct expr_node_struct {
|
||||
|
||||
/* Define the structure of a variable */
|
||||
typedef struct var {
|
||||
struct var *next;
|
||||
struct hash_link link;
|
||||
char name[VAR_NAME_LEN+1];
|
||||
char preserve;
|
||||
Value v;
|
||||
@@ -217,7 +218,7 @@ enum TokTypes
|
||||
T_MaybeUncomputable, T_Month, T_NoQueue, T_Number, T_Omit, T_OmitFunc,
|
||||
T_Once, T_Ordinal, T_Pop, T_Preserve, T_Priority, T_Push,T_Rem,
|
||||
T_RemType, T_Rep, T_Scanfrom, T_Sched, T_Set, T_Skip, T_Tag, T_Through,
|
||||
T_Time, T_UnSet, T_Until, T_Warn, T_WkDay, T_Year
|
||||
T_Time, T_Translate, T_UnSet, T_Until, T_Warn, T_WkDay, T_Year
|
||||
};
|
||||
|
||||
/* The structure of a token */
|
||||
@@ -291,7 +292,7 @@ typedef struct {
|
||||
|
||||
/* Define the data structure used to hold a user-defined function */
|
||||
typedef struct udf_struct {
|
||||
struct udf_struct *next;
|
||||
struct hash_link link;
|
||||
char name[VAR_NAME_LEN+1];
|
||||
expr_node *node;
|
||||
char **args;
|
||||
|
||||
+57
-76
@@ -15,6 +15,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef HAVE_STRINGS_H
|
||||
#include <strings.h>
|
||||
@@ -27,23 +28,46 @@
|
||||
#include "protos.h"
|
||||
#include "err.h"
|
||||
|
||||
#define FUNC_HASH_SIZE 31 /* Size of User-defined function hash table */
|
||||
|
||||
/* The hash table */
|
||||
static UserFunc *FuncHash[FUNC_HASH_SIZE];
|
||||
hash_table FuncHash;
|
||||
|
||||
static void DestroyUserFunc (UserFunc *f);
|
||||
static void FUnset (char const *name);
|
||||
static void FSet (UserFunc *f);
|
||||
static void RenameUserFunc(char const *oldname, char const *newname);
|
||||
|
||||
static unsigned int HashUserFunc(void *x)
|
||||
{
|
||||
UserFunc *f = (UserFunc *) x;
|
||||
return HashVal_preservecase(f->name);
|
||||
}
|
||||
|
||||
static int CompareUserFuncs(void *a, void *b)
|
||||
{
|
||||
UserFunc *f = (UserFunc *) a;
|
||||
UserFunc *g = (UserFunc *) b;
|
||||
return strcmp(f->name, g->name);
|
||||
}
|
||||
|
||||
void
|
||||
InitUserFunctions(void)
|
||||
{
|
||||
if (hash_table_init(&FuncHash,
|
||||
offsetof(UserFunc, link),
|
||||
HashUserFunc,
|
||||
CompareUserFuncs) < 0) {
|
||||
fprintf(stderr, "Unable to initialize function hash table: Out of memory. Exiting.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* HashVal */
|
||||
/* HashVal_preservecase */
|
||||
/* Given a string, compute the hash value. */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
unsigned int HashVal_nocase(char const *str)
|
||||
unsigned int HashVal_preservecase(char const *str)
|
||||
{
|
||||
unsigned int h = 0, high;
|
||||
while(*str) {
|
||||
@@ -257,8 +281,8 @@ int DoFset(ParsePtr p)
|
||||
}
|
||||
local_array[i].v.type = ERR_TYPE;
|
||||
StrnCpy(local_array[i].name, DBufValue(&buf), VAR_NAME_LEN);
|
||||
local_array[i].next = &(local_array[i+1]);
|
||||
local_array[i+1].next = NULL;
|
||||
local_array[i].link.next = &(local_array[i+1]);
|
||||
local_array[i+1].link.next = NULL;
|
||||
func->nargs++;
|
||||
c = ParseNonSpaceChar(p, &r, 0);
|
||||
if (r) {
|
||||
@@ -373,21 +397,11 @@ static void DestroyUserFunc(UserFunc *f)
|
||||
/***************************************************************/
|
||||
static void FUnset(char const *name)
|
||||
{
|
||||
UserFunc *cur, *prev;
|
||||
int h;
|
||||
|
||||
h = HashVal_nocase(name) % FUNC_HASH_SIZE;
|
||||
|
||||
cur = FuncHash[h];
|
||||
prev = NULL;
|
||||
while(cur) {
|
||||
if (! strncmp(name, cur->name, VAR_NAME_LEN)) break;
|
||||
prev = cur;
|
||||
cur = cur->next;
|
||||
UserFunc *f = FindUserFunc(name);
|
||||
if (f) {
|
||||
hash_table_delete(&FuncHash, f);
|
||||
DestroyUserFunc(f);
|
||||
}
|
||||
if (!cur) return;
|
||||
if (prev) prev->next = cur->next; else FuncHash[h] = cur->next;
|
||||
DestroyUserFunc(cur);
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
@@ -399,19 +413,17 @@ static void FUnset(char const *name)
|
||||
/***************************************************************/
|
||||
static void FSet(UserFunc *f)
|
||||
{
|
||||
int h = HashVal_nocase(f->name) % FUNC_HASH_SIZE;
|
||||
f->next = FuncHash[h];
|
||||
FuncHash[h] = f;
|
||||
hash_table_insert(&FuncHash, f);
|
||||
}
|
||||
|
||||
UserFunc *FindUserFunc(char const *name)
|
||||
{
|
||||
UserFunc *f;
|
||||
int h = HashVal_nocase(name) % FUNC_HASH_SIZE;
|
||||
UserFunc candidate;
|
||||
|
||||
/* Search for the function */
|
||||
f = FuncHash[h];
|
||||
while (f && strncmp(name, f->name, VAR_NAME_LEN)) f = f->next;
|
||||
StrnCpy(candidate.name, name, VAR_NAME_LEN);
|
||||
|
||||
f = hash_table_find(&FuncHash, &candidate);
|
||||
return f;
|
||||
}
|
||||
|
||||
@@ -444,16 +456,16 @@ UnsetAllUserFuncs(void)
|
||||
{
|
||||
UserFunc *f;
|
||||
UserFunc *next;
|
||||
int i;
|
||||
for (i=0; i<FUNC_HASH_SIZE; i++) {
|
||||
f = FuncHash[i];
|
||||
while(f) {
|
||||
next = f->next;
|
||||
DestroyUserFunc(f);
|
||||
f = next;
|
||||
}
|
||||
FuncHash[i] = NULL;
|
||||
|
||||
f = hash_table_next(&FuncHash, NULL);
|
||||
while(f) {
|
||||
next = hash_table_next(&FuncHash, f);
|
||||
hash_table_delete_no_resize(&FuncHash, f);
|
||||
DestroyUserFunc(f);
|
||||
f = next;
|
||||
}
|
||||
hash_table_free(&FuncHash);
|
||||
InitUserFunctions();
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
@@ -469,7 +481,6 @@ static void
|
||||
RenameUserFunc(char const *oldname, char const *newname)
|
||||
{
|
||||
UserFunc *f = FindUserFunc(oldname);
|
||||
UserFunc *cur, *prev;
|
||||
|
||||
if (!strcmp(oldname, newname)) {
|
||||
/* Same name; do nothing */
|
||||
@@ -485,52 +496,22 @@ RenameUserFunc(char const *oldname, char const *newname)
|
||||
}
|
||||
|
||||
/* Remove from hash table */
|
||||
int h = HashVal_nocase(f->name) % FUNC_HASH_SIZE;
|
||||
cur = FuncHash[h];
|
||||
prev = NULL;
|
||||
while(cur) {
|
||||
if (cur == f) {
|
||||
if (prev) {
|
||||
prev->next = cur->next;
|
||||
} else {
|
||||
FuncHash[h] = cur->next;
|
||||
}
|
||||
break;
|
||||
}
|
||||
prev = cur;
|
||||
cur = cur->next;
|
||||
}
|
||||
hash_table_delete(&FuncHash, f);
|
||||
|
||||
/* Rename */
|
||||
StrnCpy(f->name, newname, VAR_NAME_LEN);
|
||||
|
||||
/* Insert into hash table */
|
||||
h = HashVal_nocase(f->name) % FUNC_HASH_SIZE;
|
||||
f->next = FuncHash[h];
|
||||
FuncHash[h] = f;
|
||||
hash_table_insert(&FuncHash, f);
|
||||
}
|
||||
|
||||
void
|
||||
get_userfunc_hash_stats(int *total, int *maxlen, double *avglen)
|
||||
{
|
||||
int len;
|
||||
int i;
|
||||
UserFunc *f;
|
||||
|
||||
*maxlen = 0;
|
||||
*total = 0;
|
||||
|
||||
for (i=0; i<FUNC_HASH_SIZE; i++) {
|
||||
len = 0;
|
||||
f = FuncHash[i];
|
||||
while(f) {
|
||||
len++;
|
||||
(*total)++;
|
||||
f = f->next;
|
||||
}
|
||||
if (len > *maxlen) {
|
||||
*maxlen = len;
|
||||
}
|
||||
}
|
||||
*avglen = (double) *total / (double) FUNC_HASH_SIZE;
|
||||
struct hash_table_stats s;
|
||||
hash_table_get_stats(&FuncHash, &s);
|
||||
*total = s.num_entries;
|
||||
*maxlen = s.max_len;
|
||||
*avglen = s.avg_len;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
@@ -27,8 +28,6 @@
|
||||
#include "err.h"
|
||||
#define UPPER(c) (islower(c) ? toupper(c) : c)
|
||||
|
||||
/* The variable hash table */
|
||||
#define VAR_HASH_SIZE 67
|
||||
#define VARIABLE ErrMsg[E_VAR]
|
||||
#define VALUE ErrMsg[E_VAL]
|
||||
#define UNDEF ErrMsg[E_UNDEF]
|
||||
@@ -36,7 +35,30 @@
|
||||
static int IntMin = INT_MIN;
|
||||
static int IntMax = INT_MAX;
|
||||
|
||||
static Var *VHashTbl[VAR_HASH_SIZE];
|
||||
static hash_table VHashTbl;
|
||||
|
||||
static unsigned int VarHashFunc(void *x)
|
||||
{
|
||||
Var *v = (Var *) x;
|
||||
return HashVal_ignorecase(v->name);
|
||||
}
|
||||
|
||||
static int VarCompareFunc(void *a, void *b)
|
||||
{
|
||||
Var *x = (Var *) a;
|
||||
Var *y = (Var *) b;
|
||||
return StrCmpi(x->name, y->name);
|
||||
}
|
||||
|
||||
void
|
||||
InitVars(void)
|
||||
{
|
||||
if (hash_table_init(&VHashTbl, offsetof(Var, link),
|
||||
VarHashFunc, VarCompareFunc) < 0) {
|
||||
fprintf(stderr, "Unable to initialize variable hash table: Out of memory. Exiting.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static double
|
||||
strtod_in_c_locale(char const *str, char **endptr)
|
||||
@@ -455,11 +477,11 @@ static int time_sep_func(int do_set, Value *val)
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* HashVal */
|
||||
/* Given a string, compute the hash value. */
|
||||
/* HashVal_ignorecase */
|
||||
/* Given a string, compute the hash value case-insensitively */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
unsigned int HashVal(char const *str)
|
||||
unsigned int HashVal_ignorecase(char const *str)
|
||||
{
|
||||
unsigned int h = 0, high;
|
||||
while(*str) {
|
||||
@@ -483,31 +505,22 @@ unsigned int HashVal(char const *str)
|
||||
/***************************************************************/
|
||||
Var *FindVar(char const *str, int create)
|
||||
{
|
||||
register int h;
|
||||
register Var *v;
|
||||
register Var *prev;
|
||||
Var *v;
|
||||
Var candidate;
|
||||
StrnCpy(candidate.name, str, VAR_NAME_LEN);
|
||||
|
||||
h = HashVal(str) % VAR_HASH_SIZE;
|
||||
v = VHashTbl[h];
|
||||
prev = NULL;
|
||||
v = (Var *) hash_table_find(&VHashTbl, &candidate);
|
||||
if (v != NULL || !create) return v;
|
||||
|
||||
while(v) {
|
||||
if (! StrinCmp(str, v->name, VAR_NAME_LEN)) return v;
|
||||
prev = v;
|
||||
v = v-> next;
|
||||
}
|
||||
if (!create) return v;
|
||||
|
||||
/* Create the variable */
|
||||
/* Create the variable */
|
||||
v = NEW(Var);
|
||||
if (!v) return v;
|
||||
v->next = NULL;
|
||||
v->v.type = INT_TYPE;
|
||||
v->v.v.val = 0;
|
||||
v->preserve = 0;
|
||||
StrnCpy(v->name, str, VAR_NAME_LEN);
|
||||
|
||||
if (prev) prev->next = v; else VHashTbl[h] = v;
|
||||
hash_table_insert(&VHashTbl, v);
|
||||
return v;
|
||||
}
|
||||
|
||||
@@ -520,23 +533,12 @@ Var *FindVar(char const *str, int create)
|
||||
/***************************************************************/
|
||||
int DeleteVar(char const *str)
|
||||
{
|
||||
register int h;
|
||||
register Var *v;
|
||||
register Var *prev;
|
||||
Var *v;
|
||||
|
||||
h = HashVal(str) % VAR_HASH_SIZE;
|
||||
v = VHashTbl[h];
|
||||
prev = NULL;
|
||||
|
||||
while(v) {
|
||||
if (! StrinCmp(str, v->name, VAR_NAME_LEN)) break;
|
||||
prev = v;
|
||||
v = v-> next;
|
||||
}
|
||||
v = FindVar(str, 0);
|
||||
if (!v) return E_NOSUCH_VAR;
|
||||
DestroyValue(v->v);
|
||||
if (prev) prev->next = v->next; else VHashTbl[h] = v->next;
|
||||
free(v);
|
||||
hash_table_delete(&VHashTbl, v);
|
||||
return OK;
|
||||
}
|
||||
|
||||
@@ -725,19 +727,14 @@ int DoDump(ParsePtr p)
|
||||
/***************************************************************/
|
||||
void DumpVarTable(void)
|
||||
{
|
||||
register Var *v;
|
||||
register int i;
|
||||
Var *v;
|
||||
|
||||
fprintf(ErrFp, "%s %s\n\n", VARIABLE, VALUE);
|
||||
|
||||
for (i=0; i<VAR_HASH_SIZE; i++) {
|
||||
v = VHashTbl[i];
|
||||
while(v) {
|
||||
fprintf(ErrFp, "%s ", v->name);
|
||||
PrintValue(&(v->v), ErrFp);
|
||||
fprintf(ErrFp, "\n");
|
||||
v = v->next;
|
||||
}
|
||||
hash_table_for_each(v, &VHashTbl) {
|
||||
fprintf(ErrFp, "%s ", v->name);
|
||||
PrintValue(&(v->v), ErrFp);
|
||||
fprintf(ErrFp, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -751,27 +748,22 @@ void DumpVarTable(void)
|
||||
/***************************************************************/
|
||||
void DestroyVars(int all)
|
||||
{
|
||||
int i;
|
||||
Var *v, *next, *prev;
|
||||
Var *v;
|
||||
Var *next;
|
||||
|
||||
for (i=0; i<VAR_HASH_SIZE; i++) {
|
||||
v = VHashTbl[i];
|
||||
VHashTbl[i] = NULL;
|
||||
prev = NULL;
|
||||
while(v) {
|
||||
if (all || !v->preserve) {
|
||||
DestroyValue(v->v);
|
||||
next = v->next;
|
||||
free(v);
|
||||
} else {
|
||||
if (prev) prev->next = v;
|
||||
else VHashTbl[i] = v;
|
||||
prev = v;
|
||||
next = v->next;
|
||||
v->next = NULL;
|
||||
}
|
||||
v = next;
|
||||
v = hash_table_next(&VHashTbl, NULL);
|
||||
while(v) {
|
||||
next = hash_table_next(&VHashTbl, v);
|
||||
if (all || !v->preserve) {
|
||||
DestroyValue(v->v);
|
||||
hash_table_delete_no_resize(&VHashTbl, v);
|
||||
free(v);
|
||||
}
|
||||
v = next;
|
||||
}
|
||||
if (all) {
|
||||
hash_table_free(&VHashTbl);
|
||||
InitVars();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -967,6 +959,44 @@ static SysVar SysVarArr[] = {
|
||||
|
||||
#define NUMSYSVARS ( sizeof(SysVarArr) / sizeof(SysVar) )
|
||||
static void DumpSysVar (char const *name, const SysVar *v);
|
||||
|
||||
static void HandleTranslatableVariable(char **var)
|
||||
{
|
||||
if (var == (char **) &DynamicAgo) InsertTranslation("ago", *var);
|
||||
else if (var == (char **) &DynamicAm) InsertTranslation("am", *var);
|
||||
else if (var == (char **) &DynamicAnd) InsertTranslation("and", *var);
|
||||
else if (var == (char **) &DynamicAt) InsertTranslation("at", *var);
|
||||
else if (var == (char **) &DynamicFromnow) InsertTranslation("from now", *var);
|
||||
else if (var == (char **) &DynamicHour) InsertTranslation("hour", *var);
|
||||
else if (var == (char **) &DynamicIs) InsertTranslation("is", *var);
|
||||
else if (var == (char **) &DynamicMinute) InsertTranslation("minute", *var);
|
||||
else if (var == (char **) &DynamicNow) InsertTranslation("now", *var);
|
||||
else if (var == (char **) &DynamicOn) InsertTranslation("on", *var);
|
||||
else if (var == (char **) &DynamicPm) InsertTranslation("pm", *var);
|
||||
else if (var == (char **) &DynamicToday) InsertTranslation("today", *var);
|
||||
else if (var == (char **) &DynamicTomorrow) InsertTranslation("tomorrow", *var);
|
||||
else if (var == (char **) &DynamicWas) InsertTranslation("was", *var);
|
||||
else if (var == (char **) &DynamicMonthName[0]) InsertTranslation("January", *var);
|
||||
else if (var == (char **) &DynamicMonthName[1]) InsertTranslation("February", *var);
|
||||
else if (var == (char **) &DynamicMonthName[2]) InsertTranslation("March", *var);
|
||||
else if (var == (char **) &DynamicMonthName[3]) InsertTranslation("April", *var);
|
||||
else if (var == (char **) &DynamicMonthName[4]) InsertTranslation("May", *var);
|
||||
else if (var == (char **) &DynamicMonthName[5]) InsertTranslation("June", *var);
|
||||
else if (var == (char **) &DynamicMonthName[6]) InsertTranslation("July", *var);
|
||||
else if (var == (char **) &DynamicMonthName[7]) InsertTranslation("August", *var);
|
||||
else if (var == (char **) &DynamicMonthName[8]) InsertTranslation("September", *var);
|
||||
else if (var == (char **) &DynamicMonthName[9]) InsertTranslation("October", *var);
|
||||
else if (var == (char **) &DynamicMonthName[10]) InsertTranslation("November", *var);
|
||||
else if (var == (char **) &DynamicMonthName[11]) InsertTranslation("December", *var);
|
||||
else if (var == (char **) &DynamicDayName[0]) InsertTranslation("Monday", *var);
|
||||
else if (var == (char **) &DynamicDayName[1]) InsertTranslation("Tuesday", *var);
|
||||
else if (var == (char **) &DynamicDayName[2]) InsertTranslation("Wednesday", *var);
|
||||
else if (var == (char **) &DynamicDayName[3]) InsertTranslation("Thursday", *var);
|
||||
else if (var == (char **) &DynamicDayName[4]) InsertTranslation("Friday", *var);
|
||||
else if (var == (char **) &DynamicDayName[5]) InsertTranslation("Saturday", *var);
|
||||
else if (var == (char **) &DynamicDayName[6]) InsertTranslation("Sunday", *var);
|
||||
|
||||
}
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* SetSysVar */
|
||||
@@ -1005,6 +1035,7 @@ int SetSysVar(char const *name, Value *value)
|
||||
v->been_malloced = 1;
|
||||
*((char **) v->value) = value->v.str;
|
||||
value->type = ERR_TYPE; /* So that it's not accidentally freed */
|
||||
HandleTranslatableVariable((char **) v->value);
|
||||
} else {
|
||||
if (v->max != ANY && value->v.val > v->max) return E_2HIGH;
|
||||
if (v->min != ANY && value->v.val < v->min) return E_2LOW;
|
||||
@@ -1214,24 +1245,9 @@ print_sysvar_tokens(void)
|
||||
void
|
||||
get_var_hash_stats(int *total, int *maxlen, double *avglen)
|
||||
{
|
||||
int len;
|
||||
int i;
|
||||
Var *v;
|
||||
|
||||
*maxlen = 0;
|
||||
*total = 0;
|
||||
|
||||
for (i=0; i<VAR_HASH_SIZE; i++) {
|
||||
len = 0;
|
||||
v = VHashTbl[i];
|
||||
while(v) {
|
||||
len++;
|
||||
(*total)++;
|
||||
v = v->next;
|
||||
}
|
||||
if (len > *maxlen) {
|
||||
*maxlen = len;
|
||||
}
|
||||
}
|
||||
*avglen = (double) *total / (double) VAR_HASH_SIZE;
|
||||
struct hash_table_stats s;
|
||||
hash_table_get_stats(&VHashTbl, &s);
|
||||
*total = s.num_entries;
|
||||
*maxlen = s.max_len;
|
||||
*avglen = s.avg_len;
|
||||
}
|
||||
|
||||
+10235
-127
File diff suppressed because it is too large
Load Diff
@@ -1184,8 +1184,71 @@ REM MSG Hello
|
||||
FSET msgsuffix(x) char(8) + " on the same line"
|
||||
REM MSG Hello
|
||||
|
||||
# Test TRANSLATE
|
||||
set a _("Hello")
|
||||
|
||||
TRANSLATE "Hello" "Goodbye"
|
||||
set a _("Hello")
|
||||
|
||||
TRANSLATE "Hello" "Hallo!!!!!"
|
||||
set a _("Hello")
|
||||
|
||||
TRANSLATE DUMP
|
||||
TRANSLATE CLEAR
|
||||
TRANSLATE DUMP
|
||||
set a _("Hello")
|
||||
|
||||
TRANSLATE "Wookie" "Bar"
|
||||
TRANSLATE "Bar" "Quux"
|
||||
TRANSLATE "Quux" "OOOGHY"
|
||||
|
||||
# Delete Bar translation
|
||||
TRANSLATE "Bar"
|
||||
|
||||
set a _("Wookie")
|
||||
set a _("Bar")
|
||||
set a _("Quux")
|
||||
|
||||
SET $ago "Txago"
|
||||
SET $am "Txam"
|
||||
SET $and "Txand"
|
||||
SET $at "Txat"
|
||||
SET $fromnow "Txfromnow"
|
||||
SET $hour "Txhour"
|
||||
SET $is "Txis"
|
||||
SET $minute "Txminute"
|
||||
SET $now "Txnow"
|
||||
SET $on "Txon"
|
||||
SET $pm "Txpm"
|
||||
SET $today "Txtoday"
|
||||
SET $tomorrow "Txtomorrow"
|
||||
SET $was "Txwas"
|
||||
SET $January "TxJanuary"
|
||||
SET $February "TxFebruary"
|
||||
SET $March "TxMarch"
|
||||
SET $April "TxApril"
|
||||
SET $May "TxMay"
|
||||
SET $June "TxJune"
|
||||
SET $July "TxJuly"
|
||||
SET $August "TxAugust"
|
||||
SET $September "TxSeptember"
|
||||
SET $October "TxOctober"
|
||||
SET $November "TxNovember"
|
||||
SET $December "TxDecember"
|
||||
SET $Monday "TxMonday"
|
||||
SET $Tuesday "TxTuesday"
|
||||
SET $Wednesday "TxWednesday"
|
||||
SET $Thursday "TxThursday"
|
||||
SET $Friday "TxFriday"
|
||||
SET $Saturday "TxSaturday"
|
||||
SET $Sunday "TxSunday"
|
||||
TRANSLATE DUMP
|
||||
|
||||
DO torture-test.rem
|
||||
|
||||
# Output expression-node stats
|
||||
DEBUG +s
|
||||
|
||||
# Don't want Remind to queue reminders
|
||||
EXIT
|
||||
|
||||
|
||||
+310011
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user