summaryrefslogblamecommitdiffstats
path: root/src/args.c
blob: b09c7dfc36107c1ae5dfe6b24acc5ebbfeccfd09 (plain) (tree)
1
2
3
4
5
                                                                          


                                  
                                         




















                                                                       


                   
                      
                   


                 
                   

                  

                   




                 
                                 
   

            
 




                                                                                        
 




                                                           
 
                                     




                                                               

             
 
                            
                     
                                                           

                                                                               
                                 
                                                                






                                           

            
 
                            
                     





















                                                                                    


                                                                                




                                                                                 


                                                                       
 
                                 
                                                                    





                              








































                                                                              

                                                                            
   
            
                                      

                         
                      
                                       
 
                       




                                                            
                         
                                                                      




                                                                   
                 


         
                                                              
            
              
 

                                        
                                            
                          
 









                                                                     

                                                                           
                                                        
                                                              
                                                        
                                    
                                   


         

















                                                                     

                                             
                                              

                                                              
           
                                                             


                                 

                                      





                                    
                      
                                           

                             






                                                                               

                                                                      








                                                      
                                             
                                                                     

                                                                    













                                                      




                                                                   



                                                                           

                                                                

                                                                        








                                                      
                                                                   

                                                                     








                                                                    

                 
                                                      
 

                                                      











                                                                         








                                                                   

                        

                                                






                                                                   
            
                                                   

              
                   


                                       

                           
 






                                                                          

                                                
                 

                                              





                                                                          


                                       
                                               


                                                    
                                                                           

                                             

                                    



                                                                
                                                                                   
                                                                           
                        





                                                                                                                 






                                                                              
   

                                                                      
   

                                               
 







                                                                         
                                                                             







                                                                  
 
                                              
 





                                                       
                                             




                                                   
 


























                                                                              


                                  
































                                                            
 



























                                                                               
                                                      






                                                    
                                                                 



                                                               
                                                                              








                                                        
 
/*	$calcurse: args.c,v 1.29 2008/04/05 10:21:51 culot Exp $	*/

/*
 * Calcurse - text-based organizer
 * Copyright (c) 2004-2007 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 <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <getopt.h>
#include <time.h>

#include "i18n.h"
#include "custom.h"
#include "utils.h"
#include "args.h"
#include "event.h"
#include "apoint.h"
#include "day.h"
#include "todo.h"
#include "io.h"

/* 
 * Print Calcurse usage and exit.
 */
static void 
usage()
{
        char *arg_usage = 
	    _("Usage: calcurse [-h|-v] [-x] [-an] [-t[num]] [-d date|num] [-c file]\n");
	
        fputs(arg_usage, stdout);
}

static void 
usage_try()
{
        char *arg_usage_try =
	    _("Try 'calcurse -h' for more information.\n");

        fputs(arg_usage_try, stdout);
}

/*
 * Print Calcurse version with a short copyright text and exit.
 */
static void 
version_arg()
{
	char vtitle[BUFSIZ];
	char *vtext =
	    _("\nCopyright (c) 2004-2007 Frederic Culot.\n"
	    "This is free software; see the source for copying conditions.\n");

	snprintf(vtitle, BUFSIZ, 
	    _("Calcurse %s - text-based organizer\n"), VERSION);
	fputs(vtitle, stdout);
	fputs(vtext, stdout);
}

/* 
 * Print the command line options and exit.
 */
static void 
help_arg()
{
	char htitle[BUFSIZ];
	char *htext =
	_("\nMiscellaneous:\n"
	"  -h, --help\n"
	"	print this help and exit.\n"
	"\n  -v, --version\n"
	"	print calcurse version and exit.\n"
	"\nFiles:\n"
	"  -c <file>, --calendar <file>\n"
	"	specify the calendar <file> to use.\n"
	"\nNon-interactive:\n"
	"  -a, --appointment\n"
	" 	print events and appointments for current day and exit.\n"
	"\n  -d <date|num>, --day <date|num>\n"
	"	print events and appointments for <date> or <num> upcoming days and"
	"\n\texit. Possible formats are: 'mm/dd/yyyy' or 'n'.\n"
	"\n  -n, --next\n"
	"  	print next appointment within upcoming 24 hours "
	"and exit. Also given\n\tis the remaining time before this "
	"next appointment.\n"
	"\n  -t[num], --todo[=num]\n"
	"	print todo list and exit. If the optional number [num] is given,\n"
	"\tthen only todos having a priority equal to [num] will be returned.\n"
	"\tnote: priority number must be between 1 (highest) and 9 (lowest).\n"
        "\n  -N, --note\n"
        "	when used with the '-a' or '-t' flag, also print note content\n"
	"       if one is associated with the displayed item.\n"
	"\n  -x, --export\n"
	"	export user data to iCalendar format. Events, appointments and\n"
	"\ttodos are converted and echoed to stdout.\n"
	"\tnote: redirect standard output to export data to a file,\n"
	"\tby issuing a command such as: calcurse --export > my_data.ics\n"
    	"\nFor more information, type '?' from within Calcurse, "
	"or read the manpage.\n"
    	"Mail bug reports and suggestions to <calcurse@culot.org>.\n");

	snprintf(htitle, BUFSIZ, 
		_("Calcurse %s - text-based organizer\n"), VERSION);
	fputs(htitle, stdout);
        usage();
	fputs(htext, stdout);
}

/*
 * Display note contents if one is asociated with the currently displayed item
 * (to be used together with the '-a' or '-t' flag in non-interactive mode).
 * Each line begins with nbtab tabs.
 * Print "No note file found", if the notefile does not exists.
 * 
 * (patch submitted by Erik Saule).
 */
static void 
print_notefile(FILE *out, char *filename, int nbtab)
{
	char path_to_notefile[BUFSIZ];
	FILE *notefile;
	char linestarter[BUFSIZ] = "";
	char buffer[BUFSIZ];
	int i;
	int printlinestarter = 1;

	for (i = 0; i < nbtab; i++)
		strcat(linestarter, "\t");
 
	snprintf(path_to_notefile, BUFSIZ, "%s/%s", path_notes, filename);
	notefile = fopen(path_to_notefile, "r");
	if (notefile) {
		while (fgets(buffer, BUFSIZ, notefile) != NULL) {
			if (printlinestarter) {
				fputs(linestarter,out);
				printlinestarter = 0;
			}
			fputs(buffer, out);
			if (buffer[strlen(buffer) - 1] == '\n')
				printlinestarter = 1;
		}
		fputs("\n", out);
		fclose(notefile);
	} else {
		fputs(linestarter, out);
		fputs(_("No note file found\n"), out);
	}
}

/*
 * Print todo list and exit. If a priority number is given (say not equal to
 * zero), then only todo items that have this priority will be displayed.
 */
static void 
todo_arg(int priority, int print_note)
{
	struct todo_s *i;
	int title = 1;
	char priority_str[BUFSIZ] = "";

	io_load_todo();
	for (i = todolist; i != 0; i = i->next) {
		if (priority == 0 || i->id == priority) {
			if (title) {
				fputs(_("to do:\n"),stdout);
				title = 0;
			}
			snprintf(priority_str, BUFSIZ, "%d. ", i->id);
			fputs(priority_str, stdout);
			fputs(i->mesg, stdout);
			fputs("\n", stdout);
			if (print_note && i->note)
				print_notefile(stdout, i->note, 1);
		}
	}
}

/* Print the next appointment within the upcoming 24 hours. */
static void 
next_arg(void)
{
	struct notify_app_s next_app;
	const long current_time = now();
	int time_left, hours_left, min_left;
	char mesg[BUFSIZ];

	next_app.time = current_time + DAYINSEC;
	next_app.got_app = 0;
	next_app.txt = NULL;

	next_app = *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;
		hours_left = (time_left / HOURINSEC);
		min_left = (time_left - hours_left * HOURINSEC) / MININSEC;
		fputs(_("next appointment:\n"), stdout);
		snprintf(mesg, BUFSIZ, "   [%02d:%02d] %s\n", 
		    hours_left, min_left, next_app.txt);
		fputs(mesg, stdout);
		free(next_app.txt);
	}
}

/* 
 * Print the date on stdout.
 */
static void 
arg_print_date(long date) 
{
		char date_str[BUFSIZ];
		time_t t;
		struct tm *lt;

		t = date;
		lt = localtime(&t);
		snprintf(date_str, BUFSIZ, "%02u/%02u/%04u",
			lt->tm_mon+1, lt->tm_mday, 1900+lt->tm_year);
		fputs(date_str,stdout);
		fputs(":\n",stdout);
}

/*
 * Print appointments for given day and exit.
 * If no day is given, the given date is used.
 * If there is also no date given, current date is considered.
 */
static int 
app_arg(int add_line, date_t *day, long date, int print_note)
{
	struct recur_event_s *re;
	struct event_s *j;
	recur_apoint_llist_node_t *ra;
	apoint_llist_node_t *i;
	long today;
	bool print_date = true;
	int  app_found = 0;
	char apoint_start_time[100];
	char apoint_end_time[100];
	
	if (date == 0)
		today = get_sec_date(*day);
	else 
		today = date;

	/* 
	 * Calculate and print the selected date if there is an event for
 	 * that date and it is the first one, and then print all the events for
 	 * that date. 
	 */
	for (re = recur_elist; re != 0; re = re->next) {
		if (recur_item_inday(re->day, re->exc, re->rpt->type, 
			re->rpt->freq, re->rpt->until, today)) {
			app_found = 1;
			if (add_line) {
				fputs("\n", stdout);
				add_line = 0;
			}
			if (print_date) {
				arg_print_date(today);
				print_date = false;
			}
			fputs(" * ", stdout);
			fputs(re->mesg, stdout); fputs("\n", stdout);
			if (print_note && re->note)
				print_notefile(stdout, re->note, 2);
		}
	}

	for (j = eventlist; j != 0; j = j->next) {
		if (event_inday(j, today)) {
			app_found = 1;
			if (add_line) {
				fputs("\n",stdout);
				add_line = 0;
			}
			if (print_date) {
				arg_print_date(today);
				print_date = false;
			}
			fputs(" * ", stdout);
			fputs(j->mesg, stdout); 
			fputs("\n", stdout);
			if (print_note && j->note)
				print_notefile(stdout, j->note, 2);
		}	
	}

 	 /* Same process is performed but this time on the appointments. */
	pthread_mutex_lock(&(recur_alist_p->mutex));
	for (ra = recur_alist_p->root; ra != 0; ra = ra->next) {
		if (recur_item_inday(ra->start, ra->exc, ra->rpt->type, 
			ra->rpt->freq, ra->rpt->until, today)) {
			app_found = 1;
			if (add_line) {
				fputs("\n",stdout);
				add_line = 0;
			}
			if (print_date) {
				arg_print_date(today);
				print_date = false;
			}
			apoint_sec2str(apoint_recur_s2apoint_s(ra),
				RECUR_APPT, today, apoint_start_time,
				apoint_end_time);
			fputs(" - ", stdout);
			fputs(apoint_start_time, stdout);
			fputs(" -> ", stdout);
			fputs(apoint_end_time, stdout); 
			fputs("\n\t", stdout);
			fputs(ra->mesg, stdout); 
			fputs("\n", stdout);
			if (print_note && ra->note)
				print_notefile(stdout, ra->note, 2);
		}
	}
	pthread_mutex_unlock(&(recur_alist_p->mutex));

	pthread_mutex_lock(&(alist_p->mutex));
	for (i = alist_p->root; i != 0; i = i->next) {
		if (apoint_inday(i, today)) {
			app_found = 1;
			if (add_line) {
				fputs("\n",stdout);
				add_line = 0;
			}
			if (print_date) {
				arg_print_date(today);
				print_date = false;
			}
			apoint_sec2str(i, APPT, today, apoint_start_time,
					apoint_end_time);
			fputs(" - ", stdout);
			fputs(apoint_start_time, stdout);
			fputs(" -> ", stdout);
			fputs(apoint_end_time, stdout); 
			fputs("\n\t", stdout);
			fputs(i->mesg, stdout); 
			fputs("\n", stdout);
			if (print_note && i->note)
				print_notefile(stdout, i->note, 2);
		}	
	}
	pthread_mutex_unlock(&(alist_p->mutex));

	return app_found;
}

/*
 * Print appointment for the given date or for the given n upcoming
 * days.
 */
static void 
date_arg(char *ddate, int add_line, int print_note)
{
	int i;
	date_t day;
	int numdays = 0, num_digit = 0;
	int arg_len = 0, app_found = 0;
	int date_valid = 0;
	static struct tm t;
	time_t timer;

	/* 
	 * Check (with the argument length) if a date or a number of days 
	 * was entered, and then call app_arg() to print appointments
	 */
	arg_len = strlen(ddate);
	if (arg_len <= 4) { 		/* a number of days was entered */
		for (i = 0; i <= arg_len-1; i++) { 
			if (isdigit(ddate[i])) 
				num_digit++;	
		}
		if (num_digit == arg_len) 
			numdays = atoi(ddate);

		/* 
		 * Get current date, and print appointments for each day
 		 * in the chosen interval. app_found and add_line are used
 		 * to format the output correctly. 
		 */
		timer = time(NULL);
		t = *localtime(&timer);

		for (i = 0; i < numdays; i++) {
			day.dd = t.tm_mday;
			day.mm = t.tm_mon + 1;
			day.yyyy = t.tm_year + 1900;
			app_found = app_arg(add_line, &day, 0, print_note);
			if (app_found) 
				add_line = 1;
			t.tm_mday++;
			mktime(&t);
		}
	} else {			/* a date was entered */
		date_valid = check_date(ddate);
		if (date_valid) {
			sscanf(ddate, "%d / %d / %d", &day.mm, &day.dd, &day.yyyy);
			app_found = app_arg(add_line, &day, 0, print_note);
		} else {
			fputs(_("Argument to the '-d' flag is not valid\n"),
			    stdout);
			fputs(_("Possible argument formats are : 'mm/dd/yyyy' or 'n'\n"),
			    stdout);
			fputs(_("\nFor more information, type '?' from within Calcurse, or read the manpage.\n"),
			    stdout);
			fputs
	    (_("Mail bug reports and suggestions to <calcurse@culot.org>.\n"),
	     stdout);
		}
	}
}

/* 
 * Parse the command-line arguments and call the appropriate
 * routines to handle those arguments. Also initialize the data paths.
 */
int 
parse_args(int argc, char **argv, conf_t *conf)
{
	int ch, add_line = 0;
	int unknown_flag = 0, app_found = 0;
	/* Command-line flags */
	int aflag = 0;	/* -a: print appointments for current day */
	int cflag = 0;	/* -c: specify the calendar file to use */
	int dflag = 0;	/* -d: print appointments for a specified days */
	int hflag = 0;	/* -h: print help text */
	int nflag = 0;	/* -n: print next appointment */
	int Nflag = 0;  /* -N: also print note content with apps and todos */
	int tflag = 0;	/* -t: print todo list */
	int vflag = 0;	/* -v: print version number */
	int xflag = 0;  /* -x: export data to iCalendar format */
  
	int tnum = 0;
	int non_interactive = 0, multiple_flag = 0, load_data = 0;
	int no_file = 1;
	char *ddate = "", *cfile = NULL;

	static char *optstr = "hvnNaxt::d:c:";

	struct option longopts[] = {
	    {"appointment", no_argument, NULL, 'a'},
	    {"calendar", required_argument, NULL, 'c'},
	    {"day", required_argument, NULL, 'd'},
	    {"help", no_argument, NULL, 'h'},
	    {"next", no_argument, NULL, 'n'},
	    {"note", no_argument, NULL, 'N'},
	    {"todo", optional_argument, NULL, 't'},
	    {"version", no_argument, NULL, 'v'},
	    {"export", no_argument, NULL, 'x'},	
	    {NULL, no_argument, NULL, 0}
	};

	while ((ch = getopt_long(argc, argv, optstr, longopts, NULL)) != -1) {
		switch (ch) {
		case 'a':
			aflag = 1;
			multiple_flag++;
			load_data++;
			break;
		case 'c':
			cflag = 1;
			multiple_flag++;
			load_data++;
			cfile = optarg;
			break;
		case 'd':
			dflag = 1;
			multiple_flag++;
			load_data++;
			ddate = optarg;
			break;
		case 'h':
			hflag = 1;
			break;
		case 'n':
			nflag = 1;
			multiple_flag++;
			load_data++;
			break;
		case 'N':
			Nflag = 1;
			break;
		case 't':
			tflag = 1;
			multiple_flag++;
			load_data++;
			add_line = 1;
			if (optarg != NULL) {
				tnum = atoi(optarg);
				if (tnum < 1 || tnum > 9) {
					usage();
					usage_try();
					return EXIT_FAILURE;
				}
			} else
				tnum = 0;
			break;
		case 'v':
			vflag = 1;
			break;
		case 'x':
			xflag = 1;
			multiple_flag++;
			load_data++;
			break;
		default:
			usage();
                        usage_try();
			unknown_flag = 1;
			non_interactive = 1;
			/* NOTREACHED */
		}
	}
	argc -= optind;
	argv += optind;

	if (argc >= 1) {	/* incorrect arguments */
		usage();
                usage_try();
		return EXIT_FAILURE;
	} else {
		if (unknown_flag) {
			non_interactive = 1;
		} else if (hflag) {
			help_arg();
			non_interactive = 1;
		} else if (vflag) {
			version_arg();
			non_interactive = 1;
		} else if (multiple_flag) {
			if (load_data) {
				io_init(cfile);
				no_file = io_check_data_files();
				if (dflag || aflag || nflag || xflag)  
					io_load_app();
			}
			if (xflag) {
				notify_init_vars();
				custom_load_conf(conf, 0);
				io_export_data(IO_EXPORT_NONINTERACTIVE, conf);
				non_interactive = 1;
				return (non_interactive);
			}
			if (tflag) {
				todo_arg(tnum, Nflag);
				non_interactive = 1;
			}
			if (nflag) {
				next_arg();
				non_interactive = 1;
			}
			if (dflag) {
				date_arg(ddate, add_line, Nflag);
				non_interactive = 1;
			} else if (aflag) {
				date_t day;
				day.dd = day.mm = day.yyyy = 0;
				app_found = app_arg(add_line, &day, 0, Nflag);
				non_interactive = 1;
			}
		} else {
			non_interactive = 0;
			io_init(cfile);
			no_file = io_check_data_files();
		}
		return (non_interactive);
	}
}