/* * Calcurse - text-based organizer * * Copyright (c) 2004-2022 calcurse Development Team * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other * materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Send your feedback or comments to : misc@calcurse.org * Calcurse home page : http://calcurse.org * */ #include #include #include #include #include #include #include #include "calcurse.h" /* Input types for parse_datetimearg() */ enum { ARG_DATE, ARG_DATE_TIME, ARG_TIME, ARG_ERR }; /* Long options */ enum { OPT_FILTER_INVERT = 1000, OPT_FILTER_TYPE, OPT_FILTER_HASH, OPT_FILTER_PATTERN, OPT_FILTER_START_FROM, OPT_FILTER_START_TO, OPT_FILTER_START_AFTER, OPT_FILTER_START_BEFORE, OPT_FILTER_START_RANGE, OPT_FILTER_END_FROM, OPT_FILTER_END_TO, OPT_FILTER_END_AFTER, OPT_FILTER_END_BEFORE, OPT_FILTER_END_RANGE, OPT_FILTER_PRIORITY, OPT_FILTER_COMPLETED, OPT_FILTER_UNCOMPLETED, OPT_FROM, OPT_TO, OPT_DAYS, OPT_FMT_APT, OPT_FMT_RAPT, OPT_FMT_EV, OPT_FMT_REV, OPT_FMT_TODO, OPT_DUMP_IMPORTED, OPT_EXPORT_UID, OPT_READ_ONLY, OPT_STATUS, OPT_DAEMON, OPT_INPUT_DATEFMT, OPT_OUTPUT_DATEFMT }; /* * Print Calcurse usage and exit. */ static void usage(void) { printf("%s\n", _("Usage:\n" "calcurse [-D ] [-C ] [-c ]\n" "calcurse -Q [--from ] [--to ] [--days ]\n" "calcurse -a | -d | -d | -n | -r[] | -s[] | -t[]\n" "calcurse -h | -v | --status | -G | -P | -g | -i | -x[] | --daemon")); } static void usage_try(void) { printf("%s\n", _("Try `calcurse -h` for more information.")); } /* * Print Calcurse version with a short copyright text and exit. */ static void version_arg(void) { printf(_("calcurse %s -- text-based organizer\n"), VERSION); putchar('\n'); printf("%s\n", _("Copyright (c) 2004-2022 calcurse Development Team.")); printf("%s\n", _("This is free software; see the source for copying conditions.")); } /* * Print the command line options and exit. */ static void help_arg(void) { usage(); putchar('\n'); printf("%s\n", _("Operations in command line mode:")); printf("%s\n", _(" -Q, --query Print items in a given query range")); printf("%s\n", _(" -G, --grep Grep items from the data files")); printf("%s\n", _(" -P, --purge Read items and write them back")); printf("%s\n", _("Query short forms:\n" "-a, -d |, -n, -r[], -s[], -t")); putchar('\n'); printf("%s\n", _("Note that filter, format and day-range options affect input or output:")); printf("%s\n", _(" --filter-* Filter items loaded by -Q, -G, -P and -x")); printf("%s\n", _(" --format-* Rewrite output from -Q, -G and --dump-imported")); printf("%s\n", _(" --from Limit day range of -Q.")); printf("%s\n", _(" --to Limit day range of -Q.")); printf("%s\n", _(" --days Limit day range of -Q.")); putchar('\n'); printf("%s\n", _(" --limit, -l Limit number of query results")); printf("%s\n", _(" --search, -S Match regular expression in queries")); printf("%s\n", _("Consult the man page for details.")); putchar('\n'); printf("%s\n", _("Miscellaneous:")); printf("%s\n", _(" -c, --calendar The calendar data file to use")); printf("%s\n", _(" -C, --confdir The configuration directory to use")); printf("%s\n", _(" --daemon Run notification daemon in the background")); printf("%s\n", _(" -D, --datadir The data directory to use")); printf("%s\n", _(" -g, --gc Run the garbage collector")); printf("%s\n", _(" -h, --help Show this help text")); printf("%s\n", _(" -i, --import Import iCal data from file")); printf("%s\n", _(" -q, --quiet Suppress import/export result message")); printf("%s\n", _(" --read-only Do not save configuration or data files")); printf("%s\n", _(" --status Display status of running instances")); printf("%s\n", _(" -v, --version Show version information")); printf("%s\n", _(" -x, --export[] Export to stdout in ical (default) or pcal format")); putchar('\n'); printf("%s\n", _("For more information, type '?' from within calcurse, or read the manpage.")); printf("%s\n", _("Submit feature requests and suggestions to .")); printf("%s\n", _("Submit bug reports to .")); } /* * Used to display the status of running instances of calcurse. * The displayed message will look like one of the following ones: * * calcurse is running (pid #) * calcurse is running in background (pid #) * calcurse is not running * * The status is obtained by looking at pid files in user data directory * (.calcurse.pid and .daemon.pid). */ static void status_arg(void) { int cpid, dpid; cpid = io_get_pid(path_cpid); dpid = io_get_pid(path_dpid); EXIT_IF(cpid && dpid, _("Error: both calcurse (pid: %d) and its daemon (pid: %d)\n" "seem to be running at the same time!\n" "Please check manually and restart calcurse.\n"), cpid, dpid); if (cpid) fprintf(stdout, _("calcurse is running (pid %d)\n"), cpid); else if (dpid) fprintf(stdout, _("calcurse is running in background (pid %d)\n"), dpid); else puts(_("calcurse is not running")); } /* Print TODO list and return the number of printed items. */ static int todo_arg(const char *format, int *limit, struct item_filter *filter) { const char *titlestr = filter->completed ? _("completed tasks:\n") : _("to do:\n"); int title = 1; int n = 0; llist_item_t *i; LLIST_FOREACH(&todolist, i) { if (*limit == 0) break; struct todo *todo = LLIST_TS_GET_DATA(i); if (title) { fputs(titlestr, stdout); title = 0; } print_todo(format, todo); n++; (*limit)--; } return n; } /* Print the next appointment within the upcoming 24 hours. */ static void next_arg(void) { struct notify_app next_app; const long current_time = now(); int time_left, hours_left, min_left; next_app.time = date_sec_change(current_time, 0, 1); next_app.got_app = 0; next_app.txt = NULL; recur_apoint_check_next(&next_app, current_time, get_today()); next_app = *apoint_check_next(&next_app, current_time); if (next_app.got_app) { time_left = next_app.time - current_time; /* In minutes rounded up. */ min_left = time_left / MININSEC + (time_left % MININSEC ? 1 : 0); hours_left = min_left / HOURINMIN; min_left = min_left % HOURINMIN; fputs(_("next appointment:\n"), stdout); fprintf(stdout, " [%02d:%02d] %s\n", hours_left, min_left, next_app.txt); mem_free(next_app.txt); } } /* * Print the date on stdout. */ static void arg_print_date(long date) { char date_str[BUFSIZ]; struct tm lt; localtime_r((time_t *) & date, <); strftime(date_str, BUFSIZ, conf.output_datefmt, <); fputs(date_str, stdout); fputs(":\n", stdout); } /* * Print appointments inside the given query range. * If no start day is given (-1), today is considered. * If no end date is given (-1), a range of 1 day is considered. */ static void date_arg_from_to(long from, long to, int add_line, const char *fmt_apt, const char *fmt_rapt, const char *fmt_ev, const char *fmt_rev, int *limit) { long date; for (date = from; date <= to; date = date_sec_change(date, 0, 1)) { day_store_items(date, 0, 1); if (day_item_count(0) == 0) continue; if (add_line) fputs("\n", stdout); arg_print_date(date); day_write_stdout(date, fmt_apt, fmt_rapt, fmt_ev, fmt_rev, limit); add_line = 1; } } /* * Convert a string with a (local time) date, date-time or time into * the Unix time for that point in time as follows: * - a date only is converted to midnight (beginning) of that day * - a date-time is converted to that day and time * - a time only is converted to that time of the current day * The date format is taken from the user configuration. * The type of the input string is returned in the type argument. */ static time_t parse_datetimearg(const char *str, int *type) { char *date = mem_strdup(str); *type = ARG_ERR; char *time; struct date day; unsigned hour, min; time_t ret; time = strchr(date, ' '); if (time) { /* Date and time? */ *time++ = '\0'; if (!parse_time(time, &hour, &min)) return -1; if (!parse_date(date, conf.input_datefmt, (int *)&day.yyyy, (int *)&day.mm, (int *)&day.dd, NULL)) return -1; ret = date2sec(day, hour, min); *type = ARG_DATE_TIME; } else /* Date?*/ if (parse_date(date, conf.input_datefmt, (int *)&day.yyyy, (int *)&day.mm, (int *)&day.dd, NULL)) { ret = date2sec(day, 0, 0); *type = ARG_DATE; } else /* Time? */ if (parse_time(date, &hour, &min)) { ret = date2sec(*ui_calendar_get_today(), hour, min); *type = ARG_TIME; } else return -1; return ret; } /* * Parse a "from,to" date range. * For an open-end range no change occurs at the open end. */ static int parse_daterange(const char *str, time_t *date_from, time_t *date_to) { int type, ret = 0; char *s = mem_strdup(str); char *p = strchr(s, ','); if (!p) goto cleanup; *p = '\0'; p++; if (*s != '\0') { *date_from = parse_datetimearg(s, &type); if (*date_from == -1) goto cleanup; } if (*p != '\0') { *date_to = parse_datetimearg(p, &type); if (*date_to == -1) goto cleanup; if (type == ARG_DATE) *date_to = ENDOFDAY(*date_to); } ret = 1; cleanup: mem_free(s); return ret; } static int parse_type_mask(const char *str) { char *buf = mem_strdup(str), *p; int mask = 0; for (p = strtok(buf, ","); p; p = strtok(NULL, ",")) { if (!strcmp(p, "event")) { mask |= TYPE_MASK_EVNT; } else if (!strcmp(p, "apt")) { mask |= TYPE_MASK_APPT; } else if (!strcmp(p, "recur-event")) { mask |= TYPE_MASK_RECUR_EVNT; } else if (!strcmp(p, "recur-apt")) { mask |= TYPE_MASK_RECUR_APPT; } else if (!strcmp(p, "recur")) { mask |= TYPE_MASK_RECUR; } else if (!strcmp(p, "cal")) { mask |= TYPE_MASK_CAL; } else if (!strcmp(p, "todo")) { mask |= TYPE_MASK_TODO; } else { mask = 0; goto cleanup; } } cleanup: mem_free(buf); return mask; } /* * Parse the command-line arguments and call the appropriate * routines to handle those arguments. Also initialize the data paths. * Exit here in case of errors else return the non-interactive value. */ int parse_args(int argc, char **argv) { /* Command-line flags - NOTE that read_only is global */ int grep = 0, grep_filter = 0, purge = 0, query = 0, next = 0; int status = 0, gc = 0, import = 0, export = 0, daemon = 0; /* Command line invocation */ int filter_opt = 0, format_opt = 0, query_range = 0, cmd_line = 0; int start_from = 0, start_to = 0, end_from = 0, end_to = 0; /* Query ranges */ time_t from = -1, to = -1; int range = 0; int limit = INT_MAX; /* Filters */ struct item_filter filter = { 0, 0, NULL, NULL, -1, -1, -1, -1, 0, 0, 0 }; /* Format strings */ const char *fmt_apt = NULL; const char *fmt_rapt = NULL; const char *fmt_ev = NULL; const char *fmt_rev = NULL; const char *fmt_todo = NULL; /* Import and export parameters */ int xfmt = IO_EXPORT_ICAL; int dump_imported = 0, export_uid = 0; /* Data file locations */ const char *datadir = NULL; const char *cfile = NULL, *confdir = NULL; char *ifile = NULL; int ret, non_interactive = 1; int ch, cpid, type; regex_t reg; char buf[BUFSIZ]; struct tm tm; time_t t; static const char *optstr = "PFgGhvnNax::t::C:d:c:r::s::S:D:i:l:qQ"; struct option longopts[] = { {"appointment", no_argument, NULL, 'a'}, {"calendar", required_argument, NULL, 'c'}, /* Deprecated */ {"conf", required_argument, NULL, 'C'}, {"confdir", required_argument, NULL, 'C'}, {"day", required_argument, NULL, 'd'}, {"datadir", required_argument, NULL, 'D'}, /* Deprecated */ {"directory", required_argument, NULL, 'D'}, {"filter", no_argument, NULL, 'F'}, {"gc", no_argument, NULL, 'g'}, {"grep", no_argument, NULL, 'G'}, {"help", no_argument, NULL, 'h'}, {"import", required_argument, NULL, 'i'}, {"limit", required_argument, NULL, 'l'}, {"next", no_argument, NULL, 'n'}, {"note", no_argument, NULL, 'N'}, {"purge", no_argument, NULL, 'P'}, {"range", optional_argument, NULL, 'r'}, {"startday", optional_argument, NULL, 's'}, {"search", required_argument, NULL, 'S'}, {"todo", optional_argument, NULL, 't'}, {"version", no_argument, NULL, 'v'}, {"export", optional_argument, NULL, 'x'}, {"quiet", no_argument, NULL, 'q'}, {"query", optional_argument, NULL, 'Q'}, {"filter-invert", no_argument, NULL, OPT_FILTER_INVERT}, {"filter-type", required_argument, NULL, OPT_FILTER_TYPE}, {"filter-hash", required_argument, NULL, OPT_FILTER_HASH}, {"filter-pattern", required_argument, NULL, OPT_FILTER_PATTERN}, {"filter-start-from", required_argument, NULL, OPT_FILTER_START_FROM}, {"filter-start-to", required_argument, NULL, OPT_FILTER_START_TO}, {"filter-start-after", required_argument, NULL, OPT_FILTER_START_AFTER}, {"filter-start-before", required_argument, NULL, OPT_FILTER_START_BEFORE}, {"filter-start-range", required_argument, NULL, OPT_FILTER_START_RANGE}, {"filter-end-from", required_argument, NULL, OPT_FILTER_END_FROM}, {"filter-end-to", required_argument, NULL, OPT_FILTER_END_TO}, {"filter-end-after", required_argument, NULL, OPT_FILTER_END_AFTER}, {"filter-end-before", required_argument, NULL, OPT_FILTER_END_BEFORE}, {"filter-end-range", required_argument, NULL, OPT_FILTER_END_RANGE}, {"filter-priority", required_argument, NULL, OPT_FILTER_PRIORITY}, {"filter-completed", no_argument, NULL, OPT_FILTER_COMPLETED}, {"filter-uncompleted", no_argument, NULL, OPT_FILTER_UNCOMPLETED}, {"from", required_argument, NULL, OPT_FROM}, {"to", required_argument, NULL, OPT_TO}, {"days", required_argument, NULL, OPT_DAYS}, {"format-apt", required_argument, NULL, OPT_FMT_APT}, {"format-recur-apt", required_argument, NULL, OPT_FMT_RAPT}, {"format-event", required_argument, NULL, OPT_FMT_EV}, {"format-recur-event", required_argument, NULL, OPT_FMT_REV}, {"format-todo", required_argument, NULL, OPT_FMT_TODO}, {"export-uid", no_argument, NULL, OPT_EXPORT_UID}, {"dump-imported", no_argument, NULL, OPT_DUMP_IMPORTED}, {"read-only", no_argument, NULL, OPT_READ_ONLY}, {"status", no_argument, NULL, OPT_STATUS}, {"daemon", no_argument, NULL, OPT_DAEMON}, {"input-datefmt", required_argument, NULL, OPT_INPUT_DATEFMT}, {"output-datefmt", required_argument, NULL, OPT_OUTPUT_DATEFMT}, {NULL, no_argument, NULL, 0} }; /* * Load the configuration file first to get the input date format for * parsing the remaining options. */ while ((ch = getopt_long(argc, argv, optstr, longopts, NULL)) != -1) { switch (ch) { case 'C': confdir = optarg; break; case 'D': datadir = optarg; break; case 'c': cfile = optarg; break; case '?': usage(); usage_try(); exit(EXIT_FAILURE); \ } } io_init(cfile, datadir, confdir); vars_init(); notify_init_vars(); if (io_file_exists(path_conf)) config_load(); /* Parse the remaining options. */ optind = 1; while ((ch = getopt_long(argc, argv, optstr, longopts, NULL)) != -1) { switch (ch) { case 'a': filter.type_mask |= TYPE_MASK_CAL; query = 1; break; case 'c': break; case 'C': break; case 'd': if (is_all_digit(optarg) || (*optarg == '-' && is_all_digit(optarg + 1))) { range = atoi(optarg); EXIT_IF(range == 0, _("invalid range: %s"), optarg); } else { from = parse_datetimearg(optarg, &type); EXIT_IF(from == -1 || type != ARG_DATE, _("invalid date: %s"), optarg); } filter.type_mask |= TYPE_MASK_CAL; query = 1; break; case 'D': break; case 'F': grep_filter = grep = 1; break; case 'h': help_arg(); exit(EXIT_SUCCESS); case 'g': gc = 1; break; case 'G': grep = 1; break; case 'i': import = 1; ifile = optarg; break; case 'l': limit = atoi(optarg); break; case 'n': next = 1; break; case 'P': filter.invert = 1; purge = grep = 1; break; case 'r': if (optarg) range = atoi(optarg); else range = 1; EXIT_IF(range == 0, _("invalid range: %s"), optarg); filter.type_mask |= TYPE_MASK_CAL; query = 1; break; case 's': if (!optarg) optarg = "today"; from = parse_datetimearg(optarg, &type); EXIT_IF(from == -1 || type != ARG_DATE, _("invalid date: %s"), optarg); filter.type_mask |= TYPE_MASK_CAL; query = 1; break; case 't': if (optarg) { EXIT_IF(!is_all_digit(optarg), _("invalid priority: %s"), optarg); filter.priority = atoi(optarg); if (filter.priority == 0) filter.completed = 1; else filter.uncompleted = 1; EXIT_IF(filter.priority > 9, _("invalid priority: %s"), optarg); } else { filter.uncompleted = 1; } filter.type_mask |= TYPE_MASK_TODO; query = 1; break; case 'v': version_arg(); exit(EXIT_SUCCESS); case 'x': export = 1; if (optarg) { if (!strcmp(optarg, "ical")) xfmt = IO_EXPORT_ICAL; else if (!strcmp(optarg, "pcal")) xfmt = IO_EXPORT_PCAL; else EXIT(_("invalid export format: %s"), optarg); } break; case 'q': quiet = 1; break; case 'Q': query = 1; break; case OPT_FILTER_INVERT: filter.invert = !filter.invert; filter_opt = 1; break; case OPT_FILTER_TYPE: filter.type_mask = parse_type_mask(optarg); EXIT_IF(filter.type_mask == 0, _("invalid filter mask")); filter_opt = 1; break; case OPT_FILTER_HASH: filter.hash = mem_strdup(optarg); filter_opt = 1; break; case 'S': case OPT_FILTER_PATTERN: EXIT_IF(filter.regex, _("cannot handle more than one regular expression")); if (regcomp(®, optarg, REG_EXTENDED)) EXIT(_("could not compile regular expression: %s"), optarg); filter.regex = ® filter_opt = 1; break; /* * A date only is a time span (a day) and interpreted thus: * "to" means "to end of day" * "after" means "from start of next day" */ case OPT_FILTER_START_FROM: EXIT_IF(start_from, _("filter criterion already in use: %s"), argv[optind - 2]); filter.start_from = parse_datetimearg(optarg, &type); EXIT_IF(filter.start_from == -1, _("invalid date: %s"), optarg); start_from = 1; filter_opt = 1; break; case OPT_FILTER_START_TO: EXIT_IF(start_to, _("filter criterion already in use: %s"), argv[optind - 2]); filter.start_to = parse_datetimearg(optarg, &type); EXIT_IF(filter.start_to == -1, _("invalid date: %s"), optarg); if (type == ARG_DATE) filter.start_to = ENDOFDAY(filter.start_to); start_to = 1; filter_opt = 1; break; case OPT_FILTER_START_AFTER: EXIT_IF(start_from, _("filter criterion already in use: %s"), argv[optind - 2]); filter.start_from = parse_datetimearg(optarg, &type); EXIT_IF(filter.start_from == -1, _("invalid date: %s"), optarg); if (type == ARG_DATE) filter.start_from = NEXTDAY(filter.start_from); else filter.start_from++; start_from = 1; filter_opt = 1; break; case OPT_FILTER_START_BEFORE: EXIT_IF(start_to, _("filter criterion already in use: %s"), argv[optind - 2]); filter.start_to = parse_datetimearg(optarg, &type); EXIT_IF(filter.start_to == -1, _("invalid date: %s"), optarg); filter.start_to--; start_to = 1; filter_opt = 1; break; case OPT_FILTER_START_RANGE: EXIT_IF(start_from, _("filter criterion already in use: %s"), argv[optind - 2]); EXIT_IF(start_to, _("filter criterion already in use: %s"), argv[optind - 2]); /* Set initialization values in case of open-end range. */ filter.start_from = filter.start_to = -1; EXIT_IF(!parse_daterange(optarg, &filter.start_from, &filter.start_to), _("invalid date range: %s"), optarg); start_from = 1; start_to = 1; filter_opt = 1; break; case OPT_FILTER_END_FROM: EXIT_IF(end_from, _("filter criterion already in use: %s"), argv[optind - 2]); filter.end_from = parse_datetimearg(optarg, &type); EXIT_IF(filter.end_from == -1, _("invalid date: %s"), optarg); end_from = 1; filter_opt = 1; break; case OPT_FILTER_END_TO: EXIT_IF(end_to, _("filter criterion already in use: %s"), argv[optind - 2]); filter.end_to = parse_datetimearg(optarg, &type); EXIT_IF(filter.end_to == -1, _("invalid date: %s"), optarg); if (type == ARG_DATE) filter.end_to = ENDOFDAY(filter.end_to); end_to = 1; filter_opt = 1; break; case OPT_FILTER_END_AFTER: EXIT_IF(end_from, _("filter criterion already in use: %s"), argv[optind - 2]); filter.end_from = parse_datetimearg(optarg, &type); EXIT_IF(filter.end_from == -1, _("invalid date: %s"), optarg); if (type == ARG_DATE) filter.end_from = NEXTDAY(filter.end_from); else filter.end_from++; end_from = 1; filter_opt = 1; break; case OPT_FILTER_END_BEFORE: EXIT_IF(end_to, _("filter criterion already in use: %s"), argv[optind - 2]); filter.end_to = parse_datetimearg(optarg, &type); EXIT_IF(filter.end_to == -1, _("invalid date: %s"), optarg); filter.end_to--; end_to = 1; filter_opt = 1; break; case OPT_FILTER_END_RANGE: EXIT_IF(end_from, _("filter criterion already in use: %s"), argv[optind - 2]); EXIT_IF(end_to, _("filter criterion already in use: %s"), argv[optind - 2]); /* Set default values in case of open-ended range. */ filter.start_from = filter.start_to = -1; EXIT_IF(!parse_daterange(optarg, &filter.end_from, &filter.end_to), _("invalid date range: %s"), optarg); end_from = 1; end_to = 1; filter_opt = 1; break; case OPT_FILTER_PRIORITY: filter.priority = atoi(optarg); EXIT_IF(filter.priority < 1 || filter.priority > 9, _("invalid priority: %s"), optarg); filter_opt = 1; break; case OPT_FILTER_COMPLETED: filter.completed = 1; filter_opt = 1; break; case OPT_FILTER_UNCOMPLETED: filter.uncompleted = 1; filter_opt = 1; break; case OPT_FROM: from = parse_datetimearg(optarg, &type); EXIT_IF(from == -1 || type != ARG_DATE, _("invalid date: %s"), optarg); query_range = 1; break; case OPT_TO: to = parse_datetimearg(optarg, &type); EXIT_IF(to == -1 || type != ARG_DATE, _("invalid date: %s"), optarg); query_range = 1; break; case OPT_DAYS: range = atoi(optarg); EXIT_IF(range == 0, _("invalid range: %s"), optarg); query_range = 1; break; case OPT_FMT_APT: fmt_apt = optarg; format_opt = 1; break; case OPT_FMT_RAPT: fmt_rapt = optarg; format_opt = 1; break; case OPT_FMT_EV: fmt_ev = optarg; format_opt = 1; break; case OPT_FMT_REV: fmt_rev = optarg; format_opt = 1; break; case OPT_FMT_TODO: fmt_todo = optarg; format_opt = 1; break; case OPT_DUMP_IMPORTED: dump_imported = 1; break; case OPT_EXPORT_UID: export_uid = 1; break; case OPT_READ_ONLY: read_only = 1; break; case OPT_STATUS: status = 1; break; case OPT_DAEMON: EXIT_IF(cpid = io_get_pid(path_cpid), _("calcurse is running (pid = %d)"), cpid); daemon = 1; filter.type_mask = TYPE_MASK_APPT | TYPE_MASK_RECUR_APPT; break; case OPT_INPUT_DATEFMT: conf.input_datefmt = atoi(optarg); EXIT_IF(conf.input_datefmt < 1 || conf.input_datefmt > 4, _("invalid input date format: %s"), optarg); cmd_line = 1; break; case OPT_OUTPUT_DATEFMT: time(&t); localtime_r(&t, &tm); EXIT_IF(!strftime(buf, sizeof(buf), optarg, &tm), _("invalid output date format: %s"), optarg); strncpy(conf.output_datefmt, optarg, sizeof(conf.output_datefmt) - 1); conf.output_datefmt[sizeof(conf.output_datefmt) - 1] = '\0'; cmd_line = 1; break; } } if (filter.type_mask == 0) filter.type_mask = TYPE_MASK_ALL; if (status + grep + query + next + gc + import + export + daemon > 1 || optind < argc || (filter_opt && !(grep + query + export)) || (format_opt && !(grep + query + dump_imported)) || (query_range && !query) || (purge && !filter.invert) ) EXIT(_("invalid argument combination")); EXIT_IF(to >= 0 && range, _("cannot specify a range and an end date")); if (from == -1) from = get_today(); if (to == -1) to = ENDOFDAY(from); EXIT_IF(to < from, _("end date cannot come before start date")); if (range > 0) to = date_sec_change(from, 0, range - 1); else if (range < 0) from = date_sec_change(to, 0, range + 1); io_check_dir(path_ddir); io_check_dir(path_notes); io_check_dir(path_cdir); io_check_dir(path_hooks); if (status) { status_arg(); } else if (grep) { io_check_file(path_apts); io_check_file(path_todo); io_check_file(path_conf); io_load_data(&filter, FORCE); if (purge || grep_filter) { io_save_todo(path_todo); io_save_apts(path_apts); } else { /* * Use default values for non-specified format strings. */ fmt_apt = fmt_apt ? fmt_apt : "%(raw)"; fmt_rapt = fmt_rapt ? fmt_rapt : "%(raw)"; fmt_ev = fmt_ev ? fmt_ev : "%(raw)"; fmt_rev = fmt_rev ? fmt_rev : "%(raw)"; fmt_todo = fmt_todo ? fmt_todo : "%(raw)"; io_dump_todo(fmt_todo); io_dump_apts(fmt_apt, fmt_rapt, fmt_ev, fmt_rev); } } else if (query) { io_check_file(path_apts); io_check_file(path_todo); io_check_file(path_conf); io_load_data(&filter, FORCE); /* Use default values for non-specified format strings. */ fmt_apt = fmt_apt ? fmt_apt : " - %S -> %E\n\t%m\n"; fmt_rapt = fmt_rapt ? fmt_rapt : " - %S -> %E\n\t%m\n"; fmt_ev = fmt_ev ? fmt_ev : " * %m\n"; fmt_rev = fmt_rev ? fmt_rev : " * %m\n"; fmt_todo = fmt_todo ? fmt_todo : "%p. %m\n"; int add_line = todo_arg(fmt_todo, &limit, &filter); date_arg_from_to(from, to, add_line, fmt_apt, fmt_rapt, fmt_ev, fmt_rev, &limit); } else if (next) { io_check_file(path_apts); io_load_app(&filter); next_arg(); } else if (gc) { io_check_file(path_apts); io_check_file(path_todo); io_load_data(NULL, FORCE); note_gc(); } else if (import) { io_check_file(path_apts); io_check_file(path_todo); io_load_data(NULL, FORCE); if (dump_imported) { /* * Use default values for non-specified format strings. */ fmt_apt = fmt_apt ? fmt_apt : "%(raw)"; fmt_rapt = fmt_rapt ? fmt_rapt : "%(raw)"; fmt_ev = fmt_ev ? fmt_ev : "%(raw)"; fmt_rev = fmt_rev ? fmt_rev : "%(raw)"; fmt_todo = fmt_todo ? fmt_todo : "%(raw)"; } else { /* * Do not dump items, unset format strings explicitly. */ fmt_apt = fmt_rapt = fmt_ev = fmt_rev = NULL; fmt_todo = NULL; } ret = io_import_data(IO_IMPORT_ICAL, ifile, fmt_ev, fmt_rev, fmt_apt, fmt_rapt, fmt_todo); io_save_apts(path_apts); io_save_todo(path_todo); if (!ret) exit_calcurse(EXIT_FAILURE); } else if (export) { io_check_file(path_apts); io_check_file(path_todo); io_load_data(&filter, FORCE); io_export_data(xfmt, export_uid); } else if (daemon) { dmon_stop(); dmon_start(0); } else if (!cmd_line) { /* interactive mode */ non_interactive = 0; } /* Free filter parameters. */ if (filter.regex) regfree(filter.regex); return non_interactive; }