/* * This file is part of firk's window manager for x11 (fwm) * Copyright (C) 2016-2022 firk * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include #include #include "main.h" #include "cfgparse.h" #include "config.h" #define ac_log 0 #define ac_run 1 #define ac_chdir 2 #define ac_setenv 3 struct startup_list { unsigned int ln; unsigned int type; /* 0=log, 1=shellcmd */ char const * cmd; char const * a2; }; #define MAX_HOTKEYS 200 #define MAX_STARTUP 200 static struct hotkeys_list hotkeys_list[MAX_HOTKEYS]; static size_t num_hotkeys, over_hotkeys; static struct startup_list startup_list[MAX_STARTUP]; static size_t num_startup, over_startup; static char const * userconf; /* NOTE: this variable may contain strdup'ed value as well as literal string constant */ int startup_errors; int TASKBAR_WINHEIGHT = DEFAULT_TASKBAR_WINHEIGHT; #ifndef DEFAULT_TASKBAR_FONT_NAME #define DEFAULT_TASKBAR_FONT_NAME NULL #endif char const * TASKBAR_FONT_NAME = DEFAULT_TASKBAR_FONT_NAME; #ifndef DEFAULT_HOLIDAYS_FILE #define DEFAULT_HOLIDAYS_FILE NULL #endif char const * HOLIDAYS_FILE = DEFAULT_HOLIDAYS_FILE; extern struct hotkeys_list * get_hotkeys_list(size_t * count) { *count = num_hotkeys; return hotkeys_list; } extern char const * get_hotkey_by_keycode(unsigned int kc, unsigned int shift) { unsigned int n; struct hotkeys_list * hk; for(hk=hotkeys_list,n=num_hotkeys; n; hk++,n--) if(hk->keycode==kc && hk->shift==shift) return hk->cmd; return NULL; } static void add_hotkey(unsigned int keysym, unsigned int shift, char const * cmd) { size_t n; struct hotkeys_list * hk; for(n=0,hk=hotkeys_list; nkeysym==keysym && hk->shift==shift) { hk->cmd = cmd; return; } } if(nkeysym = keysym; hk->shift = shift; hk->cmd = cmd; num_hotkeys++; } else over_hotkeys++; } static void add_startup(unsigned int ln, unsigned int type, char const * cmd, char const * a2) { if(num_startup50) { logprint("invalid option value at config line %u", ln); startup_errors = 1; TASKBAR_WINHEIGHT = DEFAULT_TASKBAR_WINHEIGHT; return; } return; } if(!strcmp(b0,"option:TASKBAR_FONT_NAME")) { if(!(TASKBAR_FONT_NAME = strdup(b1))) { logprint("out of memory while reading config line %u", ln); startup_errors = 1; TASKBAR_FONT_NAME = DEFAULT_TASKBAR_FONT_NAME; return; } return; } if(!strcmp(b0,"option:HOLIDAYS_FILE")) { if(!(HOLIDAYS_FILE = strdup(b1))) { logprint("out of memory while reading config line %u", ln); startup_errors = 1; HOLIDAYS_FILE = DEFAULT_HOLIDAYS_FILE; return; } return; } if(!strcmp(b0,"clear-hotkeys")) { num_hotkeys = over_hotkeys = 0; return; } if(!strcmp(b0,"clear-startup")) { clear_startup(); return; } logprint("error in config line %u", ln); startup_errors = 1; return; } static int parse_config_fd(int fd) { char buf[4000], c; unsigned int sz, p, p0, ln; int r, skip; sz = 0; r = 1; skip = 0; ln = 1; while(r) { if(sz>=sizeof(buf)) { logprint("config line %u too long, exiting", ln); exit(-1); } while((r = read(fd, buf+sz, sizeof(buf)-sz))<0) if(errno!=EINTR) return -1; for(p=sz,p0=0,sz+=r; p=0 && c!=9 && c<32 || c==127)) { logprint("config line %u contains invalid symbols, skipping it", ln); startup_errors = 1; skip = 1; } } if(p0) if(sz-=p0) bcopy(buf+p0, buf, sz); } if(sz) { logprint("config line %u (last) non-terminated, skipping it", ln); startup_errors = 1; } return 0; } static void run_startup_item(struct startup_list const * S) { char const * s; switch(S->type) { case ac_log: logprint("(config) %s", S->cmd); return; case ac_run: system(S->cmd); return; case ac_chdir: if(!(s = parse_str(S->cmd, 1))) { logprint("error parsing chdir argument at line %u", S->ln); exit(-1); } else if(chdir(s)<0) { logprint("chdir(%s) error at line %u", s, S->ln); exit(-1); } return; case ac_setenv: if(!(s = parse_str(S->cmd, 0))) { logprint("error parsing setenv argument at line %u", S->ln); exit(-1); } else if(setenv(S->a2, s, 1)<0) { logprint("setenv(%s) error at line %u", S->a2, S->ln); exit(-1); } return; } } static int load_config_file(char const * fn, char const * curname, char const * fbname, int may_missing) { int fd; logprint("loading config file: %s", fn); if(fn[0]=='-' && !fn[1]) { if(parse_config_fd(0)<0) { logprint("%sconfig read(%s) i/o error, falling back to %s", curname, fn, fbname); return -1; } } else { if((fd = open(fn, O_RDONLY|O_NOCTTY))<0) { if(errno==ENOENT && may_missing) { logprint("%sconfig file \"%s\" does not exist", curname, fn); return -2; } logprint("%sconfig open(%s) failed, falling back to %s", curname, fn, fbname); return -1; } if(parse_config_fd(fd)<0) { logprint("%sconfig read(%s) i/o error, falling back to %s", curname, fn, fbname); close(fd); return -1; } close(fd); } return 0; } #ifndef DEFAULT_CONFIG_FILE #define DEFAULT_CONFIG_FILE NULL #endif #ifndef DEFAULT_USERCONFIG_FILE #define DEFAULT_USERCONFIG_FILE NULL #endif extern void load_configs(unsigned int count, char const * const * fns) { unsigned int i; int r, ok; char const * fn; if(!count || !(fn=*fns) || !*fn) { ok = 0; if(DEFAULT_CONFIG_FILE && (r=load_config_file(DEFAULT_CONFIG_FILE,"system-wide ","defaults",1))!=-2) { if(r<0) { startup_errors=1; goto fallback; } ok = 1; } if(userconf || (userconf=DEFAULT_USERCONFIG_FILE)) { if(!(fn = parse_str(userconf,1))) { logprint("error constructing user-local config-file path"); startup_errors=1; } else if(*fn) { if(!(fn=strdup(fn))) { logprint("error constructing user-local config-file path: out of memory"); startup_errors=1; } else { r = load_config_file(fn,"user-local ",ok?"system-wide":"defaults",1); free((void*)fn); if(r==-2) ; else if(r<0) { startup_errors = 1; if(!ok || !DEFAULT_CONFIG_FILE) goto fallback; r = load_config_file(DEFAULT_CONFIG_FILE,"system-wide ","defaults",1); if(r<0) goto fallback; ok = 1; } else ok = 1; } } } if(!ok) goto defcfg; } else { if(load_config_file(fn, "", "defaults", 0)<0) goto fallback; for(i=1; i'9') break; n = Y*10 + (c-'0'); if(n'9') break; n = M*10 + (c-'0'); if(n12 || c!='-') goto err; while(1) { c = *(st++); if(c<'0' || c>'9') break; n = D*10 + (c-'0'); if(n31 || c!=' ') goto err; c = *(st++); if(c<'0' || c>'3' || *st) goto err; c = (c=='1'); list = *pl; if(!(j = *pn)) { if(!(list = malloc(sizeof(*list)))) goto mem; list->Y = Y; list->M = M; list->n = 1; if(!(list->list = malloc(2))) { free(list); goto mem; } list->list[0] = D; list->list[1] = c; *pl = list; *pn = 1; return 0; } if(list[j-1].Y>Y || list[j-1].Y==Y && list[j-1].M>M) { logprint("holidays-list line %u not in order, skipping it", ln); return -1; } if(list[j-1].Y!=Y || list[j-1].M!=M) { jj = (j+1)*sizeof(*list); if(!jj || jj/sizeof(*list)-1!=j) goto mem; if(!(list = realloc(list, jj))) goto mem; *pl = list; list[j].Y = Y; list[j].M = M; list[j].n = 1; if(!(list[j].list = malloc(2))) goto mem; list[j].list[0] = D; list[j].list[1] = c; *pn = j+1; return 0; } list += j-1; if(list->n>=31) { logprint("holidays-list line %u - too many entires for smae month, skipping it", ln); return -1; } if(!(ll = realloc(list->list, (list->n+1)*2))) goto mem; list->list = ll; ll[list->n*2] = D; ll[list->n*2+1] = c; list->n++; return 0; err: logprint("holidays-list line %u syntax error, skipping it", ln); return -1; mem: logprint("out of memory while parsing holidays-list line %u, skipping it", ln); return -1; } static int parse_holidays_list_fd(int fd, t_holidays ** pl, size_t * pn) { char buf[4000], c; unsigned int sz, p, p0, ln; int r, skip; sz = 0; r = 1; skip = 0; ln = 1; while(r) { if(sz>=sizeof(buf)) { logprint("holidays-list line %u too long", ln); return -1; } while((r = read(fd, buf+sz, sizeof(buf)-sz))<0) if(errno!=EINTR) return -1; for(p=sz,p0=0,sz+=r; p=0 && c!=9 && c<32 || c==127)) { logprint("holidays-list line %u contains invalid symbols, skipping it", ln); skip = 1; } } if(p0) if(sz-=p0) bcopy(buf+p0, buf, sz); } if(sz) { logprint("holidays-list line %u (last) non-terminated, skipping it", ln); } return 0; } extern void check_holidays_file(int force) { int fd; t_holidays * newlist; size_t newn; struct stat sb; if(!HOLIDAYS_FILE) return; while(stat(HOLIDAYS_FILE, &sb)<0) if(errno!=EINTR) { logprint("can't stat holidays file, please create it or disable in config: %s", HOLIDAYS_FILE); holidays_error = 1; return; } if(!force && sb.st_mtime==holidays_list_time) return; if(!holidays_list_time) logprint("loading holidays file: %s", HOLIDAYS_FILE); else logprint("reloading holidays file: %s", HOLIDAYS_FILE); while((fd = open(HOLIDAYS_FILE, O_RDONLY|O_NOCTTY))<0) if(errno!=EINTR) { logprint("can't open holidays file, please create it or disable in config: %s", HOLIDAYS_FILE); holidays_error = 1; return; } holidays_list_time = sb.st_mtime; newlist = NULL; newn = 0; if(parse_holidays_list_fd(fd, &newlist, &newn)<0) { close(fd); free_holidays_list(newlist, newn); logprint("error reading or parsing holidays file, please fix it or disable in config: %s", HOLIDAYS_FILE); holidays_error = 1; return; } close(fd); holidays_last = NULL; free_holidays_list(holidays_list, num_holidays_list); holidays_list = newlist; num_holidays_list = newn; holidays_error = 0; } static t_holidays * get_holidays_list0(unsigned Y, unsigned M) { t_holidays * hl; size_t n; for(hl=holidays_list,n=0; nYY==Y) { if(hl->MM==M) return hl; } break; } return &holidays_none; } extern unsigned char const * get_holidays_list(unsigned Y, unsigned M, size_t * sz) { t_holidays * hl; hl = get_holidays_list0(Y,M); *sz = hl->n; return hl->list; } extern int get_holiday_state(unsigned Y, unsigned M, unsigned D) { unsigned j; unsigned char const *l; if(!holidays_last || holidays_last->Y!=Y || holidays_last->M!=M) holidays_last = get_holidays_list0(Y,M); for(j=holidays_last->n,l=holidays_last->list; j; j--,l+=2) if(l[0]==D) return l[1]; return -1; }