1 /*\
2 |*|  Parity Archive - A way to restore missing files in a set.
3 |*|
4 |*|  Copyright (C) 2001  Willem Monsuwe (willem@stack.nl)
5 |*|
6 |*|  File format by Stefan Wehlus -
7 |*|   initial idea by Tobias Rieper, further suggestions by Kilroy Balore
8 |*|
9 |*|  Text User Interface
10 \*/
11 
12 #include <stdio.h>
13 #include <ctype.h>
14 #include <string.h>
15 #include <stdlib.h>
16 
17 #include "interface.h"
18 #include "fileops.h"
19 
20 static u16 **parlist = 0;
21 static u16 **filelist = 0;
22 static u16 **dirlist = 0;
23 
24 enum {
25 	CMD_FLAGS,
26 	CMD_SETFLAGS,
27 	CMD_UNSETFLAGS,
28 	CMD_LOAD,
29 	CMD_SEARCH,
30 	CMD_UNLOAD,
31 	CMD_PARLIST,
32 	CMD_FILELIST,
33 	CMD_DIRLIST,
34 	CMD_CHECK,
35 	CMD_FIND,
36 	CMD_FIXNAME,
37 	CMD_GETSTATUS,
38 	CMD_SETSTATUS,
39 	CMD_RECOVER,
40 	CMD_ADDFILE,
41 	CMD_REMOVEFILE,
42 	CMD_ADDPARS,
43 	CMD_CREATE,
44 	CMD_SETSMART,
45 	CMD_HELP,
46 	CMD_QUIT,
47 	CMD_UNKNOWN,
48 	CMD_AMBIGUOUS
49 };
50 
51 static struct cmds {
52 	char *str;
53 	int cmd;
54 } cmds[] = {
55 	{ "FLAGS",	CMD_FLAGS },
56 	{ "SETFLAGS",	CMD_SETFLAGS },
57 	{ "UNSETFLAGS",	CMD_UNSETFLAGS },
58 	{ "LOAD",	CMD_LOAD },
59 	{ "SEARCH",	CMD_SEARCH },
60 	{ "UNLOAD",	CMD_UNLOAD },
61 	{ "PARLIST",	CMD_PARLIST },
62 	{ "FILELIST",	CMD_FILELIST },
63 	{ "DIRLIST",	CMD_DIRLIST },
64 	{ "CHECK",	CMD_CHECK },
65 	{ "FIND",	CMD_FIND },
66 	{ "FIXNAME",	CMD_FIXNAME },
67 	{ "GETSTATUS",	CMD_GETSTATUS },
68 	{ "SETSTATUS",	CMD_SETSTATUS },
69 	{ "RECOVER",	CMD_RECOVER },
70 	{ "ADDFILE",	CMD_ADDFILE },
71 	{ "REMOVEFILE",	CMD_REMOVEFILE },
72 	{ "ADDPARS",	CMD_ADDPARS },
73 	{ "CREATE",	CMD_CREATE },
74 	{ "SETSMART",	CMD_SETSMART },
75 	{ "HELP",	CMD_HELP },
76 	{ "QUIT",	CMD_QUIT }
77 };
78 
79 #define NO_CMDS (sizeof(cmds) / sizeof(*cmds))
80 
81 static void
print_help(void)82 print_help(void)
83 {
84 	printf(
85 "FLAGS              : Show the current flags (with numbers).\n"
86 "SETFLAGS <n>       : Set flag number <n>.\n"
87 "UNSETFLAGS <n>     : Unset flag number <n>.\n"
88 "SETSMART [<entry> <name>] : Setup handling of consistently misnamed files.\n"
89 "LOAD <filename>    : Add a (new) PAR file to the current list.\n"
90 "SEARCH             : Search for PAR files matching the current filelist.\n"
91 "UNLOAD <entry>     : Remove a PAR file from the list.\n"
92 "PARLIST            : Show the current list of PAR files.\n"
93 "FILELIST           : Show the current list of data files.\n"
94 "DIRLIST            : Show the currently cached directory entries.\n"
95 "CHECK <entry>      : Check the MD5sum of a file.\n"
96 "FIND <entry>       : Find a file by its filename.\n"
97 "FIXNAME [<entry>]  : Fix faulty filenames [of <entry>].\n"
98 "GETSTATUS <entry>  : Get the status bits of an entry.\n"
99 "SETSTATUS <entry> <status> : Set the status bits of an entry.\n"
100 "RECOVER [<entry>]  : Recover missing files [only <entry>]\n"
101 "ADDFILE <filename> : Add a data file to the current filelist.\n"
102 "REMOVEFILE <entry> : Remove a data file from the current filelist.\n"
103 "ADDPARS <number>   : Add new PAR files until there are <number>.\n"
104 "CREATE [<entry>]   : Create PAR files [only <entry>].\n"
105 "HELP               : Show this help.\n"
106 "QUIT               : Quit.\n"
107 		);
108 }
109 
110 static int cc = '\n';
111 
112 #define EOL(x) (((x) == '\n') || ((x) == EOF))
113 
114 static void
sort_cmds(void)115 sort_cmds(void)
116 {
117 	int i, j, k;
118 
119 	k = NO_CMDS - 1;
120 	do {
121 		for (i = j = 0; i < k; i++) {
122 			if (strcmp(cmds[i].str, cmds[i + 1].str) > 0) {
123 				struct cmds tmp;
124 				tmp = cmds[i + 1];
125 				cmds[i + 1] = cmds[i];
126 				cmds[i] = tmp;
127 				j = i;
128 			}
129 		}
130 		k = j;
131 	} while (k > 0);
132 }
133 
134 static int
get_cmd(void)135 get_cmd(void)
136 {
137 	struct cmds *l, *r;
138 	int c;
139 	int idx;
140 
141 	do {
142 		while (!EOL(cc))
143 			cc = getchar();
144 
145 		if (cc == EOF)
146 			return CMD_QUIT;
147 
148 		putchar('>'); fflush(stdout);
149 
150 		l = cmds;
151 		r = cmds + NO_CMDS - 1;
152 		for (idx = 0;; idx++) {
153 			cc = getchar();
154 			if (!isalnum(cc))
155 				break;
156 			c = toupper(cc);
157 			while (c > l->str[idx])
158 				if (++l > r)
159 					return CMD_UNKNOWN;
160 			while (c < r->str[idx])
161 				if (--r < l)
162 					return CMD_UNKNOWN;
163 		}
164 	} while (idx == 0);
165 	if (r > l)
166 		return CMD_AMBIGUOUS;
167 	return l->cmd;
168 }
169 
170 static int bufidx = 0;
171 
172 static u16 *
buf_add(u16 c)173 buf_add(u16 c)
174 {
175 	static u16 *buf = 0;
176 	static int bufsz = 0;
177 
178 	if (bufidx >= bufsz) {
179 		bufsz += 256;
180 		buf = realloc(buf, bufsz * sizeof(*buf));
181 	}
182 	buf[bufidx++] = c;
183 	return buf;
184 }
185 
186 static u16 *
get_str(void)187 get_str(void)
188 {
189 	bufidx = 0;
190 	while (isspace(cc)) {
191 		if (EOL(cc))
192 			return buf_add(0);
193 		cc = getchar();
194 	}
195 	do {
196 		buf_add(cc);
197 		cc = getchar();
198 	} while (!EOL(cc));
199 	return buf_add(0);
200 }
201 
202 static i64
get_number(void)203 get_number(void)
204 {
205 	i64 ret;
206 	int base = 10;
207 
208 	while (isspace(cc)) {
209 		if (EOL(cc))
210 			return 0;
211 		cc = getchar();
212 	}
213 	ret = 0;
214 	if (cc == '0') {
215 		base = 8;
216 		cc = getchar();
217 	}
218 	if ((cc == 'x') || (cc == 'X') || (cc == '$')) {
219 		base = 16;
220 		cc = getchar();
221 	}
222 	for (;;) {
223 		int c;
224 
225 		c = toupper(cc);
226 		if (c >= 'a') c -= 'a';
227 		else c -= '0';
228 		if ((c < 0) || (c >= base)) return ret;
229 		ret = ret * base + c;
230 		cc = getchar();
231 	}
232 }
233 
234 static u16 *
get_entry(u16 ** list)235 get_entry(u16 **list)
236 {
237 	i64 num, i;
238 
239 	i = get_number();
240 	if (!list) return 0;
241 	for (num = 0; list[num]; num++)
242 		;
243 	if ((i < 0) || (i > num))
244 		i = 0;
245 	if (i == 0) return 0;
246 	return list[i - 1];
247 }
248 
249 static void
print_errcode(int code)250 print_errcode(int code)
251 {
252 	static char *err_list[] = {
253 		"OK",
254 		"ERROR: File error",
255 		"ERROR: Does not exist",
256 		"ERROR: Not found",
257 		"ERROR: Corrupt",
258 		"ERROR: Failed",
259 		"ERROR: Already loaded",
260 		"ERROR: Not Implemented",
261 		"ERROR: Name Clash",
262 		"ERROR: Invalid Argument"
263 	};
264 	printf("%s\n", err_list[code]);
265 }
266 
267 static void
print_flags(int flags)268 print_flags(int flags)
269 {
270 	static char *flag_list[] = {
271 		"MOVE",
272 		"CASE",
273 		"CTRL",
274 		"KEEP",
275 	};
276 	unsigned int i;
277 
278 	for (i = 0; i < (sizeof(flag_list) / sizeof(*flag_list)); i++)
279 	{
280 		if (i > 0) printf(", ");
281 		if (!(flags & (1 << i))) printf("NO");
282 		printf("%s(%d)", flag_list[i], 1 << i);
283 	}
284 	printf("\n");
285 }
286 
287 static void
print_string(u16 * str)288 print_string(u16 *str)
289 {
290 	while (*str)
291 		putchar(*str++);
292 	putchar('\n');
293 }
294 
295 static void
print_number(i64 num)296 print_number(i64 num)
297 {
298 	printf("0x%llx\n", num);
299 }
300 
301 static void
print_list(u16 ** list)302 print_list(u16 **list)
303 {
304 	int i;
305 
306 	for (i = 1; *list; list++, i++) {
307 		printf("%3d: ", i);
308 		print_string(*list);
309 	}
310 }
311 
312 /*\ Parse commands until QUIT is read \*/
313 void
ui_text(void)314 ui_text(void)
315 {
316 	u16 *e;
317 
318 	sort_cmds();
319 	cc = '\n';
320 
321 	for (;;) switch (get_cmd()) {
322 	case CMD_FLAGS:
323 		print_flags(par_flags());
324 		break;
325 	case CMD_SETFLAGS:
326 		print_flags(par_setflags(get_number()));
327 		break;
328 	case CMD_UNSETFLAGS:
329 		print_flags(par_unsetflags(get_number()));
330 		break;
331 	case CMD_LOAD:
332 		print_errcode(par_load(get_str()));
333 		break;
334 	case CMD_SEARCH:
335 		print_errcode(par_search(get_number()));
336 		break;
337 	case CMD_UNLOAD:
338 		print_errcode(par_unload(get_entry(parlist)));
339 		break;
340 	case CMD_PARLIST:
341 		if (parlist) free(parlist);
342 		parlist = par_parlist();
343 		print_list(parlist);
344 		break;
345 	case CMD_FILELIST:
346 		if (filelist) free(filelist);
347 		filelist = par_filelist();
348 		print_list(filelist);
349 		break;
350 	case CMD_DIRLIST:
351 		if (dirlist) free(dirlist);
352 		dirlist = par_dirlist();
353 		print_list(dirlist);
354 		break;
355 	case CMD_CHECK:
356 		e = get_entry(filelist);
357 		if (e) {
358 			print_errcode(par_check(e));
359 		} else if (filelist) {
360 			int i;
361 			for (i = 0; filelist[i]; i++) {
362 				printf("CHECK ");
363 				print_string(filelist[i]);
364 				print_errcode(par_check(filelist[i]));
365 			}
366 		} else {
367 			printf("ERROR: No filelist.\n");
368 		}
369 		break;
370 	case CMD_FIND:
371 		e = get_entry(filelist);
372 		if (e) {
373 			print_string(par_find(e));
374 		} else if (filelist) {
375 			int i;
376 			for (i = 0; filelist[i]; i++) {
377 				printf("FIND ");
378 				print_string(filelist[i]);
379 				print_string(par_find(filelist[i]));
380 			}
381 		} else {
382 			printf("ERROR: No filelist.\n");
383 		}
384 		break;
385 	case CMD_FIXNAME:
386 		print_errcode(par_fixname(get_entry(filelist)));
387 		break;
388 	case CMD_GETSTATUS:
389 		print_number(par_getstatus(get_entry(filelist)));
390 		break;
391 	case CMD_SETSTATUS:
392 		e = get_entry(filelist);
393 		print_errcode(par_setstatus(e, get_number()));
394 		break;
395 	case CMD_RECOVER:
396 		print_errcode(par_recover(get_entry(filelist)));
397 		break;
398 	case CMD_ADDFILE:
399 		print_errcode(par_addfile(get_str()));
400 		break;
401 	case CMD_REMOVEFILE:
402 		print_errcode(par_removefile(get_entry(filelist)));
403 		break;
404 	case CMD_ADDPARS:
405 		e = get_entry(parlist);
406 		print_errcode(par_addpars(e, get_number()));
407 		break;
408 	case CMD_CREATE:
409 		print_errcode(par_create(get_entry(parlist)));
410 		break;
411 	case CMD_SETSMART:
412 		e = get_entry(filelist);
413 		print_errcode(par_setsmart(e, e ? get_str() : 0));
414 		break;
415 	case CMD_QUIT:
416 		return;
417 	case CMD_HELP:
418 		print_help();
419 		break;
420 	case CMD_AMBIGUOUS:
421 		printf("Ambiguous command.\n");
422 		break;
423 	default:
424 		printf("Unknown command.\n");
425 		break;
426 	}
427 }
428