Files
remind/src/calendar.c
2021-10-04 08:39:27 -04:00

2187 lines
56 KiB
C

/***************************************************************/
/* */
/* CALENDAR.C */
/* */
/* The code for generating a calendar. */
/* */
/* This file is part of REMIND. */
/* Copyright (C) 1992-2021 by Dianne Skoll */
/* */
/***************************************************************/
#define _XOPEN_SOURCE
#include "config.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <unistd.h>
#ifdef REM_USE_WCHAR
#include <wctype.h>
#include <wchar.h>
#endif
#include "types.h"
#include "protos.h"
#include "expr.h"
#include "globals.h"
#include "err.h"
#include "md5.h"
/* Data structures used by the calendar */
typedef struct cal_entry {
struct cal_entry *next;
char *text;
char *raw_text;
char const *pos;
#ifdef REM_USE_WCHAR
wchar_t *wc_text;
wchar_t const *wc_pos;
#endif
int is_color;
int r, g, b;
int time;
int priority;
DynamicBuffer tags;
char passthru[PASSTHRU_LEN+1];
int duration;
char *filename;
int lineno;
Trigger trig;
TimeTrig tt;
int nonconst_expr;
int if_depth;
} CalEntry;
/* Line-drawing sequences */
struct line_drawing {
char const *graphics_on;
char const *graphics_off;
char *tlr, *bl, *tbl, *blr, *tblr, *tr, *tb, *br, *tbr, *tl, *lr;
};
static struct line_drawing NormalDrawing = {
"", "", "+", "+", "+", "+", "+", "+", "|", "+", "+", "+", "-"
};
static struct line_drawing VT100Drawing = {
"\x1B(0", "\x1B(B",
"\x76", "\x6b", "\x75", "\x77", "\x6e", "\x6d", "\x78",
"\x6c", "\x74", "\x6a", "\x71"
};
static struct line_drawing UTF8Drawing = {
"", "",
"\xe2\x94\xb4",
"\xe2\x94\x90",
"\xe2\x94\xa4",
"\xe2\x94\xac",
"\xe2\x94\xbc",
"\xe2\x94\x94",
"\xe2\x94\x82",
"\xe2\x94\x8c",
"\xe2\x94\x9c",
"\xe2\x94\x98",
"\xe2\x94\x80"
};
static char *VT100Colors[2][2][2][2] /* [Br][R][G][B] */ = {
{
/*** DIM COLORS ***/
{
{
/* 0, 0, 0 = Black */ "\x1B[0;30m",
/* 0, 0, 1 = Blue */ "\x1B[0;34m"
},
{
/* 0, 1, 0 = Green */ "\x1B[0;32m",
/* 0, 1, 1 = Cyan */ "\x1B[0;36m"
}
},
{
{
/* 1, 0, 0 = Red */ "\x1B[0;31m",
/* 1, 0, 1 = Magenta */ "\x1B[0;35m"
},
{
/* 1, 1, 0 = Yellow */ "\x1B[0;33m",
/* 1, 1, 1 = White */ "\x1B[0;37m"
}
}
},
{
/*** BRIGHT COLORS ***/
{
{
/* 0, 0, 0 = Grey */ "\x1B[30;1m",
/* 0, 0, 1 = Blue */ "\x1B[34;1m"
},
{
/* 0, 1, 0 = Green */ "\x1B[32;1m",
/* 0, 1, 1 = Cyan */ "\x1B[36;1m"
}
},
{
{
/* 1, 0, 0 = Red */ "\x1B[31;1m",
/* 1, 0, 1 = Magenta */ "\x1B[35;1m"
},
{
/* 1, 1, 0 = Yellow */ "\x1B[33;1m",
/* 1, 1, 1 = White */ "\x1B[37;1m"
}
}
}
};
static struct line_drawing *linestruct;
#define DRAW(x) fputs(linestruct->x, stdout)
struct xterm256_colors {
int r;
int g;
int b;
};
/* Xterm support 256 different colors, numbered from 0 to 255. The following
table is a list of the [r, g, b] components of each Xterm color. The
data was obtained from https://jonasjacek.github.io/colors/ */
static struct xterm256_colors XTerm256Colors[] =
{
{ 0, 0, 0}, {128, 0, 0}, { 0, 128, 0}, {128, 128, 0},
{ 0, 0, 128}, {128, 0, 128}, { 0, 128, 128}, {192, 192, 192},
{128, 128, 128}, {255, 0, 0}, { 0, 255, 0}, {255, 255, 0},
{ 0, 0, 255}, {255, 0, 255}, { 0, 255, 255}, {255, 255, 255},
{ 0, 0, 0}, { 0, 0, 95}, { 0, 0, 135}, { 0, 0, 175},
{ 0, 0, 215}, { 0, 0, 255}, { 0, 95, 0}, { 0, 95, 95},
{ 0, 95, 135}, { 0, 95, 175}, { 0, 95, 215}, { 0, 95, 255},
{ 0, 135, 0}, { 0, 135, 95}, { 0, 135, 135}, { 0, 135, 175},
{ 0, 135, 215}, { 0, 135, 255}, { 0, 175, 0}, { 0, 175, 95},
{ 0, 175, 135}, { 0, 175, 175}, { 0, 175, 215}, { 0, 175, 255},
{ 0, 215, 0}, { 0, 215, 95}, { 0, 215, 135}, { 0, 215, 175},
{ 0, 215, 215}, { 0, 215, 255}, { 0, 255, 0}, { 0, 255, 95},
{ 0, 255, 135}, { 0, 255, 175}, { 0, 255, 215}, { 0, 255, 255},
{ 95, 0, 0}, { 95, 0, 95}, { 95, 0, 135}, { 95, 0, 175},
{ 95, 0, 215}, { 95, 0, 255}, { 95, 95, 0}, { 95, 95, 95},
{ 95, 95, 135}, { 95, 95, 175}, { 95, 95, 215}, { 95, 95, 255},
{ 95, 135, 0}, { 95, 135, 95}, { 95, 135, 135}, { 95, 135, 175},
{ 95, 135, 215}, { 95, 135, 255}, { 95, 175, 0}, { 95, 175, 95},
{ 95, 175, 135}, { 95, 175, 175}, { 95, 175, 215}, { 95, 175, 255},
{ 95, 215, 0}, { 95, 215, 95}, { 95, 215, 135}, { 95, 215, 175},
{ 95, 215, 215}, { 95, 215, 255}, { 95, 255, 0}, { 95, 255, 95},
{ 95, 255, 135}, { 95, 255, 175}, { 95, 255, 215}, { 95, 255, 255},
{135, 0, 0}, {135, 0, 95}, {135, 0, 135}, {135, 0, 175},
{135, 0, 215}, {135, 0, 255}, {135, 95, 0}, {135, 95, 95},
{135, 95, 135}, {135, 95, 175}, {135, 95, 215}, {135, 95, 255},
{135, 135, 0}, {135, 135, 95}, {135, 135, 135}, {135, 135, 175},
{135, 135, 215}, {135, 135, 255}, {135, 175, 0}, {135, 175, 95},
{135, 175, 135}, {135, 175, 175}, {135, 175, 215}, {135, 175, 255},
{135, 215, 0}, {135, 215, 95}, {135, 215, 135}, {135, 215, 175},
{135, 215, 215}, {135, 215, 255}, {135, 255, 0}, {135, 255, 95},
{135, 255, 135}, {135, 255, 175}, {135, 255, 215}, {135, 255, 255},
{175, 0, 0}, {175, 0, 95}, {175, 0, 135}, {175, 0, 175},
{175, 0, 215}, {175, 0, 255}, {175, 95, 0}, {175, 95, 95},
{175, 95, 135}, {175, 95, 175}, {175, 95, 215}, {175, 95, 255},
{175, 135, 0}, {175, 135, 95}, {175, 135, 135}, {175, 135, 175},
{175, 135, 215}, {175, 135, 255}, {175, 175, 0}, {175, 175, 95},
{175, 175, 135}, {175, 175, 175}, {175, 175, 215}, {175, 175, 255},
{175, 215, 0}, {175, 215, 95}, {175, 215, 135}, {175, 215, 175},
{175, 215, 215}, {175, 215, 255}, {175, 255, 0}, {175, 255, 95},
{175, 255, 135}, {175, 255, 175}, {175, 255, 215}, {175, 255, 255},
{215, 0, 0}, {215, 0, 95}, {215, 0, 135}, {215, 0, 175},
{215, 0, 215}, {215, 0, 255}, {215, 95, 0}, {215, 95, 95},
{215, 95, 135}, {215, 95, 175}, {215, 95, 215}, {215, 95, 255},
{215, 135, 0}, {215, 135, 95}, {215, 135, 135}, {215, 135, 175},
{215, 135, 215}, {215, 135, 255}, {215, 175, 0}, {215, 175, 95},
{215, 175, 135}, {215, 175, 175}, {215, 175, 215}, {215, 175, 255},
{215, 215, 0}, {215, 215, 95}, {215, 215, 135}, {215, 215, 175},
{215, 215, 215}, {215, 215, 255}, {215, 255, 0}, {215, 255, 95},
{215, 255, 135}, {215, 255, 175}, {215, 255, 215}, {215, 255, 255},
{255, 0, 0}, {255, 0, 95}, {255, 0, 135}, {255, 0, 175},
{255, 0, 215}, {255, 0, 255}, {255, 95, 0}, {255, 95, 95},
{255, 95, 135}, {255, 95, 175}, {255, 95, 215}, {255, 95, 255},
{255, 135, 0}, {255, 135, 95}, {255, 135, 135}, {255, 135, 175},
{255, 135, 215}, {255, 135, 255}, {255, 175, 0}, {255, 175, 95},
{255, 175, 135}, {255, 175, 175}, {255, 175, 215}, {255, 175, 255},
{255, 215, 0}, {255, 215, 95}, {255, 215, 135}, {255, 215, 175},
{255, 215, 215}, {255, 215, 255}, {255, 255, 0}, {255, 255, 95},
{255, 255, 135}, {255, 255, 175}, {255, 255, 215}, {255, 255, 255},
{ 8, 8, 8}, { 18, 18, 18}, { 28, 28, 28}, { 38, 38, 38},
{ 48, 48, 48}, { 58, 58, 58}, { 68, 68, 68}, { 78, 78, 78},
{ 88, 88, 88}, { 98, 98, 98}, {108, 108, 108}, {118, 118, 118},
{128, 128, 128}, {138, 138, 138}, {148, 148, 148}, {158, 158, 158},
{168, 168, 168}, {178, 178, 178}, {188, 188, 188}, {198, 198, 198},
{208, 208, 208}, {218, 218, 218}, {228, 228, 228}, {238, 238, 238}
};
/* Global variables */
static CalEntry *CalColumn[7];
static int ColSpaces;
static int DidAMonth;
static int DidADay;
static void ColorizeEntry(CalEntry const *e);
static void SortCol (CalEntry **col);
static void DoCalendarOneWeek (int nleft);
static void DoCalendarOneMonth (void);
static int WriteCalendarRow (void);
static void WriteWeekHeaderLine (void);
static void WritePostHeaderLine (void);
static void PrintLeft (char const *s, int width, char pad);
static void PrintCentered (char const *s, int width, char *pad);
static int WriteOneCalLine (void);
static int WriteOneColLine (int col);
static void GenerateCalEntries (int col);
static void WriteCalHeader (void);
static void WriteCalTrailer (void);
static int DoCalRem (ParsePtr p, int col);
static void WriteSimpleEntries (int col, int jul);
static void WriteTopCalLine (void);
static void WriteBottomCalLine (void);
static void WriteIntermediateCalLine (void);
static void WriteCalDays (void);
void PrintJSONString(char const *s)
{
while (*s) {
switch(*s) {
case '\b': printf("\\b"); break;
case '\f': printf("\\f"); break;
case '\n': printf("\\n"); break;
case '\r': printf("\\r"); break;
case '\t': printf("\\t"); break;
case '"': printf("\\\""); break;
case '\\': printf("\\\\"); break;
default: printf("%c", *s);
}
s++;
}
}
void PrintJSONKeyPairInt(char const *name, int val)
{
printf("\"");
PrintJSONString(name);
printf("\":%d, ", val);
}
void PrintJSONKeyPairString(char const *name, char const *val)
{
/* If value is blank, skip it! */
if (!val || !*val) {
return;
}
printf("\"");
PrintJSONString(name);
printf("\":\"");
PrintJSONString(val);
printf("\", ");
}
void PrintJSONKeyPairDate(char const *name, int jul)
{
int y, m, d;
if (jul == NO_DATE) {
/* Skip it! */
return;
}
FromJulian(jul, &y, &m, &d);
printf("\"");
PrintJSONString(name);
printf("\":\"%04d-%02d-%02d\", ", y, m+1, d);
}
void PrintJSONKeyPairDateTime(char const *name, int dt)
{
int y, m, d, h, i, k;
if (dt == NO_TIME) {
/* Skip it! */
return;
}
i = dt / MINUTES_PER_DAY;
FromJulian(i, &y, &m, &d);
k = dt % MINUTES_PER_DAY;
h = k / 60;
i = k % 60;
printf("\"");
PrintJSONString(name);
printf("\":\"%04d-%02d-%02dT%02d:%02d\", ", y, m+1, d, h, i);
}
void PrintJSONKeyPairTime(char const *name, int t)
{
int h, i;
if (t == NO_TIME) {
/* Skip it! */
return;
}
h = t / 60;
i = t % 60;
printf("\"");
PrintJSONString(name);
printf("\":\"%02d:%02d\", ", h, i);
}
#ifdef REM_USE_WCHAR
static void PutWideChar(wchar_t const wc)
{
char buf[MB_CUR_MAX+1];
int len;
len = wctomb(buf, wc);
if (len > 0) {
buf[len] = 0;
fputs(buf, stdout);
}
}
#endif
static int make_wchar_versions(CalEntry *e)
{
#ifdef REM_USE_WCHAR
size_t len;
wchar_t *buf;
len = mbstowcs(NULL, e->text, 0);
if (len == (size_t) -1) return 0;
buf = calloc(len+1, sizeof(wchar_t));
if (!buf) return 0;
(void) mbstowcs(buf, e->text, len+1);
buf[len] = 0;
e->wc_text = buf;
e->wc_pos = buf;
return 1;
#else
return 1;
#endif
}
static void gon(void)
{
printf("%s", linestruct->graphics_on);
}
static void goff(void)
{
printf("%s", linestruct->graphics_off);
}
static void
ClampColor(int *r, int *g, int *b)
{
if (TerminalBackground == TERMINAL_BACKGROUND_UNKNOWN) {
/* No special clamping if terminal background is unknown */
return;
}
if (TerminalBackground == TERMINAL_BACKGROUND_DARK) {
if (*r <= 64 && *g <= 64 && *b <= 64) {
int max = *r;
double factor;
if (*g > max) max = *g;
if (*b > max) max = *b;
if (max == 0) {
*r = 65;
*g = 65;
*b = 65;
return;
}
factor = 65.0 / (double) max;
*r = (int) (factor * (double) *r);
*g = (int) (factor * (double) *g);
*b = (int) (factor * (double) *b);
}
return;
}
if (TerminalBackground == TERMINAL_BACKGROUND_LIGHT) {
if (*r > 191 && *g > 191 && *b > 191) {
int min = *r;
if (*g < min) min = *g;
if (*b < min) min = *b;
double factor = 192.0 / (double) min;
*r = (int) (factor * (double) *r);
*g = (int) (factor * (double) *g);
*b = (int) (factor * (double) *b);
}
}
}
char const *
Decolorize(int r, int g, int b)
{
if (!strcmp(Colorize(r, g, b), "")) {
return "";
}
return "\x1B[0m";
}
static char const *
Colorize256(int r, int g, int b)
{
static char buf[40];
int best = -1;
int best_dist = 0;
int dist;
struct xterm256_colors *cur;
size_t i;
ClampColor(&r, &g, &b);
for (i=0; i<(sizeof(XTerm256Colors) / sizeof(XTerm256Colors[0])); i++) {
cur = &XTerm256Colors[i];
dist = ((r - cur->r) * (r - cur->r)) +
((b - cur->b) * (b - cur->b)) +
((g - cur->g) * (g - cur->g));
if (best == -1 || dist < best_dist) {
best_dist = dist;
best = (int) i;
}
}
cur = &XTerm256Colors[best];
sprintf(buf, "\x1B[38;5;%dm", best);
return buf;
}
static char const *
ColorizeTrue(int r, int g, int b)
{
static char buf[40];
ClampColor(&r, &g, &b);
sprintf(buf, "\x1B[38;2;%d;%d;%dm", r, g, b);
return buf;
}
char const *
Colorize(int r, int g, int b)
{
int bright = 0;
if (UseTrueColors) {
return ColorizeTrue(r, g, b);
}
if (Use256Colors) {
return Colorize256(r, g, b);
}
if (r > 128 || g > 128 || b > 128) {
bright = 1;
}
if (r > 64) r = 1;
else r = 0;
if (g > 64) g = 1;
else g = 0;
if (b > 64) b = 1;
else b = 0;
if (TerminalBackground == TERMINAL_BACKGROUND_DARK) {
/* Convert black-on-black to grey */
if (!r && !g && !b) return VT100Colors[1][0][0][0];
}
if (TerminalBackground == TERMINAL_BACKGROUND_LIGHT) {
/* Convert white-on-white to grey */
if (r && g && b) return VT100Colors[1][0][0][0];
}
return VT100Colors[bright][r][g][b];
}
static void ColorizeEntry(CalEntry const *e)
{
printf("%s", Colorize(e->r, e->g, e->b));
}
static int
ComputeCalWidth(int x)
{
struct winsize w;
if (x >= 71) {
/* Has been set with -w option */
return x;
}
if (!isatty(STDOUT_FILENO)) {
/* Output is not a TTY... assume 80 */
return 80;
}
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) < 0) {
return 80;
}
if (w.ws_col < 71) {
return 71;
}
return w.ws_col;
}
/***************************************************************/
/* */
/* ProduceCalendar */
/* */
/* Main loop for generating a calendar. */
/* */
/***************************************************************/
void ProduceCalendar(void)
{
int y, m, d;
if (UseUTF8Chars) {
linestruct = &UTF8Drawing;
} else if (UseVTChars) {
linestruct = &VT100Drawing;
} else {
linestruct = &NormalDrawing;
}
ShouldCache = 1;
CalWidth = ComputeCalWidth(CalWidth);
ColSpaces = (CalWidth - 9) / 7;
CalWidth = 7*ColSpaces + 8;
if (CalMonths) {
FromJulian(JulianToday, &y, &m, &d);
JulianToday = Julian(y, m, 1);
DidAMonth = 0;
if (PsCal == PSCAL_LEVEL3) {
printf("[\n");
}
while (CalMonths--) {
DoCalendarOneMonth();
DidAMonth = 1;
}
if (PsCal == PSCAL_LEVEL3) {
printf("\n]\n");
}
return;
} else {
if (MondayFirst) JulianToday -= (JulianToday%7);
else JulianToday -= ((JulianToday+1)%7);
if (!DoSimpleCalendar) {
WriteWeekHeaderLine();
WriteCalDays();
WriteIntermediateCalLine();
}
while (CalWeeks--)
DoCalendarOneWeek(CalWeeks);
return;
}
}
/***************************************************************/
/* */
/* DoCalendarOneWeek */
/* */
/* Write a calendar for a single week */
/* */
/***************************************************************/
static void DoCalendarOneWeek(int nleft)
{
int y, m, d, done, i, l, wd;
char buf[81];
int LinesWritten = 0;
int OrigJul = JulianToday;
/* Fill in the column entries */
for (i=0; i<7; i++) {
GenerateCalEntries(i);
JulianToday++;
}
/* Output the entries */
/* If it's "Simple Calendar" format, do it simply... */
if (DoSimpleCalendar) {
if (MondayFirst) wd = JulianToday % 7;
else wd = (JulianToday + 1) % 7;
for (i=0; i<7; i++) {
WriteSimpleEntries(i, OrigJul+i-wd);
}
return;
}
/* Here come the first few lines... */
gon();
DRAW(tb);
goff();
for (i=0; i<7; i++) {
FromJulian(OrigJul+i, &y, &m, &d);
sprintf(buf, "%d %c%c%c ", d, MonthName[m][0], MonthName[m][1],
MonthName[m][2]);
if (OrigJul+i == RealToday)
PrintLeft(buf, ColSpaces, '*');
else
PrintLeft(buf, ColSpaces, ' ');
gon();
DRAW(tb);
goff();
}
PutChar('\n');
for (l=0; l<CalPad; l++) {
gon();
DRAW(tb);
goff();
for (i=0; i<7; i++) {
PrintLeft("", ColSpaces, ' ');
gon();
DRAW(tb);
goff();
}
PutChar('\n');
}
/* Write the body lines */
done = 0;
while (!done) {
done = WriteOneCalLine();
LinesWritten++;
}
/* Write any blank lines required */
while (LinesWritten++ < CalLines) {
gon();
DRAW(tb);
goff();
for (i=0; i<7; i++) {
PrintLeft("", ColSpaces, ' ');
gon();
DRAW(tb);
goff();
}
PutChar('\n');
}
/* Write the final line */
if (nleft) {
WriteIntermediateCalLine();
} else {
WriteBottomCalLine();
}
}
/***************************************************************/
/* */
/* DoCalendarOneMonth */
/* */
/* Produce a calendar for the current month. */
/* */
/***************************************************************/
static void DoCalendarOneMonth(void)
{
int y, m, d, mm, yy;
if (!DoSimpleCalendar) WriteCalHeader();
DidADay = 0;
if (PsCal) {
FromJulian(JulianToday, &y, &m, &d);
if (PsCal == PSCAL_LEVEL1) {
printf("%s\n", PSBEGIN);
} else if (PsCal == PSCAL_LEVEL2) {
printf("%s\n", PSBEGIN2);
} else {
if (DidAMonth) {
printf(",\n");
}
printf("{\n");
}
if (PsCal < PSCAL_LEVEL3) {
printf("%s %d %d %d %d\n",
MonthName[m], y, DaysInMonth(m, y), (JulianToday+1) % 7,
MondayFirst);
printf("%s %s %s %s %s %s %s\n",
DayName[6], DayName[0], DayName[1], DayName[2],
DayName[3], DayName[4], DayName[5]);
} else {
PrintJSONKeyPairString("monthname", MonthName[m]);
PrintJSONKeyPairInt("year", y);
PrintJSONKeyPairInt("daysinmonth", DaysInMonth(m, y));
PrintJSONKeyPairInt("firstwkday", (JulianToday+1) % 7);
PrintJSONKeyPairInt("mondayfirst", MondayFirst);
printf("\"daynames\":[\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"],",
DayName[6], DayName[0], DayName[1], DayName[2],
DayName[3], DayName[4], DayName[5]);
}
mm = m-1;
if (mm<0) {
mm = 11; yy = y-1;
} else yy=y;
if (PsCal < PSCAL_LEVEL3) {
printf("%s %d\n", MonthName[mm], DaysInMonth(mm,yy));
} else {
PrintJSONKeyPairString("prevmonthname", MonthName[mm]);
PrintJSONKeyPairInt("daysinprevmonth", DaysInMonth(mm, yy));
PrintJSONKeyPairInt("prevmonthyear", yy);
}
mm = m+1;
if (mm>11) {
mm = 0; yy = y+1;
} else yy=y;
if (PsCal < PSCAL_LEVEL3) {
printf("%s %d\n", MonthName[mm], DaysInMonth(mm,yy));
} else {
PrintJSONKeyPairString("nextmonthname", MonthName[mm]);
PrintJSONKeyPairInt("daysinnextmonth", DaysInMonth(mm, yy));
PrintJSONKeyPairInt("nextmonthyear", yy);
printf("\"entries\":[\n");
}
}
while (WriteCalendarRow()) continue;
if (PsCal == PSCAL_LEVEL1) {
printf("%s\n", PSEND);
} else if (PsCal == PSCAL_LEVEL2) {
printf("%s\n", PSEND2);
} else if (PsCal == PSCAL_LEVEL3){
if (DidADay) {
printf("\n");
}
printf("]\n}");
}
if (!DoSimpleCalendar) WriteCalTrailer();
}
/***************************************************************/
/* */
/* WriteCalendarRow */
/* */
/* Write one row of the calendar */
/* */
/***************************************************************/
static int WriteCalendarRow(void)
{
int y, m, d, wd, i, l;
int done;
char buf[81];
int OrigJul = JulianToday;
int LinesWritten = 0;
int moreleft;
/* Get the date of the first day */
FromJulian(JulianToday, &y, &m, &d);
if (!MondayFirst) wd = (JulianToday + 1) % 7;
else wd = JulianToday % 7;
/* Fill in the column entries */
for (i=wd; i<7; i++) {
if (d+i-wd > DaysInMonth(m, y)) break;
GenerateCalEntries(i);
JulianToday++;
}
/* Output the entries */
/* If it's "Simple Calendar" format, do it simply... */
if (DoSimpleCalendar) {
for (i=wd; i<7 && d+i-wd<=DaysInMonth(m, y); i++) {
WriteSimpleEntries(i, OrigJul+i-wd);
}
return (d+7-wd <= DaysInMonth(m, y));
}
/* Here come the first few lines... */
gon();
DRAW(tb);
goff();
for (i=0; i<7; i++) {
if (i < wd || d+i-wd>DaysInMonth(m, y))
PrintLeft("", ColSpaces, ' ');
else {
sprintf(buf, "%d ", d+i-wd);
if (OrigJul+i == RealToday) {
PrintLeft(buf, ColSpaces-1, '*');
PutChar(' ');
} else {
PrintLeft(buf, ColSpaces, ' ');
}
}
gon();
DRAW(tb);
goff();
}
PutChar('\n');
for (l=0; l<CalPad; l++) {
gon();
DRAW(tb);
goff();
for (i=0; i<7; i++) {
PrintLeft("", ColSpaces, ' ');
gon();
DRAW(tb);
goff();
}
PutChar('\n');
}
/* Write the body lines */
done = 0;
while (!done) {
done = WriteOneCalLine();
LinesWritten++;
}
/* Write any blank lines required */
while (LinesWritten++ < CalLines) {
gon();
DRAW(tb);
goff();
for (i=0; i<7; i++) {
PrintLeft("", ColSpaces, ' ');
gon();
DRAW(tb);
goff();
}
PutChar('\n');
}
moreleft = (d+7-wd <= DaysInMonth(m, y));
if (moreleft) {
WriteIntermediateCalLine();
} else {
WriteBottomCalLine();
}
/* Return non-zero if we have not yet finished */
return moreleft;
}
/***************************************************************/
/* */
/* PrintLeft */
/* */
/* Left-justify a piece of text. */
/* */
/***************************************************************/
static void PrintLeft(char const *s, int width, char pad)
{
int len = strlen(s);
printf("%s", s);
while (len++ < width) PutChar(pad);
}
/***************************************************************/
/* */
/* PrintCentered */
/* */
/* Center a piece of text */
/* */
/***************************************************************/
static void PrintCentered(char const *s, int width, char *pad)
{
#ifndef REM_USE_WCHAR
int len = strlen(s);
int d = (width - len) / 2;
int i;
for (i=0; i<d; i++) fputs(pad, stdout);
for (i=0; i<width; i++) {
if (*s) {
if (isspace(*s)) {
PutChar(' ');
s++;
} else {
PutChar(*s++);
}
} else {
break;
}
}
for (i=d+len; i<width; i++) fputs(pad, stdout);
#else
size_t len = mbstowcs(NULL, s, 0);
int display_len;
int i;
wchar_t static_buf[128];
wchar_t *buf;
wchar_t *ws;
int d;
if (!len) {
for (i=0; i<width; i++) {
fputs(pad, stdout);
}
return;
}
if (len + 1 <= 128) {
buf = static_buf;
} else {
buf = calloc(len+1, sizeof(wchar_t));
if (!buf) {
/* Uh-oh... cannot recover */
fprintf(stderr, "%s\n", ErrMsg[E_NO_MEM]);
exit(1);
}
}
(void) mbstowcs(buf, s, len+1);
display_len = wcswidth(buf, len+1);
d = (width - display_len) / 2;
if (d < 0) d = 0;
ws = buf;
for (i=0; i<d; i++) fputs(pad, stdout);
for (i=0; i<width; i++) {
if (*ws) {
PutWideChar(*ws++);
if (wcwidth(*ws) == 0) {
/* Don't count this character... it's zero-width */
i--;
}
} else {
break;
}
}
/* Mop up any potential combining characters */
while (*ws && wcwidth(*ws) == 0) {
PutWideChar(*ws++);
}
for (i=d+len; i<width; i++) fputs(pad, stdout);
if (buf != static_buf) free(buf);
#endif
}
/***************************************************************/
/* */
/* WriteOneCalLine */
/* */
/* Write a single line. */
/* */
/***************************************************************/
static int WriteOneCalLine(void)
{
int done = 1, i;
gon();
DRAW(tb);
goff();
for (i=0; i<7; i++) {
if (CalColumn[i]) {
if (WriteOneColLine(i)) done = 0;
} else {
PrintCentered("", ColSpaces, " ");
}
gon();
DRAW(tb);
goff();
}
PutChar('\n');
return done;
}
/***************************************************************/
/* */
/* WriteOneColLine */
/* */
/* Write a single line for a specified column. Return 1 if */
/* the column still has entries; 0 otherwise. */
/* */
/***************************************************************/
static int WriteOneColLine(int col)
{
CalEntry *e = CalColumn[col];
char const *s;
char const *space;
#ifdef REM_USE_WCHAR
wchar_t const *ws;
wchar_t const *wspace;
int width;
#endif
int numwritten = 0;
/* Print as many characters as possible within the column */
#ifdef REM_USE_WCHAR
if (e->wc_text) {
wspace = NULL;
ws = e->wc_pos;
/* If we're at the end, and there's another entry, do a blank
line and move to next entry. */
if (!*ws && e->next) {
PrintLeft("", ColSpaces, ' ');
CalColumn[col] = e->next;
free(e->text);
free(e->raw_text);
free(e->filename);
if (e->wc_text) free(e->wc_text);
free(e);
return 1;
}
/* Find the last space char within the column. */
width = 0;
while (width <= ColSpaces) {
if (!*ws) {
wspace = ws;
break;
}
if (iswspace(*ws)) {
wspace = ws;
}
if (wcwidth(*ws)) {
width++;
}
ws++;
}
/* Colorize reminder if necessary */
if (UseVTColors && e->is_color) {
ColorizeEntry(e);
}
/* If we couldn't find a space char, print what we have. */
if (!wspace) {
for (ws = e->wc_pos; ws - e->wc_pos < ColSpaces; ws++) {
if (!*ws) break;
if (iswspace(*ws)) {
PutChar(' ');
numwritten++;
} else {
if (wcwidth(*ws) > 0) {
numwritten += wcwidth(*ws);
}
PutWideChar(*ws);
}
}
e->wc_pos = ws;
} else {
/* We found a space - print everything before it. */
for (ws = e->wc_pos; ws<wspace; ws++) {
if (!*ws) break;
if (iswspace(*ws)) {
PutChar(' ');
numwritten++;
} else {
if (wcwidth(*ws) > 0) {
numwritten += wcwidth(*ws);
}
PutWideChar(*ws);
}
}
}
/* Decolorize reminder if necessary */
if (UseVTColors && e->is_color) {
printf("%s", Decolorize(e->r, e->g, e->b));
}
/* Flesh out the rest of the column */
while(numwritten++ < ColSpaces) PutChar(' ');
/* Skip any spaces before next word */
while (iswspace(*ws)) ws++;
/* If done, free memory if no next entry. */
if (!*ws && !e->next) {
CalColumn[col] = e->next;
free(e->text);
free(e->raw_text);
free(e->filename);
if (e->wc_text) free(e->wc_text);
free(e);
} else {
e->wc_pos = ws;
}
if (CalColumn[col]) return 1; else return 0;
} else {
#endif
space = NULL;
s = e->pos;
/* If we're at the end, and there's another entry, do a blank
line and move to next entry. */
if (!*s && e->next) {
PrintLeft("", ColSpaces, ' ');
CalColumn[col] = e->next;
free(e->text);
free(e->filename);
#ifdef REM_USE_WCHAR
if (e->wc_text) free(e->wc_text);
#endif
free(e->raw_text);
free(e);
return 1;
}
/* Find the last space char within the column. */
while (s - e->pos <= ColSpaces) {
if (!*s) {space = s; break;}
if (isspace(*s)) space = s;
s++;
}
/* Colorize reminder if necessary */
if (UseVTColors && e->is_color) {
ColorizeEntry(e);
}
/* If we couldn't find a space char, print what we have. */
if (!space) {
for (s = e->pos; s - e->pos < ColSpaces; s++) {
if (!*s) break;
numwritten++;
if (isspace(*s)) {
PutChar(' ');
} else {
PutChar(*s);
}
}
e->pos = s;
} else {
/* We found a space - print everything before it. */
for (s = e->pos; s<space; s++) {
if (!*s) break;
numwritten++;
if (isspace(*s)) {
PutChar(' ');
} else {
PutChar(*s);
}
}
}
/* Decolorize reminder if necessary */
if (UseVTColors && e->is_color) {
printf("%s", Decolorize(e->r, e->g, e->b));
}
/* Flesh out the rest of the column */
while(numwritten++ < ColSpaces) PutChar(' ');
/* Skip any spaces before next word */
while (isspace(*s)) s++;
/* If done, free memory if no next entry. */
if (!*s && !e->next) {
CalColumn[col] = e->next;
free(e->text);
free(e->filename);
#ifdef REM_USE_WCHAR
if (e->wc_text) free(e->wc_text);
#endif
free(e->raw_text);
free(e);
} else {
e->pos = s;
}
if (CalColumn[col]) return 1; else return 0;
#ifdef REM_USE_WCHAR
}
#endif
}
/***************************************************************/
/* */
/* GenerateCalEntries */
/* */
/* Generate the calendar entries for the ith column */
/* */
/***************************************************************/
static void GenerateCalEntries(int col)
{
int r;
Token tok;
char const *s;
Parser p;
/* Do some initialization first... */
PerIterationInit();
r=IncludeFile(InitialFile);
if (r) {
fprintf(ErrFp, "%s %s: %s\n", ErrMsg[E_ERR_READING], InitialFile, ErrMsg[r]);
exit(1);
}
while(1) {
r = ReadLine();
if (r == E_EOF) return;
if (r) {
Eprint("%s: %s", ErrMsg[E_ERR_READING], ErrMsg[r]);
exit(1);
}
s = FindInitialToken(&tok, CurLine);
/* Should we ignore it? */
if (NumIfs &&
tok.type != T_If &&
tok.type != T_Else &&
tok.type != T_EndIf &&
tok.type != T_IfTrig &&
ShouldIgnoreLine())
{
/* DO NOTHING */
}
else {
/* Create a parser to parse the line */
CreateParser(s, &p);
switch(tok.type) {
case T_Empty:
case T_Comment:
break;
case T_ErrMsg: r=DoErrMsg(&p); break;
case T_Rem: r=DoCalRem(&p, col); break;
case T_If: r=DoIf(&p); break;
case T_IfTrig: r=DoIfTrig(&p); break;
case T_Else: r=DoElse(&p); break;
case T_EndIf: r=DoEndif(&p); break;
case T_Include: r=DoInclude(&p); break;
case T_IncludeCmd: r=DoIncludeCmd(&p); break;
case T_Exit: DoExit(&p); break;
case T_Set: r=DoSet(&p); break;
case T_Fset: r=DoFset(&p); break;
case T_UnSet: r=DoUnset(&p); break;
case T_Clr: r=DoClear(&p); break;
case T_Flush: r=DoFlush(&p); break;
case T_Debug: break; /* IGNORE DEBUG CMD */
case T_Dumpvars: break; /* IGNORE DUMPVARS CMD */
case T_Banner: break; /* IGNORE BANNER CMD */
case T_Omit: r=DoOmit(&p);
if (r == E_PARSE_AS_REM) {
DestroyParser(&p);
CreateParser(s, &p);
r=DoCalRem(&p, col);
}
break;
case T_Pop: r=PopOmitContext(&p); break;
case T_Push: r=PushOmitContext(&p); break;
case T_Preserve: r=DoPreserve(&p); break;
case T_RemType: if (tok.val == RUN_TYPE) {
r=DoRun(&p);
break;
} else {
CreateParser(CurLine, &p);
r=DoCalRem(&p, col);
break;
}
/* If we don't recognize the command, do a REM by default */
/* Note: Since the parser hasn't been used yet, we don't */
/* need to destroy it here. */
default: CreateParser(CurLine, &p);
r=DoCalRem(&p, col);
break;
}
if (r && (!Hush || r != E_RUN_DISABLED)) Eprint("%s", ErrMsg[r]);
/* Destroy the parser - free up resources it may be tying up */
DestroyParser(&p);
}
}
}
/***************************************************************/
/* */
/* WriteCalHeader */
/* */
/***************************************************************/
static void WriteCalHeader(void)
{
char buf[80];
int y, m, d;
FromJulian(JulianToday, &y, &m, &d);
sprintf(buf, "%s %d", MonthName[m], y);
WriteTopCalLine();
gon();
DRAW(tb);
goff();
PrintCentered(buf, CalWidth-2, " ");
gon();
DRAW(tb);
goff();
PutChar('\n');
WritePostHeaderLine();
WriteCalDays();
WriteIntermediateCalLine();
}
/***************************************************************/
/* */
/* WriteCalTrailer */
/* */
/***************************************************************/
static void WriteCalTrailer(void)
{
PutChar('\f');
}
/***************************************************************/
/* */
/* DoCalRem */
/* */
/* Do the REM command in the context of a calendar. */
/* */
/***************************************************************/
static int DoCalRem(ParsePtr p, int col)
{
int oldLen;
Trigger trig;
TimeTrig tim;
Value v;
int r, err;
int jul;
CalEntry *CurCol = CalColumn[col];
CalEntry *e;
char const *s, *s2;
DynamicBuffer buf, obuf, pre_buf, raw_buf;
Token tok;
int nonconst_expr = 0;
int is_color, col_r, col_g, col_b;
is_color = 0;
DBufInit(&buf);
DBufInit(&pre_buf);
DBufInit(&raw_buf);
/* Parse the trigger date and time */
if ( (r=ParseRem(p, &trig, &tim, 1)) ) {
FreeTrig(&trig);
return r;
}
if (trig.typ == MSG_TYPE ||
trig.typ == CAL_TYPE ||
trig.typ == MSF_TYPE) {
is_color = (
DefaultColorR != -1
&& DefaultColorG != -1
&& DefaultColorB != -1);
if (is_color) {
col_r = DefaultColorR;
col_g = DefaultColorG;
col_b = DefaultColorB;
}
}
/* Don't include timed reminders in calendar if -a option supplied. */
if (DontIssueAts && tim.ttime != NO_TIME) {
FreeTrig(&trig);
return OK;
}
if (trig.typ == NO_TYPE) {
FreeTrig(&trig);
return E_EOLN;
}
if (trig.typ == SAT_TYPE) {
r=DoSatRemind(&trig, &tim, p);
if (r) {
if (r == E_CANT_TRIG && trig.maybe_uncomputable) {
r = OK;
}
FreeTrig(&trig);
if (r == E_EXPIRED) return OK;
return r;
}
if (!LastTrigValid) {
FreeTrig(&trig);
return OK;
}
r=ParseToken(p, &buf);
if (r) {
FreeTrig(&trig);
return r;
}
FindToken(DBufValue(&buf), &tok);
DBufFree(&buf);
if (tok.type == T_Empty || tok.type == T_Comment) {
FreeTrig(&trig);
return OK;
}
if (tok.type != T_RemType || tok.val == SAT_TYPE) {
FreeTrig(&trig);
return E_PARSE_ERR;
}
if (tok.val == PASSTHRU_TYPE) {
r=ParseToken(p, &buf);
if (r) return r;
if (!DBufLen(&buf)) {
DBufFree(&buf);
FreeTrig(&trig);
return E_EOLN;
}
StrnCpy(trig.passthru, DBufValue(&buf), PASSTHRU_LEN);
DBufFree(&buf);
}
trig.typ = tok.val;
jul = LastTriggerDate;
if (!LastTrigValid) {
FreeTrig(&trig);
return OK;
}
} else {
/* Calculate the trigger date */
jul = ComputeTrigger(trig.scanfrom, &trig, &tim, &r, 1);
if (r) {
if (r == E_CANT_TRIG && trig.maybe_uncomputable) {
r = OK;
}
FreeTrig(&trig);
return r;
}
}
/* Save nonconst_expr flag */
nonconst_expr = p->nonconst_expr;
/* Convert PS and PSF to PASSTHRU */
if (trig.typ == PS_TYPE) {
strcpy(trig.passthru, "PostScript");
trig.typ = PASSTHRU_TYPE;
} else if (trig.typ == PSF_TYPE) {
strcpy(trig.passthru, "PSFile");
trig.typ = PASSTHRU_TYPE;
}
/* If it's a plain reminder but we have a default color, add the
three colors to the prebuf and change passthru to "COLOR" */
if (trig.typ == MSG_TYPE ||
trig.typ == CAL_TYPE ||
trig.typ == MSF_TYPE) {
if (PsCal && is_color) {
char cbuf[24];
sprintf(cbuf, "%d %d %d ", col_r, col_g, col_b);
DBufPuts(&pre_buf, cbuf);
strcpy(trig.passthru, "COLOR");
/* Don't change trig.typ or next if() will trigger! */
}
}
if (trig.typ == PASSTHRU_TYPE) {
if (!PsCal && StrCmpi(trig.passthru, "COLOR") && StrCmpi(trig.passthru, "COLOUR")) {
FreeTrig(&trig);
return OK;
}
if (!StrCmpi(trig.passthru, "COLOR") ||
!StrCmpi(trig.passthru, "COLOUR")) {
is_color = 1;
/* Strip off the three color numbers */
DBufFree(&buf);
r=ParseToken(p, &buf);
DBufPuts(&pre_buf, DBufValue(&buf));
DBufPutc(&pre_buf, ' ');
DBufFree(&buf);
if (r) {
FreeTrig(&trig);
return r;
}
r=ParseToken(p, &buf);
DBufPuts(&pre_buf, DBufValue(&buf));
DBufPutc(&pre_buf, ' ');
DBufFree(&buf);
if (r) {
FreeTrig(&trig);
return r;
}
r=ParseToken(p, &buf);
DBufPuts(&pre_buf, DBufValue(&buf));
DBufPutc(&pre_buf, ' ');
DBufFree(&buf);
if (r) {
FreeTrig(&trig);
return r;
}
(void) sscanf(DBufValue(&pre_buf), "%d %d %d",
&col_r, &col_g, &col_b);
if (col_r < 0) col_r = 0;
else if (col_r > 255) col_r = 255;
if (col_g < 0) col_g = 0;
else if (col_g > 255) col_g = 255;
if (col_b < 0) col_b = 0;
else if (col_b > 255) col_b = 255;
if (!PsCal && !DoSimpleCalendar) {
DBufFree(&pre_buf);
}
}
}
/* If trigger date == today, add it to the current entry */
DBufInit(&obuf);
if ((jul == JulianToday) ||
(DoSimpleCalDelta &&
ShouldTriggerReminder(&trig, &tim, jul, &err))) {
NumTriggered++;
/* The parse_ptr should not be nested, but just in case... */
if (!p->isnested) {
if (DBufPuts(&raw_buf, p->pos) != OK) {
DBufFree(&obuf);
DBufFree(&pre_buf);
FreeTrig(&trig);
return E_NO_MEM;
}
}
if (DoSimpleCalendar || tim.ttime != NO_TIME) {
/* Suppress time if it's not today or if it's a non-COLOR special */
if (jul != JulianToday ||
(trig.typ == PASSTHRU_TYPE &&
StrCmpi(trig.passthru, "COLOUR") &&
StrCmpi(trig.passthru, "COLOR"))) {
if (DBufPuts(&obuf, SimpleTime(NO_TIME)) != OK) {
DBufFree(&obuf);
DBufFree(&raw_buf);
DBufFree(&pre_buf);
FreeTrig(&trig);
return E_NO_MEM;
}
} else {
if (DBufPuts(&obuf, CalendarTime(tim.ttime, tim.duration)) != OK) {
DBufFree(&raw_buf);
DBufFree(&obuf);
DBufFree(&pre_buf);
FreeTrig(&trig);
return E_NO_MEM;
}
}
}
if (trig.typ != PASSTHRU_TYPE &&
UserFuncExists("calprefix")==1) {
char evalBuf[64];
sprintf(evalBuf, "calprefix(%d)", trig.priority);
s2 = evalBuf;
r = EvalExpr(&s2, &v, NULL);
if (!r) {
if (!DoCoerce(STR_TYPE, &v)) {
if (DBufPuts(&obuf, v.v.str) != OK) {
DestroyValue(v);
DBufFree(&raw_buf);
DBufFree(&obuf);
DBufFree(&pre_buf);
FreeTrig(&trig);
return E_NO_MEM;
}
}
DestroyValue(v);
}
}
oldLen = DBufLen(&obuf);
/* In -sa mode, run in ADVANCE mode if we're triggering
* before the actual date */
if (jul != JulianToday) {
r = DoSubst(p, &obuf, &trig, &tim, jul, ADVANCE_MODE);
} else {
r = DoSubst(p, &obuf, &trig, &tim, jul, CAL_MODE);
}
if (r) {
DBufFree(&pre_buf);
DBufFree(&obuf);
DBufFree(&raw_buf);
FreeTrig(&trig);
return r;
}
if (DBufLen(&obuf) <= oldLen) {
DBufFree(&obuf);
DBufFree(&pre_buf);
DBufFree(&raw_buf);
FreeTrig(&trig);
return OK;
}
if (trig.typ != PASSTHRU_TYPE &&
UserFuncExists("calsuffix")==1) {
char evalBuf[64];
sprintf(evalBuf, "calsuffix(%d)", trig.priority);
s2 = evalBuf;
r = EvalExpr(&s2, &v, NULL);
if (!r) {
if (!DoCoerce(STR_TYPE, &v)) {
if (DBufPuts(&obuf, v.v.str) != OK) {
DestroyValue(v);
DBufFree(&raw_buf);
DBufFree(&obuf);
DBufFree(&pre_buf);
FreeTrig(&trig);
return E_NO_MEM;
}
}
DestroyValue(v);
}
}
s = DBufValue(&obuf);
if (!DoSimpleCalendar) while (isempty(*s)) s++;
DBufPuts(&pre_buf, s);
s = DBufValue(&pre_buf);
e = NEW(CalEntry);
if (!e) {
DBufFree(&obuf);
DBufFree(&raw_buf);
DBufFree(&pre_buf);
FreeTrig(&trig);
return E_NO_MEM;
}
e->nonconst_expr = nonconst_expr;
e->if_depth = NumIfs;
e->trig = trig;
e->tt = tim;
#ifdef REM_USE_WCHAR
e->wc_pos = NULL;
e->wc_text = NULL;
#endif
e->is_color = is_color;
e->r = col_r;
e->g = col_g;
e->b = col_b;
e->text = StrDup(s);
e->raw_text = StrDup(DBufValue(&raw_buf));
DBufFree(&raw_buf);
DBufFree(&obuf);
DBufFree(&pre_buf);
if (!e->text || !e->raw_text) {
if (e->text) free(e->text);
if (e->raw_text) free(e->raw_text);
free(e);
FreeTrig(&trig);
return E_NO_MEM;
}
make_wchar_versions(e);
DBufInit(&(e->tags));
DBufPuts(&(e->tags), DBufValue(&(trig.tags)));
if (SynthesizeTags) {
AppendTag(&(e->tags), SynthesizeTag());
}
/* Don't need tags any more */
FreeTrig(&trig);
e->duration = tim.duration;
e->priority = trig.priority;
e->filename = StrDup(FileName);
if(!e->filename) {
if (e->text) free(e->text);
if (e->raw_text) free(e->raw_text);
#ifdef REM_USE_WCHAR
if (e->wc_text) free(e->wc_text);
#endif
free(e);
return E_NO_MEM;
}
e->lineno = LineNo;
if (trig.typ == PASSTHRU_TYPE || is_color) {
StrnCpy(e->passthru, trig.passthru, PASSTHRU_LEN);
} else {
e->passthru[0] = 0;
}
e->pos = e->text;
if (jul == JulianToday) {
e->time = tim.ttime;
} else {
e->time = NO_TIME;
}
e->next = CurCol;
CalColumn[col] = e;
SortCol(&CalColumn[col]);
}
return OK;
}
static void WriteSimpleEntryProtocol1(CalEntry *e)
{
if (e->passthru[0]) {
printf(" %s", e->passthru);
} else {
printf(" *");
}
if (*DBufValue(&(e->tags))) {
printf(" %s ", DBufValue(&(e->tags)));
} else {
printf(" * ");
}
if (e->duration != NO_TIME) {
printf("%d ", e->duration);
} else {
printf("* ");
}
if (e->time != NO_TIME) {
printf("%d ", e->time);
} else {
printf("* ");
}
printf("%s\n", e->text);
}
static void WriteSimpleEntryProtocol2(CalEntry *e, int today)
{
int done = 0;
if (DoPrefixLineNo) {
PrintJSONKeyPairString("filename", e->filename);
PrintJSONKeyPairInt("lineno", e->lineno);
}
PrintJSONKeyPairString("passthru", e->passthru);
PrintJSONKeyPairString("tags", DBufValue(&(e->tags)));
if (e->duration != NO_TIME) {
PrintJSONKeyPairInt("duration", e->duration);
}
if (e->time != NO_TIME) {
PrintJSONKeyPairInt("time", e->time);
if (e->tt.delta) {
PrintJSONKeyPairInt("tdelta", e->tt.delta);
}
if (e->tt.rep) {
PrintJSONKeyPairInt("trep", e->tt.rep);
}
}
if (e->trig.eventduration != NO_TIME) {
PrintJSONKeyPairInt("eventduration", e->trig.eventduration);
}
/* wd is an array of days from 0=monday to 6=sunday.
We convert to array of strings */
if (e->trig.wd != NO_WD) {
printf("\"wd\":[");
done = 0;
int i;
for (i=0; i<7; i++) {
if (e->trig.wd & (1 << i)) {
if (done) {
printf(",");
}
done = 1;
printf("\"%s\"", EnglishDayName[i]);
}
}
printf("], ");
}
if (e->trig.d != NO_DAY) {
PrintJSONKeyPairInt("d", e->trig.d);
}
if (e->trig.m != NO_MON) {
PrintJSONKeyPairInt("m", e->trig.m+1);
}
if (e->trig.y != NO_YR) {
PrintJSONKeyPairInt("y", e->trig.y);
}
PrintJSONKeyPairDateTime("eventstart", e->trig.eventstart);
if (e->trig.back) {
PrintJSONKeyPairInt("back", e->trig.back);
}
if (e->trig.delta) {
PrintJSONKeyPairInt("delta", e->trig.delta);
}
if (e->trig.rep) {
PrintJSONKeyPairInt("rep", e->trig.rep);
}
if (e->nonconst_expr) {
PrintJSONKeyPairInt("nonconst_expr", e->nonconst_expr);
}
if (e->if_depth) {
PrintJSONKeyPairInt("if_depth", e->if_depth);
}
switch(e->trig.skip) {
case SKIP_SKIP:
PrintJSONKeyPairString("skip", "SKIP");
break;
case BEFORE_SKIP:
PrintJSONKeyPairString("skip", "BEFORE");
break;
case AFTER_SKIP:
PrintJSONKeyPairString("skip", "AFTER");
break;
}
/* Local omit is an array of days from 0=monday to 6=sunday.
We convert to array of strings */
if (e->trig.localomit != NO_WD) {
printf("\"localomit\":[");
done = 0;
int i;
for (i=0; i<7; i++) {
if (e->trig.localomit & (1 << i)) {
if (done) {
printf(",");
}
done = 1;
printf("\"%s\"", EnglishDayName[i]);
}
}
printf("], ");
}
PrintJSONKeyPairDate("until", e->trig.until);
if (e->trig.once != NO_ONCE) {
PrintJSONKeyPairInt("once", e->trig.once);
}
if (e->trig.scanfrom != today) {
PrintJSONKeyPairDate("scanfrom", e->trig.scanfrom);
}
PrintJSONKeyPairDate("from", e->trig.from);
PrintJSONKeyPairInt("priority", e->trig.priority);
if (e->is_color) {
PrintJSONKeyPairInt("r", e->r);
PrintJSONKeyPairInt("g", e->g);
PrintJSONKeyPairInt("b", e->b);
} else if (!StrCmpi(e->passthru, "SHADE")) {
int r, g, b, n;
n = sscanf(e->text, "%d %d %d", &r, &g, &b);
if (n < 3) {
g = r;
b = r;
}
if (r < 0) r = 0;
else if (r > 255) r = 255;
if (g < 0) g = 0;
else if (g > 255) g = 255;
if (b < 0) b = 0;
else if (b > 255) b = 255;
PrintJSONKeyPairInt("r", r);
PrintJSONKeyPairInt("g", g);
PrintJSONKeyPairInt("b", b);
}
/* Only print rawbody if it differs from body */
if (strcmp(e->raw_text, e->text)) {
PrintJSONKeyPairString("rawbody", e->raw_text);
}
printf("\"body\":\"");
PrintJSONString(e->text);
printf("\"");
}
/***************************************************************/
/* */
/* WriteSimpleEntries */
/* */
/* Write entries in 'simple calendar' format. */
/* */
/***************************************************************/
static void WriteSimpleEntries(int col, int jul)
{
CalEntry *e = CalColumn[col];
CalEntry *n;
int y, m, d;
FromJulian(jul, &y, &m, &d);
while(e) {
if (DoPrefixLineNo) {
if (PsCal != PSCAL_LEVEL2 && PsCal != PSCAL_LEVEL3) {
printf("# fileinfo %d %s\n", e->lineno, e->filename);
}
}
if (PsCal >= PSCAL_LEVEL2) {
if (PsCal == PSCAL_LEVEL3) {
if (DidADay) {
printf(",\n");
}
}
DidADay = 1;
printf("{\"date\":\"%04d-%02d-%02d\", ", y, m+1, d);
WriteSimpleEntryProtocol2(e, jul);
printf("}");
if (PsCal != PSCAL_LEVEL3) {
printf("\n");
}
} else {
printf("%04d/%02d/%02d", y, m+1, d);
WriteSimpleEntryProtocol1(e);
}
free(e->text);
free(e->raw_text);
free(e->filename);
#ifdef REM_USE_WCHAR
if (e->wc_text) free(e->wc_text);
#endif
n = e->next;
free(e);
e = n;
}
CalColumn[col] = NULL;
}
/***************************************************************/
/* */
/* Various functions for writing different types of lines. */
/* */
/***************************************************************/
static void WriteTopCalLine(void)
{
gon();
DRAW(br);
PrintCentered("", CalWidth-2, linestruct->lr);
DRAW(bl);
goff();
PutChar('\n');
}
static void WriteBottomCalLine(void)
{
int i;
gon();
DRAW(tr);
for (i=0; i<7; i++) {
PrintCentered("", ColSpaces, linestruct->lr);
if (i != 6) {
DRAW(tlr);
} else {
DRAW(tl);
}
}
goff();
PutChar('\n');
}
static void WritePostHeaderLine(void)
{
int i;
gon();
DRAW(tbr);
for (i=0; i<7; i++) {
PrintCentered("", ColSpaces, linestruct->lr);
if (i != 6) {
DRAW(blr);
} else {
DRAW(tbl);
}
}
goff();
PutChar('\n');
}
static void WriteWeekHeaderLine(void)
{
int i;
gon();
DRAW(br);
for (i=0; i<7; i++) {
PrintCentered("", ColSpaces, linestruct->lr);
if (i != 6) {
DRAW(blr);
} else {
DRAW(bl);
}
}
goff();
PutChar('\n');
}
static void WriteIntermediateCalLine(void)
{
int i;
gon();
DRAW(tbr);
for (i=0; i<7; i++) {
PrintCentered("", ColSpaces, linestruct->lr);
if (i != 6) {
DRAW(tblr);
} else {
DRAW(tbl);
}
}
goff();
PutChar('\n');
}
static void WriteCalDays(void)
{
int i;
gon();
DRAW(tb);
goff();
for (i=0; i<7; i++) {
if (!MondayFirst)
PrintCentered(DayName[(i+6)%7], ColSpaces, " ");
else
PrintCentered(DayName[i%7], ColSpaces, " ");
gon();
DRAW(tb);
goff();
}
PutChar('\n');
}
/***************************************************************/
/* */
/* CalendarTime */
/* */
/* Format the time according to simple time format. */
/* Answer is returned in a static buffer. */
/* A trailing space is always added. */
/* This takes into account duration */
/* */
/***************************************************************/
char const *
CalendarTime(int tim, int duration)
{
static char buf[128];
int h, min, hh;
int h2, min2, hh2, newtim, days;
char const *ampm1;
char const *ampm2;
char daybuf[64];
buf[0] = 0;
if (duration == NO_TIME) {
/* No duration... just call into SimpleTime */
return SimpleTime(tim);
}
if (tim == NO_TIME) {
/* No time... nothing to return */
return buf;
}
h = tim/60;
min = tim % 60;
if (h == 0) hh=12;
else if (h > 12) hh=h-12;
else hh = h;
newtim = tim + duration;
/* How many days in duration? */
days = newtim / MINUTES_PER_DAY;
newtim = newtim % MINUTES_PER_DAY;
h2 = newtim/60;
min2 = newtim % 60;
if (h2 == 0) hh2=12;
else if (h2 > 12) hh2=h2-12;
else hh2 = h2;
if (days) {
sprintf(daybuf, "+%d", days);
} else {
daybuf[0] = 0;
}
if (h >= 12) {
ampm1 = L_PM;
} else {
ampm1 = L_AM;
}
if (h2 >= 12) {
ampm2 = L_PM;
} else {
ampm2 = L_AM;
}
if (!days) {
if (!strcmp(ampm1, ampm2)) {
ampm1 = "";
}
}
switch(ScFormat) {
case SC_AMPM:
sprintf(buf, "%d%c%02d%s-%d%c%02d%s%s ",
hh, TimeSep, min, ampm1, hh2, TimeSep, min2, ampm2, daybuf);
break;
case SC_MIL:
sprintf(buf, "%02d%c%02d-%02d%c%02d%s ",
h, TimeSep, min, h2, TimeSep, min2, daybuf);
break;
}
return buf;
}
/***************************************************************/
/* */
/* SimpleTime */
/* */
/* Format the time according to simple time format. */
/* Answer is returned in a static buffer. */
/* A trailing space is always added. */
/* */
/***************************************************************/
char const *SimpleTime(int tim)
{
static char buf[32];
int h, min, hh;
buf[0] = 0;
switch(ScFormat) {
case SC_AMPM:
if (tim != NO_TIME) {
h = tim / 60;
min = tim % 60;
if (h == 0) hh=12;
else if (h > 12) hh=h-12;
else hh=h;
sprintf(buf, "%d%c%02d%s ", hh, TimeSep, min, (h>=12) ? L_PM : L_AM);
}
break;
case SC_MIL:
if (tim != NO_TIME) {
h = tim / 60;
min = tim % 60;
sprintf(buf, "%02d%c%02d ", h, TimeSep, min);
}
break;
}
return buf;
}
/***************************************************************/
/* */
/* SortCol */
/* */
/* Sort the calendar entries in a column by time and priority */
/* */
/***************************************************************/
static void SortCol(CalEntry **col)
{
CalEntry *cur, *prev, *next;
cur = *col;
prev = NULL;
/* Note that we use <= comparison rather than > comparison to preserve the
file order of reminders which have the same time and priority */
while (cur->next &&
CompareRems(0, cur->time, cur->priority,
0, cur->next->time, cur->next->priority,
SortByDate, SortByTime, SortByPrio, UntimedBeforeTimed) <= 0) {
next = cur->next;
/* Swap cur and next */
if (!prev) {
*col = next;
cur->next = next->next;
next->next = cur;
prev = next;
} else {
prev->next = next;
cur->next = next->next;
next->next = cur;
prev = next;
}
}
}
char const *SynthesizeTag(void)
{
struct MD5Context ctx;
unsigned char buf[16];
static char out[128];
MD5Init(&ctx);
MD5Update(&ctx, (unsigned char *) CurLine, strlen(CurLine));
MD5Final(buf, &ctx);
sprintf(out, "__syn__%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
(unsigned int) buf[0], (unsigned int) buf[1],
(unsigned int) buf[2], (unsigned int) buf[3],
(unsigned int) buf[4], (unsigned int) buf[5],
(unsigned int) buf[6], (unsigned int) buf[7],
(unsigned int) buf[8], (unsigned int) buf[9],
(unsigned int) buf[10], (unsigned int) buf[11],
(unsigned int) buf[12], (unsigned int) buf[13],
(unsigned int) buf[14], (unsigned int) buf[15]);
return out;
}