/** Projekt DR - Date Reminder (Hirnstupser) **/
/* Cop. 2000, 2017 asdala.de */
#if defined(__linux__)
#define LINUX
#elif defined(_WIN32)
#define WINDOWS
#else
#error Unbekanntes System!
#endif
#ifdef WINDOWS
#ifdef __LCC__ /* LCCs ANSIC90-Modus versteht windows.h und time.h nicht */
#define MB_ICONERROR 0x10
#define MB_ICONQUESTION 0x20
#define MB_ICONWARNING 0x30
#define MB_ICONINFORMATION 0x40
int __stdcall MessageBoxA(void*,const char*,const char*,unsigned int);
struct tm { int tm_sec,tm_min,tm_hour,tm_mday,tm_mon,tm_year,tm_wday,tm_yday,tm_isdst; };
typedef unsigned long time_t;
time_t time(time_t *_timer);
struct tm *localtime(const time_t *_timer);
time_t mktime(struct tm *_timeptr);
#else
#include <windows.h>
#endif
#include <io.h>
#ifdef MKRES
#include "res.h"
#endif
#define MSGINF MB_ICONINFORMATION
#define MSGWRN MB_ICONWARNING
#define MSGQST MB_ICONQUESTION
#define MSGERR MB_ICONERROR
#endif
#ifdef LINUX
#include <unistd.h>
#include <gtk/gtk.h>
#include "dr.xpm"
#define SUPPRESS_GTKERROR_NO_TRANSIENT_PARENT
#define MSGINF GTK_MESSAGE_INFO
#define MSGWRN GTK_MESSAGE_WARNING
#define MSGQST GTK_MESSAGE_QUESTION
#define MSGERR GTK_MESSAGE_ERROR
#endif
#ifndef MKRES
#define VER_INTERNALNAME "dr"
#define VER_PRODNAME "Hirnstupser"
#define VER_ABOUT ""
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef __LCC__
#include <time.h>
#endif
#define SECS2DAYS(a) ((a)/86400ul)
#define DATNAME "dr.txt"
#define TOKCOLSEP ","
#define TOKCMT ';'
char t0a[11];
const char *fname = DATNAME; /* Global, da in mehreren Funktionen benoetigt */
int gui;
/** GUI? **/
#ifdef WINDOWS
int initgui(void)
{
return !isatty(fileno(stdin));
}
#endif
#ifdef LINUX
int initgui(int argc, char **argv)
{
if (isatty(fileno(stdin)))
return 0;
else
return gtk_init_check(&argc, &argv);
}
#endif
/** Generische Nachricht **/
void message(const char *msg, const char *title, int msgtype)
{
if (gui) {
#ifdef LINUX
GtkWidget *winparent, *dialog;
GdkPixbuf *icon;
#ifdef SUPPRESS_GTKERROR_NO_TRANSIENT_PARENT
winparent = gtk_window_new(GTK_WINDOW_TOPLEVEL); /* Elternfenster nur erzeugen, nicht zeigen */
#else
winparent = NULL;
#endif
dialog = gtk_message_dialog_new(GTK_WINDOW(winparent),
GTK_DIALOG_DESTROY_WITH_PARENT,msgtype,GTK_BUTTONS_CLOSE,"%s",msg);
gtk_window_set_title(GTK_WINDOW(dialog),title);
icon = gdk_pixbuf_new_from_xpm_data(dr_xpm);
gtk_window_set_icon(GTK_WINDOW(dialog),icon);
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
g_object_unref(icon);
#ifdef SUPPRESS_GTKERROR_NO_TRANSIENT_PARENT
gtk_widget_destroy(winparent);
#endif
#endif
#ifdef WINDOWS
MessageBoxA(NULL,msg,title,msgtype);
#endif
}
else {
puts(title);
puts(msg);
}
}
/** Programmhilfe **/
int help(void)
{
message(
VER_ABOUT
"Aufruf: " VER_INTERNALNAME " [-?] [Kalenderdatei]\n"
"\n"
"Ohne Dateiangabe wird " DATNAME " gesucht.\n\n"
"Beispiel einer Kalenderdatei:\n"
"(';' = Kommentar, '?' = Platzhalter aktuelles Datum)\n\n"
"; Datum, Davor, Danach, Nachricht\n"
"14.08.2020, 1, 0, Auto Werkstatt\n"
"12.10.????, 2, 1, Eva Geburtstag\n"
"01.??.????, 2, 1, Abschlag zahlen\n",
VER_PRODNAME " - Hilfe", MSGINF);
return 1;
}
/** Programmfehler **/
int error(const char s[])
{
message(s, VER_PRODNAME " - Fehler", MSGERR);
return 2;
}
/** Zeilenfehler in Kalenderdatei **/
int lnerror(int lnno)
{
char s[120];
sprintf(s, "Formatfehler in %.80s Zeile %d", fname, lnno);
return error(s);
}
/** Jokerfeature: Ersetze '?'-Angaben im Ereignisdatum mit aktuellem Datum **/
int expand_datestr(const char *smp, char *s)
{
if (!s || !smp)
return 0;
while(*s && *smp) {
if (*s == '?')
*s = *smp;
++s; ++smp;
}
return 1;
}
/** ASCII to struct dt **/
int atodt(const char *s, struct tm *dt)
{
int y, m, d;
if (sscanf(s,"%2d.%2d.%4d",&d,&m,&y) != 3)
return 0;
memset(dt, 0, sizeof(struct tm));
dt->tm_mday = d; dt->tm_mon = m-1; dt->tm_year = y-1900;
return 1;
}
/** struct dt to ASCII **/
void dttoa(const struct tm *dt, char *s)
{
if (dt && s)
sprintf(s,"%02d.%02d.%4d",dt->tm_mday,dt->tm_mon+1,dt->tm_year+1900);
}
/** Lese Zeile **/
int parseln(char *line, struct tm *txdt, int *txbefore, int *txafter, char **txmsg)
{
char *p;
/* "01.11.????, 10, 1, Werkstatt Auto" als Beispiel zerlegt */
/* "01.11.????" */
if (!(p=strtok(line, TOKCOLSEP))) /* Komma als 1. Begrenzer durch strtok() mit \0 belegt */
return 0;
if (!expand_datestr(t0a,p))
return 0;
if (!atodt(p,txdt))
return 0;
/* "10" */
if (!(p=strtok(NULL, TOKCOLSEP)))
return 0;
if (!sscanf(p, "%d", txbefore)) /* atoi() gibt leider bei Fehler und bei 0 je 0 zurueck */
return 0;
/* "1" */
if (!(p=strtok(NULL, TOKCOLSEP)))
return 0;
if (!sscanf(p, "%d", txafter))
return 0;
/* "Werkstatt Auto" */
if (!(*txmsg=strtok(NULL, "\n")))
return 0;
while (**txmsg==' ' || **txmsg=='\t')
(*txmsg)++;
return 1;
}
/** Verarbeite Kalenderdatei **/
int process(FILE *f)
{ /* t0... = aktuelle Zeit, tx... = Ereigniszeit */
time_t t0;
struct tm *t0dt, txdt;
char line[160], *bufp=NULL, *p, title[40], txa[11], *txmsg;
int lnno=0, txbefore, txafter, txdays, t0days, buflen=0, bufsize=0;
const int bufdelta = 1024;
/* Berechne aktuelle Zeit t0, ausgedrueckt in time_t, struct dt und String */
t0 = time(NULL); /* Sekunden seit 1970 */
t0dt = localtime(&t0); /* Achtung, dt wird bei jedem Aufruf ueberschrieben */
/* Beschraenke t0 auf ganze Tage, um mit tx vergleichbar zu sein */
t0dt->tm_sec = 0; t0dt->tm_min = 0; t0dt->tm_hour = 0;
t0 = mktime(t0dt);
t0days = SECS2DAYS(t0);
dttoa(t0dt, t0a); /* String fuer Jokerfeature und Titel */
/* Lies Kalenderdatei */
while (fgets(line, sizeof line, f)) {
lnno++;
/* Ueberlies Kommentare und Leerzeilen */
if (line[0]==TOKCMT || line[0]=='\r' || line[0]=='\n')
continue;
/* Lies Zeile, hier auch tx (Ereigniszeit) */
if (!parseln(line, &txdt, &txbefore, &txafter, &txmsg))
return lnerror(lnno);
txdays = SECS2DAYS(mktime(&txdt));
/* Uhrenvergleich (und 2038 gibt's time_t mit 128 bit) */
if (t0days >= txdays-txbefore && t0days <= txdays+txafter) {
dttoa(&txdt, txa);
/* Hol Speicher */
if (buflen + bufdelta > bufsize) {
bufsize += bufdelta;
p = (char*)realloc(bufp, bufsize); /* Erster Aufruf mit NULL arbeitet wie malloc() */
if (p == NULL) {
if (bufp) {
free(bufp);
bufp = NULL;
}
return error("Speicher aus!");
}
bufp = p;
}
buflen += sprintf(bufp+buflen, "%s: %s\n", txa, txmsg);
}
} /* while */
if (bufp) {
sprintf(title, VER_PRODNAME " - Heute ist der %s", t0a);
message(bufp, title, MSGINF);
free(bufp);
}
return 0;
}
/** Main **/
#if defined(__LCC__) || defined(LINUX)
int main(int argc, char **argv)
{
FILE *f;
char s[110];
int ret;
gui = initgui(argc,argv);
if (argc > 1) {
fname = argv[1];
if (*fname == '-')
return help();
}
if ((f=fopen(fname,"rt")) == NULL) {
sprintf(s,"Kann Datei '%.80s' nicht lesen!", fname);
return error(s);
}
ret = process(f);
fclose(f);
return ret;
}
#else
int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpCmd, int nShow)
{
FILE *f;
char s[110];
int ret;
gui = initgui();
if (*lpCmd) {
fname = lpCmd;
if (*fname == '-')
return help();
}
if ((f=fopen(fname,"rt")) == NULL) {
sprintf(s,"Kann Datei '%.80s' nicht lesen!", fname);
return error(s);
}
ret = process(f);
fclose(f);
return ret;
}
#endif