mirror of
https://salsa.debian.org/dskoll/remind.git
synced 2026-04-17 14:59:20 +02:00
Implement TRANSLATE keyword.
This commit is contained in:
@@ -29,7 +29,7 @@ MANS= $(srcdir)/../man/rem2ps.1 $(srcdir)/../man/remind.1 \
|
||||
REMINDSRCS= calendar.c dedupe.c dynbuf.c dorem.c dosubst.c expr.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 trigger.c userfns.c utils.c var.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 hashtab.h \
|
||||
lang.h md5.h protos.h rem2ps.h types.h version.h
|
||||
|
||||
@@ -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;
|
||||
|
||||
20
src/funcs.c
20
src/funcs.c
@@ -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 */
|
||||
|
||||
@@ -185,6 +185,8 @@ void InitRemind(int argc, char const *argv[])
|
||||
/* 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);
|
||||
|
||||
@@ -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 {
|
||||
@@ -609,6 +612,9 @@ int ParseQuotedString(ParsePtr p, DynamicBuffer *dbuf)
|
||||
DBufFree(dbuf);
|
||||
c = ParseNonSpaceChar(p, &err, 0);
|
||||
if (err) return err;
|
||||
if (!c) {
|
||||
return E_EOLN;
|
||||
}
|
||||
if (c != '"') {
|
||||
return E_MISS_QUOTE;
|
||||
}
|
||||
|
||||
@@ -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,7 @@ int DoDebug (ParsePtr p);
|
||||
int DoBanner (ParsePtr p);
|
||||
int DoRun (ParsePtr p);
|
||||
int DoExpr (ParsePtr p);
|
||||
int DoTranslate (ParsePtr p);
|
||||
int DoErrMsg (ParsePtr p);
|
||||
int ClearGlobalOmits (void);
|
||||
int DoClear (ParsePtr p);
|
||||
@@ -253,10 +255,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 },
|
||||
|
||||
271
src/trans.c
Normal file
271
src/trans.c
Normal file
@@ -0,0 +1,271 @@
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* 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)
|
||||
{
|
||||
XlateItem *item = NEW(XlateItem);
|
||||
if (!item) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Allocate the string space in ONE go! */
|
||||
char *s = malloc(strlen(orig) + strlen(translated) + 2);
|
||||
if (!s) {
|
||||
free(item);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
item->orig = s;
|
||||
item->translated = s + strlen(orig) + 1;
|
||||
strcpy(item->orig, orig);
|
||||
strcpy(item->translated, translated);
|
||||
return item;
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* FreeXlateItem - Free a translation item */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
static void
|
||||
FreeXlateItem(XlateItem *item)
|
||||
{
|
||||
if (!item) return;
|
||||
|
||||
if (item->orig) {
|
||||
free(item->orig);
|
||||
}
|
||||
free(item);
|
||||
}
|
||||
|
||||
static void
|
||||
RemoveTranslation(XlateItem *item)
|
||||
{
|
||||
hash_table_delete(&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;
|
||||
}
|
||||
}
|
||||
|
||||
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(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)
|
||||
{
|
||||
hash_table_init(&TranslationTable, offsetof(XlateItem, link),
|
||||
HashXlateItem, CompareXlateItems);
|
||||
}
|
||||
|
||||
static XlateItem *
|
||||
FindTranslation(char const *orig)
|
||||
{
|
||||
XlateItem *item;
|
||||
XlateItem candidate;
|
||||
candidate.orig = (char *) orig;
|
||||
item = hash_table_find(&TranslationTable, &candidate);
|
||||
return item;
|
||||
}
|
||||
|
||||
static 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;
|
||||
}
|
||||
@@ -218,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 */
|
||||
|
||||
@@ -5966,6 +5966,43 @@ Leaving UserFN msgsuffix(5000) => "\b on the same line"
|
||||
Hello on the same line
|
||||
|
||||
|
||||
# Test TRANSLATE
|
||||
set a _("Hello")
|
||||
_("Hello") => "Hello"
|
||||
|
||||
TRANSLATE "Hello" "Goodbye"
|
||||
set a _("Hello")
|
||||
_("Hello") => "Goodbye"
|
||||
|
||||
TRANSLATE "Hello" "Hallo!!!!!"
|
||||
set a _("Hello")
|
||||
_("Hello") => "Hallo!!!!!"
|
||||
|
||||
TRANSLATE DUMP
|
||||
"Hello" "Hallo!!!!!"
|
||||
TRANSLATE CLEAR
|
||||
TRANSLATE DUMP
|
||||
set a _("Hello")
|
||||
_("Hello") => "Hello"
|
||||
|
||||
TRANSLATE "Wookie" "Bar"
|
||||
TRANSLATE "Bar" "Quux"
|
||||
TRANSLATE "Quux" "OOOGHY"
|
||||
|
||||
# Delete Bar translation
|
||||
TRANSLATE "Bar"
|
||||
|
||||
set a _("Wookie")
|
||||
_("Wookie") => "Bar"
|
||||
set a _("Bar")
|
||||
_("Bar") => "Bar"
|
||||
set a _("Quux")
|
||||
_("Quux") => "OOOGHY"
|
||||
|
||||
TRANSLATE DUMP
|
||||
"Wookie" "Bar"
|
||||
"Quux" "OOOGHY"
|
||||
|
||||
# Output expression-node stats
|
||||
DEBUG +s
|
||||
# Don't want Remind to queue reminders
|
||||
@@ -5973,6 +6010,7 @@ EXIT
|
||||
Var hash: total = 141; maxlen = 4; avglen = 1.785
|
||||
Func hash: total = 17; maxlen = 3; avglen = 1.000
|
||||
Dedup hash: total = 0; maxlen = 0; avglen = 0.000
|
||||
Trans hash: total = 2; maxlen = 1; avglen = 0.118
|
||||
Expression nodes allocated: 128
|
||||
Expression nodes high-water: 74
|
||||
Expression nodes leaked: 0
|
||||
@@ -13427,6 +13465,7 @@ No reminders.
|
||||
Var hash: total = 1; maxlen = 1; avglen = 0.059
|
||||
Func hash: total = 0; maxlen = 0; avglen = 0.000
|
||||
Dedup hash: total = 0; maxlen = 0; avglen = 0.000
|
||||
Trans hash: total = 0; maxlen = 0; avglen = 0.000
|
||||
Expression nodes allocated: 512
|
||||
Expression nodes high-water: 499
|
||||
Expression nodes leaked: 0
|
||||
@@ -13666,6 +13705,8 @@ special
|
||||
tag
|
||||
third
|
||||
through
|
||||
trans
|
||||
translate
|
||||
unset
|
||||
until
|
||||
warn
|
||||
@@ -13715,6 +13756,7 @@ wednesday
|
||||
|
||||
# Built-in Functions
|
||||
|
||||
_
|
||||
abs
|
||||
access
|
||||
adawn
|
||||
|
||||
@@ -1184,6 +1184,33 @@ 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")
|
||||
|
||||
TRANSLATE DUMP
|
||||
|
||||
# Output expression-node stats
|
||||
DEBUG +s
|
||||
# Don't want Remind to queue reminders
|
||||
|
||||
Reference in New Issue
Block a user