mirror of
https://salsa.debian.org/dskoll/remind.git
synced 2026-04-16 06:18:47 +02:00
Add $DedupeReminders global variable.
This commit is contained in:
@@ -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
|
||||
DATETIME value.
|
||||
.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
|
||||
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
|
||||
|
||||
@@ -26,9 +26,10 @@ MANS= $(srcdir)/../man/rem2ps.1 $(srcdir)/../man/remind.1 \
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .c .o
|
||||
|
||||
REMINDSRCS= calendar.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
|
||||
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
|
||||
|
||||
REMINDHDRS=config.h custom.h dynbuf.h err.h globals.h lang.h \
|
||||
md5.h protos.h rem2ps.h types.h version.h
|
||||
|
||||
@@ -2054,8 +2054,6 @@ static int DoCalRem(ParsePtr p, int col)
|
||||
if ((dse == DSEToday) ||
|
||||
(DoSimpleCalDelta &&
|
||||
ShouldTriggerReminder(&trig, &tim, dse, &err))) {
|
||||
NumTriggered++;
|
||||
|
||||
/* The parse_ptr should not be nested, but just in case... */
|
||||
if (!p->isnested) {
|
||||
if (DBufPuts(&raw_buf, p->pos) != OK) {
|
||||
@@ -2152,9 +2150,20 @@ static int DoCalRem(ParsePtr p, int col)
|
||||
}
|
||||
}
|
||||
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++;
|
||||
DBufPuts(&pre_buf, s);
|
||||
s = DBufValue(&pre_buf);
|
||||
NumTriggered++;
|
||||
e = NEW(CalEntry);
|
||||
if (!e) {
|
||||
DBufFree(&obuf);
|
||||
|
||||
175
src/dedupe.c
Normal file
175
src/dedupe.c
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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 (SortByDate) {
|
||||
if (InsertIntoSortBuffer(dse, tim->ttime, DBufValue(&buf),
|
||||
|
||||
@@ -139,6 +139,7 @@ EXTERN INIT( int UseVTColors, 0);
|
||||
EXTERN INIT( int Use256Colors, 0);
|
||||
EXTERN INIT( int UseTrueColors, 0);
|
||||
EXTERN INIT( int TerminalBackground, TERMINAL_BACKGROUND_UNKNOWN);
|
||||
EXTERN INIT( int DedupeReminders, 0);
|
||||
|
||||
/* Latitude and longitude */
|
||||
EXTERN INIT( int LatDeg, 0);
|
||||
|
||||
@@ -192,6 +192,8 @@ void InitRemind(int argc, char const *argv[])
|
||||
|
||||
PurgeFP = NULL;
|
||||
|
||||
InitDedupeTable();
|
||||
|
||||
/* Make sure remind is not installed set-uid or set-gid */
|
||||
if (getgid() != getegid() ||
|
||||
getuid() != geteuid()) {
|
||||
|
||||
@@ -226,6 +226,7 @@ PerIterationInit(void)
|
||||
DefaultColorB = -1;
|
||||
NumTriggered = 0;
|
||||
ClearLastTriggers();
|
||||
ClearDedupeTable();
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
|
||||
@@ -249,3 +249,8 @@ void print_builtinfunc_tokens(void);
|
||||
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);
|
||||
|
||||
/* Dedupe code */
|
||||
int ShouldDedupe(int trigger_date, int trigger_time, char const *body);
|
||||
void ClearDedupeTable(void);
|
||||
void InitDedupeTable(void);
|
||||
|
||||
@@ -864,6 +864,7 @@ static SysVar SysVarArr[] = {
|
||||
{"DateSep", 1, SPECIAL_TYPE, date_sep_func, 0, 0 },
|
||||
{"DateTimeSep", 1, SPECIAL_TYPE, datetime_sep_func, 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 },
|
||||
{"DefaultPrio", 1, INT_TYPE, &DefaultPrio, 0, 9999 },
|
||||
{"DefaultTDelta", 1, INT_TYPE, &DefaultTDelta, 0, 1440 },
|
||||
|
||||
25
tests/dedupe.rem
Normal file
25
tests/dedupe.rem
Normal 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
|
||||
@@ -586,6 +586,10 @@ rm -f ../tests/once.timestamp
|
||||
# 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
|
||||
|
||||
# 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
|
||||
grep -F -v '$SysInclude' < ../tests/test.out > ../tests/test.out.1 && mv -f ../tests/test.out.1 ../tests/test.out
|
||||
|
||||
|
||||
@@ -2729,6 +2729,7 @@ Variable Value
|
||||
$DateSep "-"
|
||||
$DateTimeSep "@"
|
||||
$December "December"
|
||||
$DedupeReminders 0 [0, 1]
|
||||
$DefaultColor "-1 -1 -1"
|
||||
$DefaultPrio 5000 [0, 9999]
|
||||
$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
|
||||
|
||||
addomit
|
||||
@@ -13623,6 +13710,7 @@ $Daemon
|
||||
$DateSep
|
||||
$DateTimeSep
|
||||
$December
|
||||
$DedupeReminders
|
||||
$DefaultColor
|
||||
$DefaultPrio
|
||||
$DefaultTDelta
|
||||
|
||||
Reference in New Issue
Block a user