commit 5dfa1d2816b27894217c32e457d22efc00b42304
parent 1ba96a674a563adcf3bd5320be7fbe8860f2337c
Author: Andreas Gruhler <agruhl@gmx.ch>
Date: Wed, 24 Feb 2021 07:38:20 +0100
Merge pull request #61 from in0rdr/feature/config-file
Add configuration file
Diffstat:
M | README.md | | | 59 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- |
M | diary.1 | | | 163 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- |
M | diary.c | | | 289 | ++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------- |
A | diary.cfg | | | 11 | +++++++++++ |
M | diary.h | | | 26 | +++++++++++++++++++++++++- |
5 files changed, 424 insertions(+), 124 deletions(-)
diff --git a/README.md b/README.md
@@ -17,7 +17,7 @@ This is a text based diary, inspired by [khal](https://github.com/pimutils/khal)
Instead of this, you can also set the environment variable `DIARY_DIR`
to the desired directory. If both an argument and the environment
- variable are given, the argument takes precedence.
+ variable are given, the argument takes precedence (see [Variable Precedence Rules](#precedence_rules)).
The text files in this folder will be named 'yyyy-mm-dd'.
@@ -70,3 +70,60 @@ Note: for *BSD users run gmake.
By default this will copy the binary to /usr/local/bin. To use a different
path prefix, type `sudo make PREFIX=/usr install` to use /usr/bin for example.
You can uninstall diary with `sudo make uninstall`.
+
+## Configuration File
+
+The [`diary.cfg`](./diary.cfg) configuration file can optionally be used to persist diary configuration. To install the sample from this repository:
+```bash
+mkdir -p ${XDG_CONFIG_HOME:-~/.config}/diary
+cp diary.cfg ${XDG_CONFIG_HOME:-~/.config}/diary/
+```
+
+The file `${XDG_CONFIG_HOME:-~/.config}/diary/diary.cfg` should adhere to a basic `key = value` format. Lines can be commented with the special characters `#` or `;`. The following configuration keys are currently supported:
+
+| Command Line Option | Config Key | Example Config Value | Default Config Value | Description |
+| --- | --- | --- | --- | --- |
+| `--dir`, `-d`, or first non-option argument | `dir` | ~/diary | n/a | Diary directory. Path that holds the journal text files. If unset, defaults to environment variable `$DIARY_DIR`.|
+| `--editor` or `-e` | `editor` | "vim" | "" | Editor to open journal files with. If unset, defaults to environment variable `$EDITOR`. If no editor is provided, the diary is opened read-only. |
+| `--fmt` or `-f` | `fmt` | %d_%b_%y | %Y-%m-%d | Date format and file name for the files inside the `dir`. For the format specifiers, see [`man strftime`](https://man7.org/linux/man-pages/man3/strftime.3.html). Be careful: If you change this, you might no longer find your existing diary entries, because the diary assumes to find the journal files under another file name. Hence, a change in FMT shows an empty diary, at first. Rename all files in the DIARY_DIR to migrate to a new FMT. |
+| `--range` or `-r` | `range` | 10 | 1 | Number of years to show before/after todays date |
+| `--weekday` or `-w` | `weekday` | 0 | 1 | First weekday, `7` = Sunday, `1` = Monday, ..., `6` = Saturday. Use `7` (or `0`) to display week beginning at Sunday ("S-M-T-W-T-F-S"), or `1` for "M-T-W-T-F-S-S". If `glibc` is installed, the first day of the week is derived from the current locale setting (`$LANG`, see `man locale`). Without `glibc`, the first weekday defaults to 1 (Monday), unless specified otherwise with this option. |
+
+## Precedence Rules
+<a name="precedence_rules"></a>
+
+The default variables, for instance, for the configuration variables `editor`, `dir` and `weekday`, are populated with values in the following order, where earlier entries are overwritten by subsequent ones if they exist:
+1. No default for `DIARY_DIR`. Defaults for `range`, `weekday`, `fmt` and `editor` are provided in [diary.h](diary.h)
+* If `EDITOR` is unset and no editor is provided in the config file or via the `-e` option, the diary works read-only. Journal files cannot be opened.
+* If `DIARY_DIR` is not provided, the diary won't open.
+2. **Config file** (empty default for `editor`, no default for `dir`)
+3. **Environment** variables `$DIARY_DIR`, `$EDITOR` and `$LANG` for locale (`weekday`)
+4. **Option arguments** `-d` / `-e` / `-w`
+5. First non-option argument is interpreted as `DIARY_DIR`
+
+### Precedence Example: Locale and First Day of Week
+
+If glibc is installed, the first weekday defaults to the locale defined in the current shell environment (`$LANG`, see `man locale`), unless specified otherwise via the `--weekday` / `-w` command line options. For example:
+
+```bash
+# start with weekday=3(Wed), overrule any other configuration value
+$ diary -w3
+
+# start with glibc derived weekday=1, regardless of 'weekday' in config file
+$ LANG=de_CH diary
+
+# if glibc is installed, start with glibc derived base date (weekday=0)
+$ LANG= diary
+
+# disable environment variable, default to value from config file
+$ unset LANG
+
+# start with 'weekday' default from config file, if available
+$ diary
+
+# remove config file
+$ rm ${XDG_CONFIG_HOME:-~/.config}/diary/diary.cfg
+
+# start with 'weekday' default value from source code (1=Mon)
+$ diary
+```
diff --git a/diary.1 b/diary.1
@@ -11,23 +11,36 @@ diary \- Simple text-based diary program
.B diary
is a simple text-based program for managing journal entries.
-.SH ENVIRONMENT
-
-.IP DIARY_DIR
-If this variable is set to a directory that can be opened,
-.B diary
-will use it to store diary files. Diary files are simple text files named
-after their date, formatted like YYYY-MM-DD. All other files are ignored.
-
-.IP EDITOR
-The program used to edit journal entries.
-
-
-.SH ARGUMENTS
-
-If the argument \fIDIRECTORY\fR is given, diary files are read from and
-stored to that directory, ignoring the DIARY_DIR environment variable or
-any '-d' or '--dir' options.
+.SH OPTIONS
+.TP
+\fB\-v\fR, \fB\-\-version\fR
+Print diary version
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Show diary help text
+.TP
+\fB\-d\fR, \fB\-\-dir\fR=\fI\,DIARY_DIR\/\fR
+Diary storage directory DIARY_DIR
+.TP
+\fB\-e\fR, \fB\-\-editor\fR=\fI\,EDITOR\/\fR
+EDITOR is the text editor used for opening the journal files.
+.TP
+\fB\-f\fR, \fB\-\-fmt\fR=\fI\,FMT\/\fR
+FMT is a custom date and file format. Change with care, because the diary
+reads and writes to files with file name FMT. The new FMT is only
+applied to newly saved entries. Existing entries with the old FMT are not
+automatically migrated to the new FMT and do not show up with a new FMT
+specifier. Consequently, a change in FMT shows an empty diary at first.
+Rename all files in the DIARY_DIR to migrate to a new FMT.
+.TP
+\fB\-r\fR, \fB\-\-range\fR=\fI\,RANGE\/\fR
+RANGE is the number of years to show before/after todays date. Defaults to 1 year.
+.TP
+\fB\-f\fR, \fB\-\-weekday\fR=\fI\,DAY\/\fR
+First day of the week. DAY is an integer in range (0..6), interpreted as 0 or 7 = Sun,
+1 = Mon, ..., 6 = Sat. If glibc is installed, the first day of the week is derived
+from the current locale setting ('$LANG', see man locale). Without glibc, the
+first weekday defaults to 1 (Monday), unless specified otherwise with this option.
.SH NAVIGATION
Navigation is done using the following vim-inspired keyboard shortcuts:
@@ -57,6 +70,122 @@ t | jump to today
s | jump to specific day
.TE
+.SH ENVIRONMENT
+
+.IP DIARY_DIR
+If this variable is set to a directory that can be opened,
+.B diary
+will use it to store diary files. Diary files are simple text files named
+after their date, formatted according to FMT (see '-f'/'--fmt' options and
+'fmt' config key). The format defaults to "%Y-%m-%d", which is "YYYY-MM-DD"
+(see man strftime). All other files different from FMT are ignored.
+
+.IP EDITOR
+The program used to edit journal entries.
+
+.IP LANG
+The default locale used to display the first day of the week.
+
+.SH ARGUMENTS
+
+If the argument \fIDIRECTORY\fR is given, diary files are read from and
+stored to that directory, ignoring the DIARY_DIR environment variable,
+any '-d'/'--dir' options or the 'dir' value set in the config file.
+
+.SH FILES
+.TP
+.I ${PREFIX:-/usr/local/bin}/diary
+The diary binary
+.TP
+.I ${XDG_CONFIG_HOME:-~/.config}/diary/diary.cfg
+An optional diary configuration file
+
+.SH CONFIGURATION FILE
+The diary.cfg configuration file can optionally be used to persist diary configuration.
+
+Create default config location:
+
+.nf
+ $ mkdir -p ${XDG_CONFIG_HOME:-~/.config}/diary
+.fi
+
+Install an example config file with defaults:
+
+.nf
+ $ echo '# Path that holds the journal text files
+ #dir = ~/diary
+ # Number of years to show before/after todays date
+ range = 1
+ # 0 = Sunday, 1 = Monday, ..., 6 = Saturday
+ weekday = 1
+ # Date and file format, change with care
+ fmt = %Y-%m-%d
+ # Editor to open journal files with
+ editor = ""' | sed 's/^[[:space:]]*//' > ${XDG_CONFIG_HOME:-~/.config}/diary/diary.cfg
+.fi
+
+Use the '#' or ';' characters to comment lines.
+
+.SH PRECEDENCE RULES
+
+The default variables, for instance, for the configuration variables 'editor', 'dir' and 'weekday', are populated with values in the following order:
+
+.TP
+1.
+No default for 'DIARY_DIR'. Defaults for 'range', 'weekday', 'fmt' and 'editor' are provided in 'diary.h'. If 'EDITOR' is unset and no editor is provided in the config file or via the '-e' option, the
+.B
+diary
+works read-only. Journal files cannot be opened. If 'DIARY_DIR' is not provided, the
+.B
+diary
+won't open.
+.TP
+2.
+.B
+Config file
+(empty default for 'editor', no default for 'dir')
+.TP
+3.
+.B
+Environment
+variables '$DIARY_DIR', '$EDITOR' and '$LANG' for locale ('weekday')
+.TP
+4.
+.B
+Option
+arguments, see section
+.B
+OPTIONS
+.TP
+5.
+First non-option argument \fIDIRECTORY\fR is interpreted as 'DIARY_DIR'
+
+.SH PRECEDENCE EXAMPLE: LOCALE AND FIRST DAY OF WEEK
+If glibc is installed, the first weekday defaults to the locale defined in the current shell
+environment ($LANG, see man locale), unless specified otherwise via the '--weekday'/'-w'.
+
+.nf
+ # start with weekday=3(Wed), overrule any other configuration value
+ $ diary -w3
+
+ # start with glibc derived weekday=1, regardless of 'weekday' in config file
+ $ LANG=de_CH diary
+
+ # if glibc is installed, start with glibc derived base date (weekday=0)
+ $ LANG= diary
+
+ # disable environment variable, default to value from config file
+ $ unset LANG
+
+ # start with 'weekday' default from config file, if available
+ $ diary
+
+ # remove config file
+ $ rm ${XDG_CONFIG_HOME:-~/.config}/diary/diary.cfg
+
+ # start with 'weekday' default value from source code (1=Mon)
+ $ diary
+.fi
.SH DEVELOPMENT
All source code is available in this github repository:
diff --git a/diary.c b/diary.c
@@ -6,10 +6,6 @@ struct tm today;
struct tm curs_date;
struct tm cal_start;
struct tm cal_end;
-// 0 = Sunday, 1 = Monday, ..., 6 = Saturday
-int first_weekday = 1;
-
-#define DATE_FMT "%Y-%m-%d"
// normally leap is every 4 years,
// but is skipped every 100 years,
@@ -23,21 +19,21 @@ void setup_cal_timeframe()
curs_date = today;
cal_start = today;
- cal_start.tm_year -= YEAR_RANGE;
+ cal_start.tm_year -= CONFIG.range;
cal_start.tm_mon = 0;
cal_start.tm_mday = 1;
mktime(&cal_start);
- if (cal_start.tm_wday != first_weekday) {
- // adjust start date to first_weekday before 01.01
+ if (cal_start.tm_wday != CONFIG.weekday) {
+ // adjust start date to weekday before 01.01
cal_start.tm_year--;
cal_start.tm_mon = 11;
- cal_start.tm_mday = 31 - (cal_start.tm_wday - first_weekday) + 1;
+ cal_start.tm_mday = 31 - (cal_start.tm_wday - CONFIG.weekday) + 1;
mktime(&cal_start);
}
cal_end = today;
- cal_end.tm_year += YEAR_RANGE;
+ cal_end.tm_year += CONFIG.range;
cal_end.tm_mon = 11;
cal_end.tm_mday = 31;
mktime(&cal_end);
@@ -46,7 +42,7 @@ void setup_cal_timeframe()
void draw_wdays(WINDOW* head)
{
int i;
- for (i = first_weekday; i < first_weekday + 7; i++) {
+ for (i = CONFIG.weekday; i < CONFIG.weekday + 7; i++) {
waddstr(head, WEEKDAYS[i % 7]);
waddch(head, ' ');
}
@@ -163,21 +159,13 @@ void display_entry(const char* dir, size_t dir_size, const struct tm* date, WIND
/* Writes edit command for 'date' entry to 'rcmd'. '*rcmd' is NULL on error. */
void edit_cmd(const char* dir, size_t dir_size, const struct tm* date, char** rcmd, size_t rcmd_size)
{
- // get editor from environment
- char* editor = getenv("EDITOR");
- if (editor == NULL) {
- fprintf(stderr, "'EDITOR' environment variable not set");
- *rcmd = NULL;
- return;
- }
-
// set the edit command to env editor
- if (strlen(editor) + 2 > rcmd_size) {
+ if (strlen(CONFIG.editor) + 2 > rcmd_size) {
fprintf(stderr, "Binary path of default editor too long");
*rcmd = NULL;
return;
}
- strcpy(*rcmd, editor);
+ strcpy(*rcmd, CONFIG.editor);
strcat(*rcmd, " ");
// get path of entry
@@ -217,7 +205,7 @@ bool date_has_entry(const char* dir, size_t dir_size, const struct tm* i)
void get_date_str(const struct tm* date, char* date_str, size_t date_str_size)
{
- strftime(date_str, date_str_size, DATE_FMT, date);
+ strftime(date_str, date_str_size, CONFIG.fmt, date);
}
/* Writes file path for 'date' entry to 'rpath'. '*rpath' is NULL on error. */
@@ -287,16 +275,60 @@ struct tm find_closest_entry(const struct tm current,
return current;
}
-/* Set the diary storage directory.
-* Copies the path to the storage directory from character
-* string `path` to the destination location `pdiary_dir`
-*/
-bool set_diary_dir(char* path, char* pdiary_dir, size_t diary_dir_size) {
- if (strlen(path) + 1 > diary_dir_size) {
- fprintf(stderr, "Diary directory path too long\n");
+bool read_config(const char* file_path)
+{
+ wordexp_t config_file_path_wordexp;
+ char config_file_path[256];
+
+ if ( wordexp( file_path, &config_file_path_wordexp, 0 ) == 0) {
+ if (strlen(config_file_path_wordexp.we_wordv[0]) + 1 > sizeof config_file_path) {
+ fprintf(stderr, "Config file path '%s' too long\n", config_file_path_wordexp.we_wordv[0]);
+ return false;
+ }
+ strcpy(config_file_path, config_file_path_wordexp.we_wordv[0]);
+ }
+ wordfree(&config_file_path_wordexp);
+
+ // check if config file is readable
+ if( access( config_file_path, R_OK ) != 0 ) {
+ fprintf(stderr, "Config file '%s' not readable, skipping\n", config_file_path);
return false;
}
- strcpy(pdiary_dir, path);
+
+ char key_buf[80];
+ char value_buf[80];
+ char line[256];
+ FILE * pfile;
+
+ // read config file line by line
+ pfile = fopen(config_file_path, "r");
+ while (fgets(line, sizeof line, pfile)) {
+ // ignore comment lines
+ if (*line == '#' || *line == ';') continue;
+
+ if (sscanf(line, "%s = %s", key_buf, value_buf) == 2) {
+ if (strcmp("dir", key_buf) == 0) {
+ wordexp_t diary_dir_wordexp;
+ if ( wordexp( value_buf, &diary_dir_wordexp, 0 ) == 0) {
+ // set expanded diary directory path from config file
+ CONFIG.dir = (char *) calloc(strlen(diary_dir_wordexp.we_wordv[0]) + 1, sizeof(char));
+ strcpy(CONFIG.dir, diary_dir_wordexp.we_wordv[0]);
+ }
+ wordfree(&diary_dir_wordexp);
+ } else if (strcmp("range", key_buf) == 0) {
+ CONFIG.range = atoi(value_buf);
+ } else if (strcmp("weekday", key_buf) == 0) {
+ CONFIG.weekday = atoi(value_buf);
+ } else if (strcmp("fmt", key_buf) == 0) {
+ CONFIG.fmt = (char *) malloc(strlen(value_buf) + 1 * sizeof(char));
+ strcpy(CONFIG.fmt, value_buf);
+ } else if (strcmp("editor", key_buf) == 0) {
+ CONFIG.editor = (char *) malloc(strlen(value_buf) + 1 * sizeof(char));
+ strcpy(CONFIG.editor, value_buf);
+ }
+ }
+ }
+ fclose (pfile);
return true;
}
@@ -307,52 +339,113 @@ void usage() {
printf("Edit journal entries from the command line\n");
printf("\n");
printf("Options:\n");
- printf(" -v, --version : Print diary version\n");
- printf(" -h, --help : Show diary help text\n");
- printf(" -d, --dir DIARY_DIR : Diary storage directory (DIARY_DIR)\n");
+ printf(" -v, --version : Print diary version\n");
+ printf(" -h, --help : Show diary help text\n");
+ printf(" -d, --dir DIARY_DIR : Diary storage directory DIARY_DIR\n");
+ printf(" -e, --editor EDITOR : Editor to open journal files with\n");
+ printf(" -f, --fmt FMT : Date and file format, change with care\n");
+ printf(" -r, --range RANGE : RANGE is the number of years to show before/after today's date\n");
+ printf(" -w, --weekday DAY : First day of the week, 0 = Sun, 1 = Mon, ..., 6 = Sat\n");
printf("\n");
- printf("Full docs and keyboard shortcuts: DIARY(1)\n");
+ printf("Full docs and keyboard shortcuts: 'man diary'\n");
printf("or online via: <https://github.com/in0rdr/diary>\n");
}
int main(int argc, char** argv) {
setlocale(LC_ALL, "");
- char diary_dir[80];
char* env_var;
+ char* config_home;
+ char* config_file_path;
chtype atrs;
- // get the diary directory via environment variable or argument
- // if both are given, the argument takes precedence
- if (argc < 2) {
- // the diary directory is not specified via command line argument
- // use the environment variable if available
- env_var = getenv("DIARY_DIR");
- if (env_var == NULL) {
+ // the diary directory defaults to the diary_dir specified in the config file
+ config_home = getenv("XDG_CONFIG_HOME");
+ if (config_home == NULL) config_home = XDG_CONFIG_HOME_FALLBACK;
+ // concat config home with the file path to the config file
+ config_file_path = (char *) calloc(strlen(config_home) + strlen(CONFIG_FILE_PATH) + 1, sizeof(char));
+ sprintf(config_file_path, "%s/%s", config_home, CONFIG_FILE_PATH);
+ // read config from config file path
+ read_config(config_file_path);
+
+ // get diary directory from environment
+ env_var = getenv("DIARY_DIR");
+ if (env_var != NULL) {
+ // if available, overwrite the diary directory with the environment variable
+ CONFIG.dir = (char *) calloc(strlen(env_var) + 1, sizeof(char));
+ strcpy(CONFIG.dir, env_var);
+ }
+
+ // get editor from environment
+ env_var = getenv("EDITOR");
+ if (env_var != NULL) {
+ // if available, overwrite the editor with the environments EDITOR
+ CONFIG.editor = (char *) calloc(strlen(env_var) + 1, sizeof(char));
+ strcpy(CONFIG.editor, env_var);
+ }
+
+ // get locale from environment variable LANG
+ // https://www.gnu.org/software/gettext/manual/html_node/Locale-Environment-Variables.html
+ env_var = getenv("LANG");
+ if (env_var != NULL) {
+ // if available, overwrite the editor with the environments locale
+ #ifdef __GNU_LIBRARY__
+ // references: locale(5) and util-linux's cal.c
+ // get the base date, 8-digit integer (YYYYMMDD) returned as char *
+ #ifdef _NL_TIME_WEEK_1STDAY
+ unsigned long d = (uintptr_t) nl_langinfo(_NL_TIME_WEEK_1STDAY);
+ // reference: https://sourceware.org/glibc/wiki/Locales
+ // assign a static date value 19971130 (a Sunday)
+ #else
+ unsigned long d = 19971130;
+ #endif
+ struct tm base = {
+ .tm_sec = 0,
+ .tm_min = 0,
+ .tm_hour = 0,
+ .tm_mday = d % 100,
+ .tm_mon = (d / 100) % 100 - 1,
+ .tm_year = d / (100 * 100) - 1900
+ };
+ mktime(&base);
+ // weekday is base date's day of the week offset by (_NL_TIME_FIRST_WEEKDAY - 1)
+ #ifdef __linux__
+ CONFIG.weekday = (base.tm_wday + *nl_langinfo(_NL_TIME_FIRST_WEEKDAY) - 1) % 7;
+ #elif defined __MACH__
+ CFIndex first_day_of_week;
+ CFCalendarRef currentCalendar = CFCalendarCopyCurrent();
+ first_day_of_week = CFCalendarGetFirstWeekday(currentCalendar);
+ CFRelease(currentCalendar);
+ CONFIG.weekday = (base.tm_wday + first_day_of_week - 1) % 7;
+ #endif
+ #endif
+ }
+ // get the diary directory via argument, this takes precedence over env/config
+ if (argc < 2) {
+ if (CONFIG.dir == NULL) {
fprintf(stderr, "The diary directory must be provided as (non-option) arg, `--dir` arg,\n"
"or in the DIARY_DIR environment variable, see `diary --help` or DIARY(1)\n");
return 1;
}
-
- // set diary directory from environment variable
- if ( !set_diary_dir(env_var, diary_dir, sizeof diary_dir) ) {
- return 1;
- }
} else {
int option_char;
int option_index = 0;
// define options, see GETOPT(3)
static const struct option long_options[] = {
- { "version", no_argument, 0, 'v' },
- { "help", no_argument, 0, 'h' },
- { "dir", required_argument, 0, 'd' },
- { 0, 0, 0, 0 }
+ { "version", no_argument, 0, 'v' },
+ { "help", no_argument, 0, 'h' },
+ { "dir", required_argument, 0, 'd' },
+ { "editor", required_argument, 0, 'e' },
+ { "fmt", required_argument, 0, 'f' },
+ { "range", required_argument, 0, 'r' },
+ { "weekday", required_argument, 0, 'w' },
+ { 0, 0, 0, 0 }
};
// read option characters
while (1) {
- option_char = getopt_long(argc, argv, "vhd:", long_options, &option_index);
+ option_char = getopt_long(argc, argv, "vhd:r:w:f:e:", long_options, &option_index);
if (option_char == -1) {
break;
@@ -372,9 +465,27 @@ int main(int argc, char** argv) {
break;
case 'd':
// set diary directory from option character
- if ( !set_diary_dir(optarg, diary_dir, sizeof diary_dir) ) {
- return 1;
- }
+ CONFIG.dir = (char *) calloc(strlen(optarg) + 1, sizeof(char));
+ strcpy(CONFIG.dir, optarg);
+ break;
+ case 'r':
+ // set year range from option character
+ CONFIG.range = atoi(optarg);
+ break;
+ case 'w':
+ // set first week day from option character
+ fprintf(stderr, "%i\n", atoi(optarg));
+ CONFIG.weekday = atoi(optarg);
+ break;
+ case 'f':
+ // set date format from option character
+ CONFIG.fmt = (char *) calloc(strlen(optarg) + 1, sizeof(char));
+ strcpy(CONFIG.fmt, optarg);
+ break;
+ case 'e':
+ // set default editor from option character
+ CONFIG.editor = (char *) calloc(strlen(optarg) + 1, sizeof(char));
+ strcpy(CONFIG.editor, optarg);
break;
default:
printf("?? getopt returned character code 0%o ??\n", option_char);
@@ -384,56 +495,24 @@ int main(int argc, char** argv) {
if (optind < argc) {
// set diary directory from first non-option argv-element,
// required for backwarad compatibility with diary <= 0.4
- if ( !set_diary_dir(argv[optind], diary_dir, sizeof diary_dir) ) {
- return 1;
- }
+ CONFIG.dir = (char *) calloc(strlen(argv[optind]) + 1, sizeof(char));
+ strcpy(CONFIG.dir, argv[optind]);
}
}
// check if that directory exists
- DIR* diary_dir_ptr = opendir(diary_dir);
+ DIR* diary_dir_ptr = opendir(CONFIG.dir);
if (diary_dir_ptr) {
// directory exists, continue
closedir(diary_dir_ptr);
} else if (errno == ENOENT) {
- fprintf(stderr, "The directory '%s' does not exist\n", diary_dir);
+ fprintf(stderr, "The directory '%s' does not exist\n", CONFIG.dir);
return 2;
} else {
- fprintf(stderr, "The directory '%s' could not be opened\n", diary_dir);
+ fprintf(stderr, "The directory '%s' could not be opened\n", CONFIG.dir);
return 1;
}
- #ifdef __GNU_LIBRARY__
- // references: locale(5) and util-linux's cal.c
- // get the base date, 8-digit integer (YYYYMMDD) returned as char *
- #ifdef _NL_TIME_WEEK_1STDAY
- unsigned long d = (uintptr_t) nl_langinfo(_NL_TIME_WEEK_1STDAY);
- // reference: https://sourceware.org/glibc/wiki/Locales
- // assign a static date value 19971130 (a Sunday)
- #else
- unsigned long d = 19971130;
- #endif
- struct tm base = {
- .tm_sec = 0,
- .tm_min = 0,
- .tm_hour = 0,
- .tm_mday = d % 100,
- .tm_mon = (d / 100) % 100 - 1,
- .tm_year = d / (100 * 100) - 1900
- };
- mktime(&base);
- // first_weekday is base date's day of the week offset by (_NL_TIME_FIRST_WEEKDAY - 1)
- #ifdef __linux__
- first_weekday = (base.tm_wday + *nl_langinfo(_NL_TIME_FIRST_WEEKDAY) - 1) % 7;
- #elif defined __MACH__
- CFIndex first_day_of_week;
- CFCalendarRef currentCalendar = CFCalendarCopyCurrent();
- first_day_of_week = CFCalendarGetFirstWeekday(currentCalendar);
- CFRelease(currentCalendar);
- first_weekday = (base.tm_wday + first_day_of_week - 1) % 7;
- #endif
- #endif
-
setup_cal_timeframe();
initscr();
@@ -446,10 +525,10 @@ int main(int argc, char** argv) {
WINDOW* wdays = newwin(1, 3 * 7, 0, ASIDE_WIDTH);
draw_wdays(wdays);
- WINDOW* aside = newpad((YEAR_RANGE * 2 + 1) * 12 * MAX_MONTH_HEIGHT, ASIDE_WIDTH);
- WINDOW* cal = newpad((YEAR_RANGE * 2 + 1) * 12 * MAX_MONTH_HEIGHT, CAL_WIDTH);
+ WINDOW* aside = newpad((CONFIG.range * 2 + 1) * 12 * MAX_MONTH_HEIGHT, ASIDE_WIDTH);
+ WINDOW* cal = newpad((CONFIG.range * 2 + 1) * 12 * MAX_MONTH_HEIGHT, CAL_WIDTH);
keypad(cal, TRUE);
- draw_calendar(cal, aside, diary_dir, strlen(diary_dir));
+ draw_calendar(cal, aside, CONFIG.dir, strlen(CONFIG.dir));
int ch, conf_ch;
int pad_pos = 0;
@@ -457,7 +536,7 @@ int main(int argc, char** argv) {
struct tm new_date;
int prev_width = COLS - ASIDE_WIDTH - CAL_WIDTH;
int prev_height = LINES - 1;
- size_t diary_dir_size = strlen(diary_dir);
+ size_t diary_dir_size = strlen(CONFIG.dir);
bool mv_valid = go_to(cal, aside, raw_time, &pad_pos);
// mark current day
@@ -466,7 +545,7 @@ int main(int argc, char** argv) {
prefresh(cal, pad_pos, 0, 1, ASIDE_WIDTH, LINES - 1, ASIDE_WIDTH + CAL_WIDTH);
WINDOW* prev = newwin(prev_height, prev_width, 1, ASIDE_WIDTH + CAL_WIDTH);
- display_entry(diary_dir, strlen(diary_dir), &today, prev, prev_width);
+ display_entry(CONFIG.dir, diary_dir_size, &today, prev, prev_width);
do {
@@ -479,7 +558,7 @@ int main(int argc, char** argv) {
char pth[100];
char* ppth = pth;
char dstr[16];
- edit_cmd(diary_dir, diary_dir_size, &new_date, &pecmd, sizeof ecmd);
+ edit_cmd(CONFIG.dir, diary_dir_size, &new_date, &pecmd, sizeof ecmd);
switch(ch) {
// basic movements
@@ -506,11 +585,11 @@ int main(int argc, char** argv) {
// jump to top/bottom of page
case 'g':
- new_date = find_closest_entry(cal_start, false, diary_dir, diary_dir_size);
+ new_date = find_closest_entry(cal_start, false, CONFIG.dir, diary_dir_size);
mv_valid = go_to(cal, aside, mktime(&new_date), &pad_pos);
break;
case 'G':
- new_date = find_closest_entry(cal_end, true, diary_dir, diary_dir_size);
+ new_date = find_closest_entry(cal_end, true, CONFIG.dir, diary_dir_size);
mv_valid = go_to(cal, aside, mktime(&new_date), &pad_pos);
break;
@@ -551,9 +630,9 @@ int main(int argc, char** argv) {
// delete entry
case 'd':
case 'x':
- if (date_has_entry(diary_dir, diary_dir_size, &curs_date)) {
+ if (date_has_entry(CONFIG.dir, diary_dir_size, &curs_date)) {
// get file path of entry and delete entry
- fpath(diary_dir, diary_dir_size, &curs_date, &ppth, sizeof pth);
+ fpath(CONFIG.dir, diary_dir_size, &curs_date, &ppth, sizeof pth);
if (ppth == NULL) {
fprintf(stderr, "Error retrieving file path for entry removal");
break;
@@ -601,7 +680,7 @@ int main(int argc, char** argv) {
keypad(cal, TRUE);
// mark newly created entry
- if (date_has_entry(diary_dir, diary_dir_size, &curs_date)) {
+ if (date_has_entry(CONFIG.dir, diary_dir_size, &curs_date)) {
atrs = winch(cal) & A_ATTRIBUTES;
wchgat(cal, 2, atrs | A_BOLD, 0, NULL);
@@ -612,12 +691,12 @@ int main(int argc, char** argv) {
break;
// Move to the previous diary entry
case 'N':
- new_date = find_closest_entry(new_date, true, diary_dir, diary_dir_size);
+ new_date = find_closest_entry(new_date, true, CONFIG.dir, diary_dir_size);
mv_valid = go_to(cal, aside, mktime(&new_date), &pad_pos);
break;
// Move to the next diary entry
case 'n':
- new_date = find_closest_entry(new_date, false, diary_dir, diary_dir_size);
+ new_date = find_closest_entry(new_date, false, CONFIG.dir, diary_dir_size);
mv_valid = go_to(cal, aside, mktime(&new_date), &pad_pos);
break;
}
@@ -630,7 +709,7 @@ int main(int argc, char** argv) {
wresize(prev, prev_height, prev_width);
// read the diary
- display_entry(diary_dir, diary_dir_size, &curs_date, prev, prev_width);
+ display_entry(CONFIG.dir, diary_dir_size, &curs_date, prev, prev_width);
}
} while (ch != 'q');
diff --git a/diary.cfg b/diary.cfg
@@ -0,0 +1,10 @@
+# Path that holds the journal text files
+#dir = ~/diary
+# Number of years to show before/after todays date
+range = 1
+# 0 = Sunday, 1 = Monday, ..., 6 = Saturday
+weekday = 1
+# Date and file format, change with care
+fmt = %Y-%m-%d
+# Editor to open journal files with
+editor = ""
+\ No newline at end of file
diff --git a/diary.h b/diary.h
@@ -5,8 +5,10 @@
#ifdef __MACH__
#include <CoreFoundation/CoreFoundation.h>
#endif
+#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
+#include <wordexp.h>
#include <unistd.h>
#include <getopt.h>
#include <string.h>
@@ -17,8 +19,9 @@
#include <locale.h>
#include <langinfo.h>
+#define XDG_CONFIG_HOME_FALLBACK "~/.config"
+#define CONFIG_FILE_PATH "diary/diary.cfg"
#define DIARY_VERSION "0.4"
-#define YEAR_RANGE 1
#define CAL_WIDTH 21
#define ASIDE_WIDTH 4
#define MAX_MONTH_HEIGHT 6
@@ -38,4 +41,25 @@ bool date_has_entry(const char* dir, size_t dir_size, const struct tm* i);
void get_date_str(const struct tm* date, char* date_str, size_t date_str_size);
void fpath(const char* dir, size_t dir_size, const struct tm* date, char** rpath, size_t rpath_size);
+typedef struct
+{
+ // Path that holds the journal text files
+ char* dir;
+ // Number of years to show before/after todays date
+ int range;
+ // 7 = Sunday, 1 = Monday, ..., 6 = Saturday
+ int weekday;
+ // 2020-12-31
+ char* fmt;
+ // Editor to open journal files with
+ char* editor;
+} config;
+
+config CONFIG = {
+ .range = 1,
+ .weekday = 1,
+ .fmt = "%Y-%m-%d",
+ .editor = ""
+};
+
#endif