-/*
- * mhical.c -- operate on an iCalendar request
+/* mhical.c -- operate on an iCalendar request
*
* This code is Copyright (c) 2014, by the authors of nmh.
* See the COPYRIGHT file in the root directory of the nmh
*/
#include "h/mh.h"
+#include "sbr/ambigsw.h"
+#include "sbr/path.h"
+#include "sbr/print_version.h"
+#include "sbr/print_help.h"
+#include "sbr/error.h"
#include "h/icalendar.h"
#include "sbr/icalparse.h"
-#include <h/fmt_scan.h>
+#include "h/fmt_scan.h"
#include "h/addrsbr.h"
#include "h/mts.h"
+#include "h/done.h"
#include "h/utils.h"
#include <time.h>
static void dump_unfolded (FILE *, contentline *);
static void output (FILE *, contentline *, int);
static void display (FILE *, contentline *, char *);
-static const char *identity (const contentline *);
+static const char *identity (const contentline *) PURE;
static char *format_params (char *, param_list *);
static char *fold (char *, int);
#undef X
vevent vevents = { NULL, NULL, NULL};
+int parser_status = 0;
int
-main (int argc, char *argv[]) {
+main (int argc, char *argv[])
+{
/* RFC 5322 § 3.3 date-time format, including the optional
day-of-week and not including the optional seconds. The
zone is required by the RFC but not always output by this
act action = ACT_NONE;
char *infile = NULL, *outfile = NULL;
FILE *inputfile = NULL, *outputfile = NULL;
- int contenttype = 0, unfold = 0;
+ bool contenttype = false;
+ bool unfold = false;
vevent *v, *nextvevent;
char *form = "mhical.24hour", *format = NULL;
char **argp, **arguments, *cp;
icaldebug = 0; /* Global provided by bison (with name-prefix "ical"). */
- if (nmh_init(argv[0], 1)) { return 1; }
+ if (nmh_init(argv[0], true, false)) { return 1; }
arguments = getarguments (invo_name, argc, argv, 1);
argp = arguments;
ambigsw (cp, switches);
done (1);
case UNKWNSW:
- adios (NULL, "-%s unknown", cp);
+ die("-%s unknown", cp);
case HELPSW: {
char buf[128];
case REPLYSW:
if (! (cp = *argp++) || (*cp == '-' && cp[1]))
- adios (NULL, "missing argument to %s", argp[-2]);
+ die("missing argument to %s", argp[-2]);
if (! strcasecmp (cp, "accept")) {
action = ACT_ACCEPT;
} else if (! strcasecmp (cp, "decline")) {
} else if (! strcasecmp (cp, "delegate")) {
action = ACT_DELEGATE;
} else {
- adios (NULL, "Unknown action: %s", cp);
+ die("Unknown action: %s", cp);
}
continue;
case FORMSW:
if (! (form = *argp++) || *form == '-')
- adios (NULL, "missing argument to %s", argp[-2]);
+ die("missing argument to %s", argp[-2]);
format = NULL;
continue;
case FMTSW:
if (! (format = *argp++) || *format == '-')
- adios (NULL, "missing argument to %s", argp[-2]);
+ die("missing argument to %s", argp[-2]);
form = NULL;
continue;
case INFILESW:
if (! (cp = *argp++) || (*cp == '-' && cp[1]))
- adios (NULL, "missing argument to %s", argp[-2]);
- infile = *cp == '-' ? add (cp, NULL) : path (cp, TFILE);
+ die("missing argument to %s", argp[-2]);
+ infile = *cp == '-' ? mh_xstrdup(cp) : path (cp, TFILE);
continue;
case OUTFILESW:
if (! (cp = *argp++) || (*cp == '-' && cp[1]))
- adios (NULL, "missing argument to %s", argp[-2]);
- outfile = *cp == '-' ? add (cp, NULL) : path (cp, TFILE);
+ die("missing argument to %s", argp[-2]);
+ outfile = *cp == '-' ? mh_xstrdup(cp) : path (cp, TFILE);
continue;
case CONTENTTYPESW:
- contenttype = 1;
+ contenttype = true;
continue;
case NCONTENTTYPESW:
- contenttype = 0;
+ contenttype = false;
continue;
case UNFOLDSW:
- unfold = 1;
+ unfold = true;
continue;
}
}
free (outfile);
}
- return 0;
+ return parser_status;
}
/*
* - Excise VALARM sections.
*/
static void
-convert_to_reply (contentline *clines, act action) {
+convert_to_reply (contentline *clines, act action)
+{
char *partstat = NULL;
- int found_my_attendee_line = 0;
+ bool found_my_attendee_line = false;
contentline *node;
convert_common (clines, action);
while (getname ("")) { continue; }
if (ismymbox (mn)) {
- found_my_attendee_line = 1;
+ found_my_attendee_line = true;
for (p = node->params; p && p->param_name; p = p->next) {
value_list *v;
(node = find_contentline (clines, "BEGIN", "VEVENT"))) {
contentline *new_node = add_contentline (node, "ATTENDEE");
- add_param_name (new_node, strdup ("PARTSTAT"));
- add_param_value (new_node, strdup (partstat));
- add_param_name (new_node, strdup ("CN"));
- add_param_value (new_node, strdup (getfullname ()));
+ add_param_name (new_node, mh_xstrdup ("PARTSTAT"));
+ add_param_value (new_node, mh_xstrdup (partstat));
+ add_param_name (new_node, mh_xstrdup ("CN"));
+ add_param_value (new_node, mh_xstrdup (getfullname ()));
new_node->value = concat ("MAILTO:", getlocalmbox (), NULL);
}
}
* - Excise VALARM sections.
*/
static void
-convert_to_cancellation (contentline *clines) {
+convert_to_cancellation (contentline *clines)
+{
contentline *node;
convert_common (clines, ACT_CANCEL);
if ((node = find_contentline (clines, "STATUS", 0)) &&
! strcasecmp (node->value, "CONFIRMED")) {
free (node->value);
- node->value = strdup ("CANCELLED");
+ node->value = mh_xstrdup ("CANCELLED");
}
if ((node = find_contentline (clines, "SEQUENCE", 0))) {
(void) snprintf (buf, sizeof buf, "%d", sequence + 1);
free (node->value);
- node->value = strdup (buf);
+ node->value = mh_xstrdup (buf);
}
}
static void
-convert_common (contentline *clines, act action) {
+convert_common (contentline *clines, act action)
+{
contentline *node;
- int in_valarm;
+ bool in_valarm;
if ((node = find_contentline (clines, "METHOD", 0))) {
free (node->value);
- node->value = strdup (action == ACT_CANCEL ? "CANCEL" : "REPLY");
+ node->value = mh_xstrdup (action == ACT_CANCEL ? "CANCEL" : "REPLY");
}
if ((node = find_contentline (clines, "PRODID", 0))) {
free (node->value);
- node->value = strdup ("nmh mhical v0.1");
+ node->value = mh_xstrdup ("nmh mhical v0.1");
}
if ((node = find_contentline (clines, "VERSION", 0))) {
if (! node->value) {
- admonish (NULL, "Version property is missing value, assume 2.0");
- node->value = strdup ("2.0");
+ inform("Version property is missing value, assume 2.0, continuing...");
+ node->value = mh_xstrdup ("2.0");
}
if (strcmp (node->value, "2.0")) {
- admonish (NULL, "supports the Version 2.0 specified by RFC 5545 "
- "but iCalendar object has Version %s", node->value);
- node->value = strdup ("2.0");
+ inform("supports the Version 2.0 specified by RFC 5545 "
+ "but iCalendar object has Version %s, continuing...",
+ node->value);
+ node->value = mh_xstrdup ("2.0");
}
}
insert = "Tentative: ";
break;
case ACT_DELEGATE:
- adios (NULL, "Delegate replies are not supported");
+ die("Delegate replies are not supported");
break;
case ACT_CANCEL:
insert = "Cancelled:";
node->value = tmp;
} else {
/* Should never get here. */
- adios (NULL, "Unknown action: %d", action);
+ die("Unknown action: %d", action);
}
}
if (strftime (buf, sizeof buf, "%Y%m%dT%H%M%SZ", &now_tm)) {
free (node->value);
- node->value = strdup (buf);
+ node->value = mh_xstrdup (buf);
} else {
- admonish (NULL, "strftime unable to format current time");
+ inform("strftime unable to format current time, continuing...");
}
} else {
- admonish (NULL, "gmtime_r failed on current time");
+ inform("gmtime_r failed on current time, continuing...");
}
}
/* Excise X- lines and VALARM section(s). */
- in_valarm = 0;
+ in_valarm = false;
for (node = clines; node; node = node->next) {
/* node->name will be NULL if the line was deleted. */
if (! node->name) { continue; }
if (in_valarm) {
if (! strcasecmp ("END", node->name) &&
! strcasecmp ("VALARM", node->value)) {
- in_valarm = 0;
+ in_valarm = false;
}
remove_contentline (node);
} else {
if (! strcasecmp ("BEGIN", node->name) &&
! strcasecmp ("VALARM", node->value)) {
- in_valarm = 1;
+ in_valarm = true;
remove_contentline (node);
} else if (! strncasecmp ("X-", node->name, 2)) {
remove_contentline (node);
/* Echo the input, but with unfolded lines. */
static void
-dump_unfolded (FILE *file, contentline *clines) {
+dump_unfolded (FILE *file, contentline *clines)
+{
contentline *node;
for (node = clines; node; node = node->next) {
}
static void
-output (FILE *file, contentline *clines, int contenttype) {
+output (FILE *file, contentline *clines, int contenttype)
+{
contentline *node;
if (contenttype) {
char *line = NULL;
size_t len;
- line = strdup (node->name);
+ line = mh_xstrdup (node->name);
line = format_params (line, node->params);
len = strlen (line);
line = fold (add (node->value, line),
clines->cr_before_lf == CR_BEFORE_LF);
- if (clines->cr_before_lf == LF_ONLY) {
- fprintf (file, "%s\n", line);
- } else {
- fprintf (file, "%s\r\n", line);
- }
+ fputs(line, file);
+ if (clines->cr_before_lf != LF_ONLY)
+ putc('\r', file);
+ putc('\n', file);
free (line);
}
}
* - attendees (limited to number specified in initialization)
*/
static void
-display (FILE *file, contentline *clines, char *nfs) {
+display (FILE *file, contentline *clines, char *nfs)
+{
tzdesc_t timezones = load_timezones (clines);
- int in_vtimezone;
- int in_valarm;
+ bool in_vtimezone;
+ bool in_valarm;
contentline *node;
struct format *fmt;
int dat[5] = { 0, 0, 0, INT_MAX, 0 };
if ((c = fmt_findcomp ("method"))) {
if ((node = find_contentline (clines, "METHOD", 0)) && node->value) {
- c->c_text = strdup (node->value);
+ c->c_text = mh_xstrdup (node->value);
}
}
if ((c = fmt_findcomp ("organizer"))) {
if ((node = find_contentline (clines, "ORGANIZER", 0)) &&
node->value) {
- c->c_text = strdup (identity (node));
+ c->c_text = mh_xstrdup (identity (node));
}
}
if ((c = fmt_findcomp ("summary"))) {
if ((node = find_contentline (clines, "SUMMARY", 0)) && node->value) {
- c->c_text = strdup (node->value);
+ c->c_text = mh_xstrdup (node->value);
}
}
/* Only display DESCRIPTION lines that are outside VALARM section(s). */
- in_valarm = 0;
+ in_valarm = false;
if ((c = fmt_findcomp ("description"))) {
for (node = clines; node; node = node->next) {
/* node->name will be NULL if the line was deleted. */
if (node->name && node->value && ! in_valarm &&
! strcasecmp ("DESCRIPTION", node->name) &&
strcasecmp (node->value, "\\n\\n")) {
- c->c_text = strdup (node->value);
+ c->c_text = mh_xstrdup (node->value);
} else if (in_valarm) {
if (! strcasecmp ("END", node->name) &&
! strcasecmp ("VALARM", node->value)) {
- in_valarm = 0;
+ in_valarm = false;
}
} else {
if (! strcasecmp ("BEGIN", node->name) &&
! strcasecmp ("VALARM", node->value)) {
- in_valarm = 1;
+ in_valarm = true;
}
}
}
if ((c = fmt_findcomp ("location"))) {
if ((node = find_contentline (clines, "LOCATION", 0)) &&
node->value) {
- c->c_text = strdup (node->value);
+ c->c_text = mh_xstrdup (node->value);
}
}
if ((c = fmt_findcomp ("dtstart"))) {
/* Find DTSTART outsize of a VTIMEZONE section. */
- in_vtimezone = 0;
+ in_vtimezone = false;
for (node = clines; node; node = node->next) {
/* node->name will be NULL if the line was deleted. */
if (! node->name) { continue; }
if (in_vtimezone) {
if (! strcasecmp ("END", node->name) &&
! strcasecmp ("VTIMEZONE", node->value)) {
- in_vtimezone = 0;
+ in_vtimezone = false;
}
} else {
if (! strcasecmp ("BEGIN", node->name) &&
! strcasecmp ("VTIMEZONE", node->value)) {
- in_vtimezone = 1;
+ in_vtimezone = true;
} else if (! strcasecmp ("DTSTART", node->name)) {
/* Got it: DTSTART outside of a VTIMEZONE section. */
char *datetime = format_datetime (timezones, node);
- c->c_text = datetime ? datetime : node->value;
+ c->c_text = datetime ? datetime : mh_xstrdup(node->value);
}
}
}
if ((c = fmt_findcomp ("dtend"))) {
if ((node = find_contentline (clines, "DTEND", 0)) && node->value) {
char *datetime = format_datetime (timezones, node);
- c->c_text = datetime ? datetime : node->value;
+ c->c_text = datetime ? datetime : strdup(node->value);
+ } else if ((node = find_contentline (clines, "DTSTART", 0)) &&
+ node->value) {
+ /* There is no DTEND. If there's a DTSTART, use it. If it
+ doesn't have a time, assume that the event is for the
+ entire day and append 23:59:59 to it so that it signifies
+ the end of the day. And assume local timezone. */
+ if (strchr(node->value, 'T')) {
+ char * datetime = format_datetime (timezones, node);
+ c->c_text = datetime ? datetime : strdup(node->value);
+ } else {
+ char *datetime;
+ contentline node_copy;
+
+ node_copy = *node;
+ node_copy.value = concat(node_copy.value, "T235959", NULL);
+ datetime = format_datetime (timezones, &node_copy);
+ c->c_text = datetime ? datetime : strdup(node_copy.value);
+ free(node_copy.value);
+ }
}
}
}
/* Don't call on the END:VCALENDAR line. */
- if (clines->next) {
+ if (clines && clines->next) {
(void) fmt_scan (fmt, buffer, INT_MAX, dat, NULL);
fputs (charstring_buffer (buffer), file);
fmt_free (fmt, 1);
}
static const char *
-identity (const contentline *node) {
+identity (const contentline *node)
+{
/* According to RFC 5545 § 3.3.3, an email address in the value
must be a mailto URI. */
if (! strncasecmp (node->value, "mailto:", 7)) {
}
static char *
-format_params (char *line, param_list *p) {
+format_params (char *line, param_list *p)
+{
for ( ; p && p->param_name; p = p->next) {
value_list *v;
size_t num_values = 0;
}
static char *
-fold (char *line, int uses_cr) {
+fold (char *line, int uses_cr)
+{
size_t remaining = strlen (line);
size_t current_line_len = 0;
charstring_t folded_line = charstring_create (2 * remaining);
#endif
charstring_push_back_chars (folded_line, cp, char_len, 1);
- remaining -= char_len > 0 ? char_len : 1;
+ remaining -= max(char_len, 1);
/* remaining must be > 0 to pass the loop condition above, so
if it's not > 1, it is == 1. */
}
}
- cp += char_len > 0 ? char_len : 1;
+ cp += max(char_len, 1);
}
free (line);