Blob


1 /*
2 * Copyright (c) 2021 Matthias Schmidt <xhr@giessen.ccc.de>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
17 #include <sys/stat.h>
18 #include <sys/types.h>
20 #include <limits.h>
21 #include <signal.h>
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <time.h>
27 #include <unistd.h>
29 #include <readline/readline.h>
30 #include <readline/history.h>
32 #include "isscrolls.h"
34 static char prompt[MAX_PROMPT_LEN];
35 char isscrolls_dir[_POSIX_PATH_MAX];
37 static int debug = 0;
38 static int color = 0;
40 static void
41 signal_handler(int signal)
42 {
43 switch (signal) {
44 case SIGINT:
45 case SIGTERM:
46 shutdown();
47 break;
48 default:
49 log_errx(1, "Unknown signal");
50 }
51 }
53 void
54 show_banner(__attribute__((unused)) char *unused)
55 {
56 pm(GREEN, "\n██▓ ██████ ██████ ▄████▄ ██▀███ ▒█████ ██▓ ██▓ ██████\n");
57 pm(GREEN, "▓██▒▒██ ▒ ▒██ ▒ ▒██▀ ▀█ ▓██ ▒ ██▒▒██▒ ██▒▓██▒ ▓██▒ ▒██ ▒\n");
58 pm(GREEN, "▒██▒░ ▓██▄ ░ ▓██▄ ▒▓█ ▄ ▓██ ░▄█ ▒▒██░ ██▒▒██░ ▒██░ ░ ▓██▄\n");
59 pm(GREEN, "░██░ ▒ ██▒ ▒ ██▒▒▓▓▄ ▄██▒▒██▀▀█▄ ▒██ ██░▒██░ ▒██░ ▒ ██▒\n");
60 pm(GREEN, "░██░▒██████▒▒▒██████▒▒▒ ▓███▀ ░░██▓ ▒██▒░ ████▓▒░░██████▒░██████▒▒██████▒▒\n");
61 pm(GREEN, "░▓ ▒ ▒▓▒ ▒ ░▒ ▒▓▒ ▒ ░░ ░▒ ▒ ░░ ▒▓ ░▒▓░░ ▒░▒░▒░ ░ ▒░▓ ░░ ▒░▓ ░▒ ▒▓▒ ▒ ░\n");
62 pm(GREEN, " ▒ ░░ ░▒ ░ ░░ ░▒ ░ ░ ░ ▒ ░▒ ░ ▒░ ░ ▒ ▒░ ░ ░ ▒ ░░ ░ ▒ ░░ ░▒ ░ ░\n");
63 pm(GREEN, " ▒ ░░ ░ ░ ░ ░ ░ ░ ░░ ░ ░ ░ ░ ▒ ░ ░ ░ ░ ░ ░ ░\n");
64 pm(GREEN, " ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░\n");
65 pm(GREEN, " ░\n");
66 printf(" Version %s\n\n", VERSION);
67 printf("\tSimple player toolkit for the Ironsworn tabletop RPG\n");
68 printf("\tBy Matthias Schmidt - https://mastodon.social/@_xhr_\n\n");
69 printf("Enter 'help' for available commands\n\n");
70 }
72 int
73 main(int argc, char **argv)
74 {
75 char *line, *res;
76 int ch;
78 /*
79 * Seed the PRNG with the current time of the day. This is not fine
80 * for a security critical application, for rolling dice it is
81 */
82 srandom(time(NULL));
84 while ((ch = getopt(argc, argv, "cd")) != -1) {
85 switch (ch) {
86 case 'c':
87 color = 1;
88 break;
89 case 'd':
90 debug = 1;
91 break;
92 }
93 }
95 argc -= optind;
96 argv += optind;
98 setup_base_dir();
100 initialize_readline(isscrolls_dir);
102 show_banner(NULL);
104 if (signal(SIGINT, signal_handler) == SIG_ERR)
105 log_errx(1, "signal");
106 if (signal(SIGTERM, signal_handler) == SIG_ERR)
107 log_errx(1, "signal");
109 sandbox(isscrolls_dir);
111 load_characters_list();
113 set_prompt("> ");
114 while (1) {
115 line = readline(prompt);
116 if (line == NULL)
117 continue;
118 res = stripwhite(line);
120 if (*res) {
121 add_history(res);
122 execute_command(res);
125 free(line);
128 shutdown();
130 return 0;
133 void
134 set_prompt(const char *p)
136 if (p == NULL || strlen(p) == 0)
137 return;
139 snprintf(prompt, sizeof(prompt), "%s", p);
142 void
143 sandbox(const char *dir)
145 #ifdef __OpenBSD__
146 if (unveil(_PATH_SHARE_DIR, "r") == -1)
147 log_errx(1, "unveil");
148 if (unveil(dir, "rwc") == -1)
149 log_errx(1, "unveil");
150 if (unveil(NULL, NULL) == -1)
151 log_errx(1, "unveil");
153 if (pledge("stdio rpath wpath cpath tty", NULL) == -1)
154 log_errx(1, "pledge");
155 #endif /* __OpenBSD__ */
158 void
159 cmd_quit(__attribute__((unused)) char *unused)
161 shutdown();
164 void
165 shutdown()
167 char hist_path[_POSIX_PATH_MAX];
169 save_current_character();
171 snprintf(hist_path, _POSIX_PATH_MAX, "%s/history", isscrolls_dir);
173 log_debug("Writing history to %s\n", hist_path);
174 write_history(hist_path);
176 exit(0);
179 void
180 setup_base_dir()
182 struct stat sb;
183 char *home, *xdg_home;
185 if ((xdg_home = getenv("XDG_CONFIG_HOME")) != NULL) {
186 snprintf(isscrolls_dir, _POSIX_PATH_MAX, "%s/isscrolls", xdg_home);
187 } else if ((home = getenv("HOME")) != NULL) {
188 snprintf(isscrolls_dir, _POSIX_PATH_MAX, "%s/.isscrolls", home);
189 } else {
190 log_errx(1, "Neither $XDG_CONFIG_HOME nor $HOME is set!");
193 if (stat(isscrolls_dir, &sb) == 0 && S_ISDIR(sb.st_mode)) {
194 log_debug("%s already exists\n", isscrolls_dir);
195 } else {
196 log_debug("%s does not exists. Attempt to create it\n", isscrolls_dir);
197 if (mkdir(isscrolls_dir, 0755) == -1) {
198 log_errx(1, "Cannot create %s directory\n", isscrolls_dir);
203 void
204 log_debug(const char *fmt, ...)
206 va_list ap;
208 if (debug == 0)
209 return;
211 va_start(ap, fmt);
212 fprintf(stdout, "[*] ");
213 vfprintf(stdout, fmt, ap);
214 va_end(ap);
217 void
218 log_errx(int prio, const char *fmt, ...)
220 va_list ap;
222 va_start(ap, fmt);
223 vfprintf(stderr, fmt, ap);
224 va_end(ap);
226 shutdown();
229 void
230 pm(int what, const char *fmt, ...)
232 va_list ap;
234 va_start(ap, fmt);
235 if (color == 1) {
236 switch (what) {
237 case RED:
238 fprintf(stdout, ANSI_COLOR_RED);
239 break;
240 case YELLOW:
241 fprintf(stdout, ANSI_COLOR_YELLOW);
242 break;
243 case GREEN:
244 fprintf(stdout, ANSI_COLOR_GREEN);
245 break;
246 default:
247 break;
251 vfprintf(stdout, fmt, ap);
252 if (color == 1)
253 fprintf(stdout, ANSI_COLOR_RESET);
254 va_end(ap);
257 const char*
258 get_isscrolls_dir()
260 return isscrolls_dir;