Add $DedupeReminders global variable.

This commit is contained in:
Dianne Skoll
2024-11-12 09:26:31 -05:00
parent aec17b3243
commit e0fde98410
13 changed files with 346 additions and 6 deletions

View File

@@ -2520,6 +2520,26 @@ This variable can be set only to "/" or "-". It holds the character
used to separate portions of a date when \fBRemind\fR prints a DATE or used to separate portions of a date when \fBRemind\fR prints a DATE or
DATETIME value. DATETIME value.
.TP .TP
.B $DedupeReminders
If this variable is set to 1, then Remind will suppress duplicate
reminders. A given reminder is considered to be a duplicate of a
previous one if it has the \fIexact\fR same trigger date, trigger
time, and body. By default, this variable is set to 0 and Remind does
not suppress duplicate reminders.
.RS
.PP
As an example, consider the following reminder file:
.nf
SET $DedupeReminders 1
REM Wednesday MSG Phooey
REM 20 MSG Phooey
.fi
.PP
On Wednesday, 20 November 2024, only \fIone\fR "Phooey" will be issued.
In December of 2024, "Phooey" will be issued every Wednesday as well
as on Friday, 20 December 2024
.RE
.TP
.B $DefaultColor .B $DefaultColor
This variable can be set to a string that has the form of three This variable can be set to a string that has the form of three
space-separated numbers. Each number must be an integer from 0 to space-separated numbers. Each number must be an integer from 0 to

View File

@@ -26,9 +26,10 @@ MANS= $(srcdir)/../man/rem2ps.1 $(srcdir)/../man/remind.1 \
.SUFFIXES: .SUFFIXES:
.SUFFIXES: .c .o .SUFFIXES: .c .o
REMINDSRCS= calendar.c dynbuf.c dorem.c dosubst.c expr.c files.c funcs.c \ REMINDSRCS= calendar.c dedupe.c dynbuf.c dorem.c dosubst.c expr.c \
globals.c hbcal.c init.c main.c md5.c moon.c omit.c queue.c \ files.c funcs.c globals.c hbcal.c init.c main.c md5.c \
sort.c token.c trigger.c userfns.c utils.c var.c moon.c omit.c queue.c sort.c token.c trigger.c \
userfns.c utils.c var.c
REMINDHDRS=config.h custom.h dynbuf.h err.h globals.h lang.h \ REMINDHDRS=config.h custom.h dynbuf.h err.h globals.h lang.h \
md5.h protos.h rem2ps.h types.h version.h md5.h protos.h rem2ps.h types.h version.h

View File

@@ -2054,8 +2054,6 @@ static int DoCalRem(ParsePtr p, int col)
if ((dse == DSEToday) || if ((dse == DSEToday) ||
(DoSimpleCalDelta && (DoSimpleCalDelta &&
ShouldTriggerReminder(&trig, &tim, dse, &err))) { ShouldTriggerReminder(&trig, &tim, dse, &err))) {
NumTriggered++;
/* The parse_ptr should not be nested, but just in case... */ /* The parse_ptr should not be nested, but just in case... */
if (!p->isnested) { if (!p->isnested) {
if (DBufPuts(&raw_buf, p->pos) != OK) { if (DBufPuts(&raw_buf, p->pos) != OK) {
@@ -2152,9 +2150,20 @@ static int DoCalRem(ParsePtr p, int col)
} }
} }
s = DBufValue(&obuf); s = DBufValue(&obuf);
if (DedupeReminders) {
if (ShouldDedupe(dse, tim.ttime, DBufValue(&obuf))) {
DBufFree(&obuf);
DBufFree(&raw_buf);
DBufFree(&pre_buf);
FreeTrig(&trig);
return OK;
}
}
if (!DoSimpleCalendar) while (isempty(*s)) s++; if (!DoSimpleCalendar) while (isempty(*s)) s++;
DBufPuts(&pre_buf, s); DBufPuts(&pre_buf, s);
s = DBufValue(&pre_buf); s = DBufValue(&pre_buf);
NumTriggered++;
e = NEW(CalEntry); e = NEW(CalEntry);
if (!e) { if (!e) {
DBufFree(&obuf); DBufFree(&obuf);

175
src/dedupe.c Normal file
View File

@@ -0,0 +1,175 @@
/***************************************************************/
/* */
/* DEDUPE.C */
/* */
/* Code to suppress duplicate reminders */
/* */
/* This file is part of REMIND. */
/* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */
/* */
/***************************************************************/
#include "config.h"
#include "types.h"
#include "globals.h"
#include "protos.h"
#include <stdlib.h>
#include <string.h>
#define DEDUPE_HASH_SLOTS 64
typedef struct dedupe_entry {
struct dedupe_entry *next;
int trigger_date;
int trigger_time;
char const *body;
} DedupeEntry;
static DedupeEntry *DedupeTable[DEDUPE_HASH_SLOTS];
/***************************************************************/
/* */
/* FreeDedupeEntry */
/* */
/* Free a DedupeEntry object */
/* */
/***************************************************************/
static void
FreeDedupeEntry(DedupeEntry *e)
{
if (e->body) {
free((char *) e->body);
}
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 */
/* */
/* Check if we have a dedupe entry for a given date and body */
/* */
/***************************************************************/
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;
}
/***************************************************************/
/* */
/* InsertDedupeEntry */
/* */
/* Insert a dedupe entry for given date and body. */
/* */
/***************************************************************/
static void
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? */
}
e->trigger_date = trigger_date;
e->trigger_time = trigger_time;
e->body = strdup(body);
if (!e->body) {
free(e);
return;
}
e->next = DedupeTable[bucket];
DedupeTable[bucket] = e;
}
/***************************************************************/
/* */
/* ShouldDedupe */
/* */
/* Returns 1 if we've already issued this exact reminder; 0 */
/* otherwise. If it returns 0, remembers that we have seen */
/* the reminder */
/* */
/***************************************************************/
int
ShouldDedupe(int trigger_date, int trigger_time, char const *body)
{
DedupeEntry *e = FindDedupeEntry(trigger_date, trigger_time, body);
if (e) {
return 1;
}
InsertDedupeEntry(trigger_date, trigger_time, body);
return 0;
}
/***************************************************************/
/* */
/* ClearDedupeTable */
/* */
/* Free all the storage used by the dedupe table */
/* */
/***************************************************************/
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;
}
}
/***************************************************************/
/* */
/* InitDedupeTable */
/* */
/* Initialize the dedupe table at program startup */
/* */
/***************************************************************/
void
InitDedupeTable(void)
{
for (int i=0; i<DEDUPE_HASH_SLOTS; i++) {
DedupeTable[i] = NULL;
}
}

View File

@@ -1315,6 +1315,14 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queue
} }
} }
/* If this is a dupe and we are de-duping, do nothing */
if (DedupeReminders) {
if (ShouldDedupe(dse, tim->ttime, DBufValue(&buf))) {
DBufFree(&buf);
return OK;
}
}
/* If we are sorting, just queue it up in the sort buffer */ /* If we are sorting, just queue it up in the sort buffer */
if (SortByDate) { if (SortByDate) {
if (InsertIntoSortBuffer(dse, tim->ttime, DBufValue(&buf), if (InsertIntoSortBuffer(dse, tim->ttime, DBufValue(&buf),

View File

@@ -139,6 +139,7 @@ EXTERN INIT( int UseVTColors, 0);
EXTERN INIT( int Use256Colors, 0); EXTERN INIT( int Use256Colors, 0);
EXTERN INIT( int UseTrueColors, 0); EXTERN INIT( int UseTrueColors, 0);
EXTERN INIT( int TerminalBackground, TERMINAL_BACKGROUND_UNKNOWN); EXTERN INIT( int TerminalBackground, TERMINAL_BACKGROUND_UNKNOWN);
EXTERN INIT( int DedupeReminders, 0);
/* Latitude and longitude */ /* Latitude and longitude */
EXTERN INIT( int LatDeg, 0); EXTERN INIT( int LatDeg, 0);

View File

@@ -192,6 +192,8 @@ void InitRemind(int argc, char const *argv[])
PurgeFP = NULL; PurgeFP = NULL;
InitDedupeTable();
/* Make sure remind is not installed set-uid or set-gid */ /* Make sure remind is not installed set-uid or set-gid */
if (getgid() != getegid() || if (getgid() != getegid() ||
getuid() != geteuid()) { getuid() != geteuid()) {

View File

@@ -226,6 +226,7 @@ PerIterationInit(void)
DefaultColorB = -1; DefaultColorB = -1;
NumTriggered = 0; NumTriggered = 0;
ClearLastTriggers(); ClearLastTriggers();
ClearDedupeTable();
} }
/***************************************************************/ /***************************************************************/

View File

@@ -249,3 +249,8 @@ void print_builtinfunc_tokens(void);
void print_remind_tokens(void); void print_remind_tokens(void);
void get_var_hash_stats(int *total, int *maxlen, double *avglen); void get_var_hash_stats(int *total, int *maxlen, double *avglen);
void get_userfunc_hash_stats(int *total, int *maxlen, double *avglen); void get_userfunc_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);

View File

@@ -864,6 +864,7 @@ static SysVar SysVarArr[] = {
{"DateSep", 1, SPECIAL_TYPE, date_sep_func, 0, 0 }, {"DateSep", 1, SPECIAL_TYPE, date_sep_func, 0, 0 },
{"DateTimeSep", 1, SPECIAL_TYPE, datetime_sep_func, 0, 0 }, {"DateTimeSep", 1, SPECIAL_TYPE, datetime_sep_func, 0, 0 },
{"December", 1, STR_TYPE, &DynamicMonthName[11],0, 0 }, {"December", 1, STR_TYPE, &DynamicMonthName[11],0, 0 },
{"DedupeReminders",1, INT_TYPE, &DedupeReminders, 0, 1 },
{"DefaultColor", 1, SPECIAL_TYPE, default_color_func, 0, 0 }, {"DefaultColor", 1, SPECIAL_TYPE, default_color_func, 0, 0 },
{"DefaultPrio", 1, INT_TYPE, &DefaultPrio, 0, 9999 }, {"DefaultPrio", 1, INT_TYPE, &DefaultPrio, 0, 9999 },
{"DefaultTDelta", 1, INT_TYPE, &DefaultTDelta, 0, 1440 }, {"DefaultTDelta", 1, INT_TYPE, &DefaultTDelta, 0, 1440 },

25
tests/dedupe.rem Normal file
View File

@@ -0,0 +1,25 @@
SET $DedupeReminders 1
SET $SuppressLRM 1
REM Wednesday MSG foo
FLUSH
REM 8 MSG foo
FLUSH
REM Wednesday MSG Bar
FLUSH
REM Wednesday MSG Bar
FLUSH
REM Wednesday AT 23:59 +1440 MSG with_time
FLUSH
REM AT 23:59 +1440 MSG with_time
FLUSH
REM AT 23:58 +1440 MSG with_time
FLUSH
REM 8 RUN echo %"foo%"
FLUSH
REM 8 RUN echo %"foo%"
FLUSH
REM Wed RUN echo %"foo%"
FLUSH
REM Wed RUN echo %"bar%"
FLUSH

View File

@@ -586,6 +586,10 @@ rm -f ../tests/once.timestamp
# Newlines in calendar output # Newlines in calendar output
(echo 'REM 16 MSG foo%_bar%_baz wookie quux apple %_ %_ %_ blech'; echo "REM 16 MSG ANOTHER") | ../src/remind -c -w80 - 1 sep 1990 >> ../tests/test.out 2>&1 (echo 'REM 16 MSG foo%_bar%_baz wookie quux apple %_ %_ %_ blech'; echo "REM 16 MSG ANOTHER") | ../src/remind -c -w80 - 1 sep 1990 >> ../tests/test.out 2>&1
# Dedupe feature
../src/remind -c ../tests/dedupe.rem 1 November 2023 >> ../tests/test.out 2>&1
../src/remind -q ../tests/dedupe.rem 8 November 2023 >> ../tests/test.out 2>&1
# Remove references to SysInclude, which is build-specific # Remove references to SysInclude, which is build-specific
grep -F -v '$SysInclude' < ../tests/test.out > ../tests/test.out.1 && mv -f ../tests/test.out.1 ../tests/test.out grep -F -v '$SysInclude' < ../tests/test.out > ../tests/test.out.1 && mv -f ../tests/test.out.1 ../tests/test.out

View File

@@ -2729,6 +2729,7 @@ Variable Value
$DateSep "-" $DateSep "-"
$DateTimeSep "@" $DateTimeSep "@"
$December "December" $December "December"
$DedupeReminders 0 [0, 1]
$DefaultColor "-1 -1 -1" $DefaultColor "-1 -1 -1"
$DefaultPrio 5000 [0, 9999] $DefaultPrio 5000 [0, 9999]
$DefaultTDelta 0 [0, 1440] $DefaultTDelta 0 [0, 1440]
@@ -13377,7 +13378,93 @@ No reminders.
| | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | |
+----------+----------+----------+----------+----------+----------+----------+ +----------+----------+----------+----------+----------+----------+----------+
+----------------------------------------------------------------------------+
| November 2023 |
+----------+----------+----------+----------+----------+----------+----------+
| Sunday | Monday | Tuesday |Wednesday | Thursday | Friday | Saturday |
+----------+----------+----------+----------+----------+----------+----------+
| | | |1 |2 |3 |4 |
| | | | | | | |
| | | |11:58pm |11:58pm |11:58pm |11:58pm |
| | | |with_time |with_time |with_time |with_time |
| | | | | | | |
| | | |11:59pm |11:59pm |11:59pm |11:59pm |
| | | |with_time |with_time |with_time |with_time |
| | | | | | | |
| | | |foo | | | |
| | | | | | | |
| | | |Bar | | | |
| | | | | | | |
| | | |bar | | | |
+----------+----------+----------+----------+----------+----------+----------+
|5 |6 |7 |8 |9 |10 |11 |
| | | | | | | |
|11:58pm |11:58pm |11:58pm |11:58pm |11:58pm |11:58pm |11:58pm |
|with_time |with_time |with_time |with_time |with_time |with_time |with_time |
| | | | | | | |
|11:59pm |11:59pm |11:59pm |11:59pm |11:59pm |11:59pm |11:59pm |
|with_time |with_time |with_time |with_time |with_time |with_time |with_time |
| | | | | | | |
| | | |foo | | | |
| | | | | | | |
| | | |Bar | | | |
| | | | | | | |
| | | |bar | | | |
+----------+----------+----------+----------+----------+----------+----------+
|12 |13 |14 |15 |16 |17 |18 |
| | | | | | | |
|11:58pm |11:58pm |11:58pm |11:58pm |11:58pm |11:58pm |11:58pm |
|with_time |with_time |with_time |with_time |with_time |with_time |with_time |
| | | | | | | |
|11:59pm |11:59pm |11:59pm |11:59pm |11:59pm |11:59pm |11:59pm |
|with_time |with_time |with_time |with_time |with_time |with_time |with_time |
| | | | | | | |
| | | |foo | | | |
| | | | | | | |
| | | |Bar | | | |
| | | | | | | |
| | | |bar | | | |
+----------+----------+----------+----------+----------+----------+----------+
|19 |20 |21 |22 |23 |24 |25 |
| | | | | | | |
|11:58pm |11:58pm |11:58pm |11:58pm |11:58pm |11:58pm |11:58pm |
|with_time |with_time |with_time |with_time |with_time |with_time |with_time |
| | | | | | | |
|11:59pm |11:59pm |11:59pm |11:59pm |11:59pm |11:59pm |11:59pm |
|with_time |with_time |with_time |with_time |with_time |with_time |with_time |
| | | | | | | |
| | | |foo | | | |
| | | | | | | |
| | | |Bar | | | |
| | | | | | | |
| | | |bar | | | |
+----------+----------+----------+----------+----------+----------+----------+
|26 |27 |28 |29 |30 | | |
| | | | | | | |
|11:58pm |11:58pm |11:58pm |11:58pm |11:58pm | | |
|with_time |with_time |with_time |with_time |with_time | | |
| | | | | | | |
|11:59pm |11:59pm |11:59pm |11:59pm |11:59pm | | |
|with_time |with_time |with_time |with_time |with_time | | |
| | | | | | | |
| | | |foo | | | |
| | | | | | | |
| | | |Bar | | | |
| | | | | | | |
| | | |bar | | | |
+----------+----------+----------+----------+----------+----------+----------+
Reminders for Wednesday, 8th November, 2023:
foo
Bar
with_time
with_time
foo
bar
# Remind Tokens # Remind Tokens
addomit addomit
@@ -13623,6 +13710,7 @@ $Daemon
$DateSep $DateSep
$DateTimeSep $DateTimeSep
$December $December
$DedupeReminders
$DefaultColor $DefaultColor
$DefaultPrio $DefaultPrio
$DefaultTDelta $DefaultTDelta