tim

small extendable personal assistant
git clone git://edryd.org/tim
Log | Files | Refs | LICENSE

tim.c (4213B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include <ctype.h>
      3 #include <limits.h>
      4 #include <pwd.h>
      5 #include <stdarg.h>
      6 #include <stdio.h>
      7 #include <stdlib.h>
      8 #include <string.h>
      9 #include <unistd.h>
     10 
     11 #include "arg.h"
     12 #include "str.h"
     13 #include "tim-lua.h"
     14 #include "util.h"
     15 
     16 /* macros */
     17 #define MAX_CMD 2048
     18 
     19 /* typedefs */
     20 
     21 /* functions */
     22 char *get_tim_ans(char *s);
     23 
     24 char *fmt_cmd(char *s);
     25 void run_cmd(void);
     26 void print_out(void);
     27 
     28 void setup(void);
     29 void cleanup(void);
     30 void usage(void);
     31 
     32 /* variables */
     33 char *argv0;
     34 
     35 char *in;
     36 char *cmd;
     37 char *out;
     38 int fmtout = 1;
     39 
     40 #include "config.h"
     41 
     42 /* returns the path to fname based on:
     43  *  1) $XDG_CONFIG_HOME/tim/ (~/.config/tim/)
     44  *  3) /etc/tim/             (system wide config)
     45  *  2) /usr/local/share/tim/ (default when installed)
     46  *  4) ./                    (current directory)
     47  */
     48 char *
     49 get_path(const char *fname)
     50 {
     51 	if (!fname) return NULL;
     52 	int i;
     53 	static char ret[PATH_MAX];
     54 	const char *paths[] = { "/etc/tim/", "/usr/local/share/tim/", "./" };
     55 	const char *home = getenv("HOME");
     56 	const char *xdg_config = getenv("XDG_CONFIG_HOME");
     57 	struct passwd *pw;
     58 
     59 #ifdef DEBUG
     60 	/* if DEBUG is set check current directory first */
     61 	strcpy(ret, "./");
     62 	strcat(ret, fname);
     63 	if (access(ret, F_OK) != -1)
     64 		return ret;
     65 #endif
     66 
     67 	/* try to get users home directory */
     68 	if (!home || !*home)
     69 		if ((pw = getpwuid(getuid())))
     70 			home = pw->pw_dir;
     71 
     72 	/* get XDG_CONFIG_HOME path */
     73 	if (xdg_config)
     74 		snprintf(ret, sizeof(ret), "%s/tim/%s", xdg_config, fname);
     75 	else if (home && *home)
     76 		snprintf(ret, sizeof(ret), "%s/.config/tim/%s", home, fname);
     77 
     78 	/* try rest of paths in paths[] */
     79 	for (i = 0; access(ret, F_OK) == -1; i++) {
     80 		if (i >= LEN(paths)) die("%s: file '%s' not found", argv0, fname);
     81 		strcpy(ret, paths[i]);
     82 		strcat(ret, fname);
     83 	}
     84 
     85 	return ret;
     86 }
     87 
     88 char *
     89 get_tim_ans(char *s)
     90 {
     91 	if (!s) return NULL;
     92 	char *ans = NULL;
     93 	int c, i = 2;
     94 	char **cmds;
     95 	char *fname = NULL;
     96 	char args[MAX_CMD];
     97 
     98 	s = str_replace(s, "to", "");
     99 	char *tmp = str_rmspaces(s);
    100 	cmds = str_split(tmp, ' ', &c);
    101 
    102 	if (c > 2) {
    103 		if (strcmp(cmds[1], "my") == 0)
    104 			fname = get_path(memfile);
    105 		else if (strcmp(cmds[1], "your") == 0)
    106 			fname = get_path(cogfile);
    107 		else {
    108 			fname = get_path(cogfile);
    109 			i--;
    110 		}
    111 	}
    112 
    113 	if (strcmp(cmds[0], "get") == 0 && c > 2) {
    114 		snprintf(args, sizeof(args), "%s %s", fname, cmds[i]);
    115 		ans = tim_lua_run(get_path("plugins/json-get.lua"), args);
    116 	} else if (strcmp(cmds[0], "set") == 0 && c > 3) {
    117 		snprintf(args, sizeof(args), "%s %s %s", fname, cmds[i], cmds[i+1]);
    118 		ans = tim_lua_run(get_path("plugins/json-set.lua"), args);
    119 	}
    120 
    121 	free(s);
    122 	free(tmp);
    123 	for (int j = 0; j < c; j++)
    124 		free(cmds[j]);
    125 	free(cmds);
    126 
    127 	return ans;
    128 }
    129 
    130 char *
    131 fmt_cmd(char *s)
    132 {
    133 	int i;
    134 	for (i = 0; s[i]; i++)
    135 		s[i] = tolower(s[i]); /* TODO not for proper nouns */
    136 	return s;
    137 }
    138 
    139 /* generate tim's response */
    140 void
    141 run_cmd(void)
    142 {
    143 	char args[MAX_CMD];
    144 	cmd = fmt_cmd(in);
    145 	snprintf(args, sizeof(args), "%s %s", get_path(actfile), cmd);
    146 	out = tim_lua_run(get_path("plugins/json-get.lua"), args);
    147 	if (STREMPTY(out))
    148 		out = get_tim_ans(cmd);
    149 	if (STREMPTY(out))
    150 		out = tim_lua_run(get_path("plugins/weather.lua"), cmd);
    151 	if (STREMPTY(out))
    152 		out = tim_lua_run(get_path("plugins/rand.lua"), cmd);
    153 	if (STREMPTY(out)) {
    154 		out = tim_lua_run(get_path("plugins/ddg.lua"), in);
    155 		fmtout = 0;
    156 	}
    157 	if (STREMPTY(out))
    158 		out = estrdup("Sorry I didn't get that");
    159 }
    160 
    161 /* print tim's respond */
    162 void
    163 print_out(void)
    164 {
    165 	if (!out) return;
    166 	printf("%s\n", out);
    167 	out = NULL;
    168 }
    169 
    170 /* allocate strs */
    171 void
    172 setup(void)
    173 {
    174 	in  = ecalloc(MAX_CMD, sizeof(char));
    175 	out = ecalloc(MAX_CMD, sizeof(char));
    176 }
    177 
    178 /* free memory */
    179 void
    180 cleanup(void)
    181 {
    182 	free(in);
    183 	free(out);
    184 }
    185 
    186 void
    187 usage(void)
    188 {
    189 	die("usage: %s [-iv] [CMD]", argv0);
    190 }
    191 
    192 int
    193 main(int argc, char *argv[])
    194 {
    195 	ARGBEGIN {
    196 		case 'i':
    197 			interactive = 1;
    198 			break;
    199 		case 'v':
    200 			printf("%s v%s (c) 2017 Ed van Bruggen\n", argv0, VERSION);
    201 			return 0;
    202 		default:
    203 			usage();
    204 	} ARGEND;
    205 
    206 	setup();
    207 
    208 	do {
    209 		if (argc > 0) {
    210 			in = str_concat(argv, argc);
    211 			argc = 0;
    212 		} else
    213 			fgets(in, MAX_CMD, stdin);
    214 		str_trim(in);
    215 		run_cmd();
    216 		print_out();
    217 	} while (interactive && !feof(stdin));
    218 
    219 	cleanup();
    220 
    221 	return 0;
    222 }