aboutsummaryrefslogtreecommitdiffstats
path: root/src/io.c
diff options
context:
space:
mode:
authorFrederic Culot <calcurse@culot.org>2006-07-31 21:00:02 +0000
committerFrederic Culot <calcurse@culot.org>2006-07-31 21:00:02 +0000
commitac36e94341ca73d581f0df39f1c7bbf2138b2845 (patch)
tree47de561cd962ff8f47f6d811109907f15b9ff989 /src/io.c
downloadcalcurse-ac36e94341ca73d581f0df39f1c7bbf2138b2845.tar.gz
calcurse-ac36e94341ca73d581f0df39f1c7bbf2138b2845.zip
Initial revision
Diffstat (limited to 'src/io.c')
-rwxr-xr-xsrc/io.c484
1 files changed, 484 insertions, 0 deletions
diff --git a/src/io.c b/src/io.c
new file mode 100755
index 0000000..35c5802
--- /dev/null
+++ b/src/io.c
@@ -0,0 +1,484 @@
+/* $calcurse: io.c,v 1.1 2006/07/31 21:00:03 culot Exp $ */
+
+/*
+ * Calcurse - text-based organizer
+ * Copyright (c) 2004-2006 Frederic Culot
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Send your feedback or comments to : calcurse@culot.org
+ * Calcurse home page : http://culot.org/calcurse
+ *
+ */
+
+#include <ncurses.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <math.h>
+#include <unistd.h>
+
+#include "i18n.h"
+#include "utils.h"
+#include "custom.h"
+#include "todo.h"
+#include "event.h"
+#include "apoint.h"
+#include "recur.h"
+#include "io.h"
+#include "vars.h"
+
+/*
+ * Initialization of data paths. The argument cfile is the variable
+ * which contains the calendar file. If none is given, then the default
+ * one (~/.calcurse/apts) is taken. If the one given does not exist, it
+ * is created.
+ */
+void
+io_init(char *cfile)
+{
+ FILE *data_file;
+ char *home;
+ char apts_file[MAX_LENGTH] = "";
+ int ch;
+
+ home = getenv("HOME");
+ if (home == NULL) {
+ home = ".";
+ }
+ snprintf(path_dir, MAX_LENGTH, "%s/" DIR_NAME, home);
+ snprintf(path_todo, MAX_LENGTH, "%s/" TODO_PATH, home);
+ snprintf(path_conf, MAX_LENGTH, "%s/" CONF_PATH, home);
+ if (cfile == NULL) {
+ snprintf(path_apts, MAX_LENGTH, "%s/" APTS_PATH, home);
+ } else {
+ snprintf(apts_file, MAX_LENGTH, "%s", cfile);
+ strncpy(path_apts, apts_file, MAX_LENGTH);
+ /* check if the file exists, otherwise create it */
+ data_file = fopen(path_apts, "r");
+ if (data_file == NULL) {
+ printf(_("%s does not exist, create it now [y or n] ? "), path_apts);
+ ch = getchar();
+ switch (ch) {
+ case 'N':
+ case 'n':
+ printf(_("aborting...\n"));
+ exit(EXIT_FAILURE);
+ break;
+
+ case 'Y':
+ case 'y':
+ data_file = fopen(path_apts, "w");
+ if (data_file == NULL) {
+ perror(path_apts);
+ exit(EXIT_FAILURE);
+ } else {
+ printf(_("%s successfully created\n"),path_apts);
+ printf(_("starting interactive mode...\n"));
+ }
+ break;
+
+ default:
+ printf(_("aborting...\n"));
+ exit(EXIT_FAILURE);
+ break;
+ }
+ }
+ fclose(data_file);
+ }
+}
+
+ /* get data from file */
+void extract_data(char *dst_data, const char *org, int len)
+{
+ for (;;) {
+ if (*org == '\n' || *org == '\0')
+ break;
+ *dst_data++ = *org++;
+ }
+ *dst_data = '\0';
+}
+
+/* Save the calendar data */
+void
+save_cal(bool auto_save, bool confirm_quit,
+ bool confirm_delete, bool skip_system_dialogs,
+ bool skip_progress_bar, bool week_begins_on_monday,
+ int colr, int layout)
+{
+ FILE *data_file;
+ struct event_s *k;
+ struct apoint_s *j;
+ struct todo_s *i;
+ char *access_pb = _("Problems accessing data file ...");
+ char *config_txt =
+ "#\n# Calcurse configuration file\n#\n# This file sets the configuration options used by Calcurse. These\n# options are usually set from within Calcurse. A line beginning with \n# a space or tab is considered to be a continuation of the previous line.\n# For a variable to be unset its value must be blank.\n# To set a variable to the empty string its value should be \"\".\n# Lines beginning with \"#\" are comments, and ignored by Calcurse.\n";
+ char *save_success = _("The data files were successfully saved");
+ char *enter = _("Press [ENTER] to continue");
+ bool save = true, show_bar = false;
+
+ if (!skip_progress_bar) show_bar = true;
+
+ /* Save the user configuration. */
+
+ if (show_bar) progress_bar(save, 1);
+ data_file = fopen(path_conf, "w");
+ if (data_file == (FILE *) 0)
+ status_mesg(access_pb, "");
+ else {
+ fprintf(data_file, "%s\n", config_txt);
+
+ fprintf(data_file,
+ "# If this option is set to yes, automatic save is done when quitting\n");
+ fprintf(data_file, "auto_save=\n");
+ fprintf(data_file, "%s\n",
+ (auto_save) ? "yes" : "no");
+
+ fprintf(data_file,
+ "\n# If this option is set to yes, confirmation is required before quitting\n");
+ fprintf(data_file, "confirm_quit=\n");
+ fprintf(data_file, "%s\n",
+ (confirm_quit) ? "yes" : "no");
+
+ fprintf(data_file,
+ "\n# If this option is set to yes, confirmation is required before deleting an event\n");
+ fprintf(data_file, "confirm_delete=\n");
+ fprintf(data_file, "%s\n",
+ (confirm_delete) ? "yes" : "no");
+
+ fprintf(data_file,
+ "\n# If this option is set to yes, messages about loaded and saved data will not be displayed\n");
+ fprintf(data_file, "skip_system_dialogs=\n");
+ fprintf(data_file, "%s\n",
+ (skip_system_dialogs) ? "yes" : "no");
+
+ fprintf(data_file,
+ "\n# If this option is set to yes, progress bar appearing when saving data will not be displayed\n");
+ fprintf(data_file, "skip_progress_bar=\n");
+ fprintf(data_file, "%s\n",
+ (skip_progress_bar) ? "yes" : "no");
+
+ fprintf(data_file,
+ "\n# If this option is set to yes, monday is the first day of the week, else it is sunday\n");
+ fprintf(data_file, "week_begins_on_monday=\n");
+ fprintf(data_file, "%s\n",
+ (week_begins_on_monday) ? "yes" : "no");
+
+ fprintf(data_file,
+ "\n# This is the color theme used for menus (1 to 8) :\n");
+ fprintf(data_file, "color-theme=\n");
+ fprintf(data_file, "%d\n", colr);
+
+ fprintf(data_file,
+ "\n# This is the layout of the calendar (1 to 4) :\n");
+ fprintf(data_file, "layout=\n");
+ fprintf(data_file, "%d\n", layout);
+ fclose(data_file);
+ }
+
+ /* Save the todo data file. */
+ if (show_bar) progress_bar(save, 2);
+ data_file = fopen(path_todo, "w");
+ if (data_file == (FILE *) 0)
+ status_mesg(access_pb, "");
+ else {
+ for (i = todolist; i != 0; i = i->next)
+ fprintf(data_file, "%s\n", i->mesg);
+ fclose(data_file);
+ }
+
+ /*
+ * Save the apts data file, which contains the
+ * appointments first, and then the events.
+ * Recursive items are written first.
+ */
+ if (show_bar) progress_bar(save, 3);
+ data_file = fopen(path_apts, "w");
+ if (data_file == (FILE *) 0)
+ status_mesg(access_pb, "");
+ else {
+ recur_save_data(data_file);
+ for (j = apointlist; j != 0; j = j->next)
+ apoint_write(j, data_file);
+ for (k = eventlist; k != 0; k = k->next)
+ event_write(k, data_file);
+ fclose(data_file);
+ }
+
+
+ /* Print a message telling data were saved */
+ if (!skip_system_dialogs){
+ status_mesg(save_success, enter);
+ wgetch(swin);
+ }
+}
+
+/*
+ * Check what type of data is written in the appointment file,
+ * and then load either: a new appointment, a new event, or a new
+ * recursive item (which can also be either an event or an appointment).
+ */
+void load_app()
+{
+ FILE *data_file;
+ int c, is_appointment, is_event, is_recursive;
+ struct tm start, end, until, *lt;
+ time_t t;
+ int id = 0;
+ int freq;
+ char type;
+ char *error =
+ _("FATAL ERROR in load_app: wrong format in the appointment or event\n");
+
+ t = time(NULL);
+ lt = localtime(&t);
+ start = end = until = *lt;
+
+ data_file = fopen(path_apts, "r");
+ for (;;) {
+ is_appointment = is_event = is_recursive = 0;
+ c = getc(data_file);
+ if (c == EOF)
+ break;
+ ungetc(c, data_file);
+
+ /* Read the date first: it is common to both events
+ * and appointments.
+ */
+ if (fscanf(data_file, "%u / %u / %u ",
+ &start.tm_mon, &start.tm_mday, &start.tm_year) != 3) {
+ fputs(_("FATAL ERROR in load_app: "
+ "syntax error in the item date\n"), stderr);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Read the next character : if it is an '@' then we have
+ * an appointment, else if it is an '[' we have en event.
+ */
+ c = getc(data_file);
+
+ if (c == '@')
+ is_appointment = 1;
+ else if (c == '[')
+ is_event = 1;
+ else {
+ fputs(_("FATAL ERROR in load_app: "
+ "no event nor appointment found\n"), stderr);
+ exit(EXIT_FAILURE);
+ }
+ ungetc(c, data_file);
+
+ /* Read the remaining informations. */
+ if (is_appointment) {
+ fscanf(data_file, "@ %u : %u -> %u / %u / %u @ %u : %u ",
+ &start.tm_hour, &start.tm_min,
+ &end.tm_mon, &end.tm_mday, &end.tm_year,
+ &end.tm_hour, &end.tm_min);
+ } else if (is_event) {
+ fscanf(data_file, "[%d] ", &id);
+ } else { /* NOT REACHED */
+ fputs(error, stderr);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Check if we have a recursive item. */
+ c = getc(data_file);
+
+ if (c == '{') {
+ ungetc(c, data_file);
+ is_recursive = 1;
+ fscanf(data_file, "{ %d%c ", &freq, &type);
+ /* Check if we have an endless recurrent item. */
+ c = getc(data_file);
+ if (c == '}') {
+ ungetc(c, data_file);
+ fscanf(data_file, "} ");
+ until.tm_year = 0;
+ if (is_appointment)
+ c = getc(data_file); // useless '|'
+ } else if (c == '-') {
+ ungetc(c, data_file);
+ fscanf(data_file, " -> %u / %u / %u } ",
+ &until.tm_mon, &until.tm_mday,
+ &until.tm_year);
+ if (is_appointment)
+ c = getc(data_file); // useless '|'
+ } else { /* NOT REACHED */
+ fputs(error, stderr);
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ if (is_event) // if appointment we have a useless '|'
+ ungetc(c, data_file);
+ }
+
+ /*
+ * Last: read the item description and load it into its
+ * corresponding linked list, depending on the item type.
+ */
+ if (is_appointment) {
+ if (is_recursive) {
+ recur_apoint_scan(data_file, start, end,
+ type, freq, until);
+ } else {
+ apoint_scan(data_file, start, end);
+ }
+ } else if (is_event) {
+ if (is_recursive) {
+ recur_event_scan(data_file, start, id,
+ type, freq, until);
+ } else {
+ event_scan(data_file, start, id);
+ }
+ } else { /* NOT REACHED */
+ fputs(error, stderr);
+ exit(EXIT_FAILURE);
+ }
+ }
+ fclose(data_file);
+}
+
+/* Load the todo data */
+int
+load_todo(int colr)
+{
+ FILE *data_file;
+ char *mesg_line1 = _("Failed to open todo file");
+ char *mesg_line2 = _("Press [ENTER] to continue");
+ int nb_tod = 0;
+ char buf[100], e_todo[100];
+
+ data_file = fopen(path_todo, "r");
+ if (data_file == NULL) {
+ status_mesg(mesg_line1, mesg_line2);
+ wgetch(swin);
+ }
+ for (;;) {
+ if (fgets(buf, 99, data_file) == NULL) {
+ break;
+ }
+ extract_data(e_todo, buf, strlen(buf));
+ todo_add(e_todo);
+ ++nb_tod;
+ }
+ fclose(data_file);
+ return nb_tod;
+}
+
+/* Checks if data files exist. If not, create them */
+int check_data_files()
+{
+ FILE *data_file;
+ int no_data_file;
+
+ no_data_file = 0;
+ /* Create the calcurse repertory if not present. */
+ mkdir(path_dir, 0700);
+
+ data_file = fopen(path_todo, "r");
+ if (data_file == NULL) {
+ no_data_file++;
+ data_file = fopen(path_todo, "w");
+ if (data_file == NULL) {
+ perror(path_todo);
+ return no_data_file;
+ }
+ }
+ fclose(data_file);
+
+ data_file = fopen(path_apts, "r");
+ if (data_file == NULL) {
+ no_data_file++;
+ data_file = fopen(path_apts, "w");
+ if (data_file == NULL) {
+ perror(path_apts);
+ return no_data_file;
+ }
+ }
+ fclose(data_file);
+
+ data_file = fopen(path_conf, "r");
+ if (data_file == NULL) {
+ no_data_file++;
+ data_file = fopen(path_conf, "w");
+ if (data_file == NULL) {
+ perror(path_conf);
+ return no_data_file;
+ }
+ }
+ fclose(data_file);
+ return no_data_file;
+}
+
+/* Draw the startup screen */
+void startup_screen(bool skip_dialogs, int no_data_file, int colr)
+{
+ char *welcome_mesg = _("Welcome to Calcurse. Missing data files were created.");
+ char *data_mesg = _("Data files found. Data will be loaded now.");
+ char *enter = _("Press [ENTER] to continue");
+
+ if (no_data_file != 0) {
+ status_mesg(welcome_mesg, enter);
+ wgetch(swin);
+ } else if (!skip_dialogs){
+ status_mesg(data_mesg, enter);
+ wgetch(swin);
+ }
+}
+
+/* Draw a progress bar while saving or loading data. */
+void progress_bar(bool save, int progress)
+{
+ int i, nbd = 4;
+ char *mesg_sav = _("Saving...");
+ char *mesg_load = _("Loading...");
+ char *barchar = "|";
+ char *data[4] = {
+ "[ ]",
+ "[ conf ]",
+ "[ todo ]",
+ "[ apts ]"};
+ int ipos = strlen(data[1]) + 2;
+ int epos[4];
+ int sleep_time = 125000;
+
+ /* progress bar length init. */
+ epos[0] = floor(col / nbd);
+ epos[1] = 2*epos[0];
+ epos[2] = 3*epos[0];
+ epos[3] = col - 2;
+
+ /* Display which data is being saved. */
+ if (save)
+ status_mesg(mesg_sav, data[progress]);
+ else
+ status_mesg(mesg_load, data[progress]);
+
+ /* Draw the progress bar. */
+ mvwprintw(swin, 1, ipos, barchar);
+ mvwprintw(swin, 1, epos[nbd - 1], barchar);
+ custom_apply_attr(swin, ATTR_HIGHEST);
+ for (i = ipos + 1; i < epos[progress]; i++)
+ mvwaddch(swin, 1, i, ' ' | A_REVERSE);
+ custom_remove_attr(swin, ATTR_HIGHEST);
+ wmove(swin, 0, 0);
+ wrefresh(swin);
+ usleep(sleep_time);
+}