1 /******************************************************************************
2 *   This file is part of TinTin++                                             *
3 *                                                                             *
4 *   Copyright 2004-2020 Igor van den Hoven                                    *
5 *                                                                             *
6 *   TinTin++ is free software; you can redistribute it and/or modify          *
7 *   it under the terms of the GNU General Public License as published by      *
8 *   the Free Software Foundation; either version 3 of the License, or         *
9 *   (at your option) any later version.                                       *
10 *                                                                             *
11 *   This program is distributed in the hope that it will be useful,           *
12 *   but WITHOUT ANY WARRANTY; without even the implied warranty of            *
13 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
14 *   GNU General Public License for more details.                              *
15 *                                                                             *
16 *   You should have received a copy of the GNU General Public License         *
17 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
18 ******************************************************************************/
19 
20 /******************************************************************************
21 *                               T I N T I N + +                               *
22 *                                                                             *
23 *                      coded by Igor van den Hoven 2005                       *
24 ******************************************************************************/
25 
26 #include "tintin.h"
27 
28 #ifdef HAVE_PTY_H
29   #include <pty.h>
30 #else
31   #ifdef HAVE_UTIL_H
32     #include <util.h>
33   #endif
34 #endif
35 #include <dirent.h>
36 #include <limits.h>
37 
38 #define DO_SCAN(scan) struct session *scan(struct session *ses, FILE *fp, char *arg, char *arg1, char *arg2)
39 
40 DO_SCAN(scan_abort);
41 DO_SCAN(scan_csv);
42 DO_SCAN(scan_dir);
43 DO_SCAN(scan_file);
44 DO_SCAN(scan_forward);
45 DO_SCAN(scan_tsv);
46 DO_SCAN(scan_txt);
47 
48 #define SCAN_FLAG_NONE                0
49 #define SCAN_FLAG_FILE                BV01
50 #define SCAN_FLAG_SCAN                BV02
51 
52 typedef struct session *SCAN(struct session *ses, FILE *fp, char *arg, char *arg1, char *arg2);
53 
54 struct scan_type
55 {
56 	char                  * name;
57 	SCAN                  * fun;
58 	int                     flags;
59 	char                  * desc;
60 };
61 
62 struct scan_type scan_table[] =
63 {
64 	{       "ABORT",            scan_abort,   SCAN_FLAG_NONE,                "Abort a scan currently in progress."},
65 	{       "CSV",              scan_csv,     SCAN_FLAG_FILE|SCAN_FLAG_SCAN, "Scan a comma separated value file." },
66 	{       "DIR",              scan_dir,     SCAN_FLAG_NONE,                "Scan a directory to a variable."    },
67 	{       "FILE",             scan_file,    SCAN_FLAG_FILE,                "Scan a file all at once."           },
68 	{       "FORWARD",          scan_forward, SCAN_FLAG_FILE,                "Scan a file and send each line."    },
69 	{       "TSV",              scan_tsv,     SCAN_FLAG_FILE|SCAN_FLAG_SCAN, "Scan a tab separated value file."   },
70 	{       "TXT",              scan_txt,     SCAN_FLAG_FILE|SCAN_FLAG_SCAN, "Scan a text file line by line."     },
71 	{       "",                 NULL,         0,                             ""                                   }
72 };
73 
DO_COMMAND(do_scan)74 DO_COMMAND(do_scan)
75 {
76 	char cmd[BUFFER_SIZE];
77 	FILE *fp = NULL;
78 	int cnt;
79 
80 	arg = sub_arg_in_braces(ses, arg, cmd, GET_ONE, SUB_VAR|SUB_FUN);
81 
82 	if (*cmd == 0)
83 	{
84 		tintin_header(ses, 80, " SCAN OPTIONS ");
85 
86 		for (cnt = 0 ; *scan_table[cnt].name != 0 ; cnt++)
87 		{
88 			tintin_printf2(ses, "  [%-13s] %s", scan_table[cnt].name, scan_table[cnt].desc);
89 		}
90 		tintin_header(ses, 80, "");
91 
92 		return ses;
93 	}
94 
95 	for (cnt = 0 ; *scan_table[cnt].name != 0 ; cnt++)
96 	{
97 		if (!is_abbrev(cmd, scan_table[cnt].name))
98 		{
99 			continue;
100 		}
101 
102 		arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
103 
104 		if (HAS_BIT(scan_table[cnt].flags, SCAN_FLAG_FILE))
105 		{
106 			if (*arg1 == 0)
107 			{
108 				show_error(ses, LIST_COMMAND, "#SYNTAX: #SCAN {%s} {<FILENAME>}", scan_table[cnt].name);
109 
110 				return ses;
111 			}
112 
113 			if ((fp = fopen(arg1, "r")) == NULL)
114 			{
115 				show_error(ses, LIST_COMMAND, "#ERROR: #SCAN {%s} FILE {%s} NOT FOUND.", scan_table[cnt].name, arg1);
116 
117 				return ses;
118 			}
119 		}
120 
121 		if (HAS_BIT(scan_table[cnt].flags, SCAN_FLAG_SCAN))
122 		{
123 			gtd->level->scan++;
124 		}
125 
126 		ses = scan_table[cnt].fun(ses, fp, arg, arg1, arg2);
127 
128 		if (HAS_BIT(scan_table[cnt].flags, SCAN_FLAG_SCAN))
129 		{
130 			gtd->level->scan--;
131 		}
132 
133 		if (HAS_BIT(scan_table[cnt].flags, SCAN_FLAG_FILE))
134 		{
135 			fclose(fp);
136 		}
137 		return ses;
138 	}
139 
140 	show_error(ses, LIST_COMMAND, "\e[1;31mTHE SCAN COMMAND HAS CHANGED, EXECUTING #SCAN {TXT} {%s} INSTEAD.", cmd);
141 
142 	ses = command(ses, do_scan, "{TXT} {%s}", cmd);
143 
144 	return ses;
145 }
146 
DO_SCAN(scan_abort)147 DO_SCAN(scan_abort)
148 {
149 	if (gtd->level->scan)
150 	{
151 		SET_BIT(ses->flags, SES_FLAG_SCANABORT);
152 	}
153 	else
154 	{
155 		show_error(ses, LIST_COMMAND, "#SCAN ABORT: NOT CURRENTLY SCANNING.");
156 	}
157 	return ses;
158 }
159 
160 /* support routines for comma separated value files */
161 
get_arg_in_quotes(struct session * ses,char * string,char * result,int flag)162 char *get_arg_in_quotes(struct session *ses, char *string, char *result, int flag)
163 {
164 	char *pti, *pto;
165 	int nest = 0;
166 
167 	pti = space_out(string);
168 	pto = result;
169 
170 	if (*pti == '"')
171 	{
172 		nest = TRUE;
173 		pti++;
174 	}
175 
176 	while (*pti)
177 	{
178 		if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, pti))
179 		{
180 			*pto++ = *pti++;
181 			*pto++ = *pti++;
182 			continue;
183 		}
184 
185 		if (*pti == '"')
186 		{
187 			if (pti[1] == '"')
188 			{
189 				*pto++ = *pti++;
190 			}
191 			else if (nest)
192 			{
193 				nest = FALSE;
194 			}
195 			pti++;
196 			continue;
197 		}
198 		else if (nest == TRUE)
199 		{
200 			*pto++ = *pti++;
201 		}
202 		else if (*pti == ' ' || *pti == '\t')
203 		{
204 			pti++;
205 		}
206 		else if (*pti == ',')
207 		{
208 			pti++;
209 			break;
210 		}
211 		else
212 		{
213 			*pto++ = *pti++;
214 		}
215 	}
216 
217 	if (nest)
218 	{
219 		tintin_printf2(ses, "#SCAN CSV: GET QUOTED ARGUMENT: UNMATCHED QUOTE.");
220 	}
221 	*pto = 0;
222 
223 	return pti;
224 }
225 
226 
DO_SCAN(scan_csv)227 DO_SCAN(scan_csv)
228 {
229 	char line[STRING_SIZE];
230 	int i, header = FALSE;
231 
232 	while (fgets(line, BUFFER_SIZE, fp))
233 	{
234 		arg = strchr(line, '\r');
235 
236 		if (arg)
237 		{
238 			*arg = 0;
239 		}
240 		else
241 		{
242 
243 			arg = strchr(line, '\n');
244 
245 			if (arg)
246 			{
247 				*arg = 0;
248 			}
249 		}
250 
251 		RESTRING(gtd->vars[0], line);
252 
253 		arg = line;
254 
255 		for (i = 1 ; i < 100 ; i++)
256 		{
257 			arg = get_arg_in_quotes(ses, arg, arg2, FALSE);
258 
259 			RESTRING(gtd->vars[i], arg2);
260 
261 			if (*arg == 0)
262 			{
263 				while (++i < 100)
264 				{
265 					if (*gtd->vars[i])
266 					{
267 						RESTRING(gtd->vars[i], "");
268 					}
269 				}
270 				break;
271 			}
272 		}
273 
274 		if (header == FALSE)
275 		{
276 			header = TRUE;
277 
278 			check_all_events(ses, SUB_SEC|EVENT_FLAG_SCAN, 0, 0, "SCAN CSV HEADER");
279 		}
280 		else
281 		{
282 			check_all_events(ses, SUB_SEC|EVENT_FLAG_SCAN, 0, 0, "SCAN CSV LINE");
283 		}
284 
285 		if (HAS_BIT(ses->flags, SES_FLAG_SCANABORT))
286 		{
287 			break;
288 		}
289 	}
290 
291 	if (HAS_BIT(ses->flags, SES_FLAG_SCANABORT))
292 	{
293 		DEL_BIT(ses->flags, SES_FLAG_SCANABORT);
294 
295 		show_message(ses, LIST_COMMAND, "#SCAN CSV: FILE {%s} PARTIALLY SCANNED.", arg1);
296 	}
297 	else
298 	{
299 		show_message(ses, LIST_COMMAND, "#SCAN CSV: FILE {%s} SCANNED.", arg1);
300 	}
301 
302 	return ses;
303 }
304 
DO_SCAN(scan_dir)305 DO_SCAN(scan_dir)
306 {
307 	char cwd[PATH_SIZE], filename[PATH_SIZE * 2];
308 	struct dirent **dirlist;
309 	struct stat info;
310 	int size, index;
311 
312 	arg = get_arg_in_braces(ses, arg, arg2, GET_ALL);
313 
314 	if (*arg2 == 0)
315 	{
316 		show_error(ses, LIST_COMMAND, "SYNTAX: #SCAN DIR {%s} <VARIABLE NAME>", arg1);
317 
318 		return ses;
319 	}
320 
321 	set_nest_node_ses(ses, arg2, "");
322 
323 	if (*arg1 == 0)
324 	{
325 		if (getcwd(cwd, PATH_MAX) == NULL)
326 		{
327 			syserr_printf(ses, "scan_dir: getcwd:");
328 
329 			cwd[0] = 0;
330 		}
331 		arg1 = cwd;
332 	}
333 
334 	size = scandir(arg1, &dirlist, 0, NULL);
335 
336 	if (size == -1)
337 	{
338 		if (stat(arg1, &info) == -1)
339 		{
340 			syserr_printf(ses, "scan_dir: stat(%s): error:", arg1);
341 
342 			return ses;
343 		}
344 
345 		arg = arg1;
346 
347 		while (strchr(arg, '\\'))
348 		{
349 			arg = strchr(arg, '\\');
350 		}
351 
352 		add_nest_node_ses(ses, arg2, "{%s}{{FILE}{%d}{MODE}{%u}{SIZE}{%u}{TIME}{%lld}}",
353 			arg,
354 			!S_ISDIR(info.st_mode),
355 			info.st_mode,
356 			info.st_size,
357 			info.st_mtime);
358 
359 		return ses;
360 	}
361 
362 	for (index = 0 ; index < size ; index++)
363 	{
364 		sprintf(filename, "%s%s%s", arg1, is_suffix(arg1, "/") ? "" : "/", dirlist[index]->d_name);
365 
366 		if (stat(filename, &info) == -1)
367 		{
368 			syserr_printf(ses, "scan_dir: stat(%s): error:", filename);
369 
370 			continue;
371 		}
372 
373 		add_nest_node_ses(ses, arg2, "{%s}{{FILE}{%d}{MODE}{%u}{SIZE}{%u}{TIME}{%lld}}",
374 			dirlist[index]->d_name,
375 			!S_ISDIR(info.st_mode),
376 			info.st_mode,
377 			info.st_size,
378 			info.st_mtime);
379 	}
380 
381 	for (index = 0 ; index < size ; index++)
382 	{
383 		free(dirlist[index]);
384 	}
385 	free(dirlist);
386 
387 	show_message(ses, LIST_COMMAND, "#SCAN DIR: DIRECTORY {%s} SAVED TO {%s}.", arg1, arg2);
388 
389 	return ses;
390 }
391 
DO_SCAN(scan_file)392 DO_SCAN(scan_file)
393 {
394 	char line[STRING_SIZE], *str_out, *str_rip, *str_sub;
395 	int cnt = 0;
396 
397 	arg = get_arg_in_braces(ses, arg, arg2, GET_ALL);
398 
399 	str_out = str_alloc_stack(0);
400 
401 	while (fgets(line, BUFFER_SIZE - 1, fp))
402 	{
403 		cnt++;
404 		str_cat(&str_out, line);
405 	}
406 
407 	str_rip = str_alloc_stack(str_len(str_out));
408 
409 	strip_vt102_codes(str_out, str_rip);
410 
411 	RESTRING(gtd->cmds[0], str_out);
412 	RESTRING(gtd->cmds[1], str_rip);
413 	RESTRING(gtd->cmds[2], ntos(str_len(str_out)));
414 	RESTRING(gtd->cmds[3], ntos(strlen(str_rip)));
415 	RESTRING(gtd->cmds[4], ntos(cnt));
416 
417 	str_sub = str_alloc_stack(strlen(arg) * 2);
418 
419 	substitute(ses, arg2, str_sub, SUB_CMD);
420 
421 	show_message(ses, LIST_COMMAND, "#SCAN FILE: FILE {%s} SCANNED.", arg1);
422 
423 	ses = script_driver(ses, LIST_COMMAND, str_sub);
424 
425 	return ses;
426 }
427 
DO_SCAN(scan_forward)428 DO_SCAN(scan_forward)
429 {
430 	char line[STRING_SIZE], *lnf;
431 	float delay = 0;
432 
433 	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN);
434 
435 	if (!HAS_BIT(ses->flags, SES_FLAG_CONNECTED))
436 	{
437 		show_error(ses, LIST_COMMAND, "#SCAN FORWARD: SESSION {%s} IS NOT CONNECTED.", ses->name);
438 
439 		return ses;
440 	}
441 
442 	while (fgets(line, BUFFER_SIZE - 1, fp))
443 	{
444 		lnf = strchr(line, '\n');
445 
446 		if (lnf)
447 		{
448 			*lnf = 0;
449 		}
450 
451 		if (*arg2)
452 		{
453 			delay += get_number(ses, arg2);
454 
455 			command(ses, do_delay, "%.3f #send {%s}", delay, line);
456 		}
457 		else
458 		{
459 			write_mud(ses, line, SUB_EOL);
460 		}
461 	}
462 
463 	show_message(ses, LIST_COMMAND, "#SCAN FORWARD: FILE {%s} FORWARDED.", arg1);
464 
465 	return ses;
466 }
467 
468 /* support routines for tab separated value files */
469 
get_arg_stop_tabs(struct session * ses,char * string,char * result,int flag)470 char *get_arg_stop_tabs(struct session *ses, char *string, char *result, int flag)
471 {
472 	char *pti, *pto;
473 
474 	pti = string;
475 	pto = result;
476 
477 	while (*pti)
478 	{
479 		if (*pti == '\t')
480 		{
481 			pti++;
482 			break;
483 		}
484 		*pto++ = *pti++;
485 	}
486 	*pto = 0;
487 
488 	return pti;
489 }
490 
DO_SCAN(scan_tsv)491 DO_SCAN(scan_tsv)
492 {
493 	char line[STRING_SIZE];
494 	int i, header = FALSE;
495 
496 	arg = get_arg_in_braces(ses, arg, arg2, GET_ALL);
497 
498 	while (fgets(line, BUFFER_SIZE, fp))
499 	{
500 		arg = strchr(line, '\r');
501 
502 		if (arg)
503 		{
504 			*arg = 0;
505 		}
506 		else
507 		{
508 
509 			arg = strchr(line, '\n');
510 
511 			if (arg)
512 			{
513 				*arg = 0;
514 			}
515 		}
516 
517 		RESTRING(gtd->vars[0], line);
518 
519 		arg = line;
520 
521 		for (i = 1 ; i < 100 ; i++)
522 		{
523 			arg = get_arg_stop_tabs(ses, arg, arg2, FALSE);
524 
525 			RESTRING(gtd->vars[i], arg2);
526 
527 			if (*arg == 0)
528 			{
529 				while (++i < 100)
530 				{
531 					if (*gtd->vars[i])
532 					{
533 						RESTRING(gtd->vars[i], "");
534 					}
535 				}
536 				break;
537 			}
538 		}
539 
540 		if (header == FALSE)
541 		{
542 			header = TRUE;
543 
544 			check_all_events(ses, SUB_SEC|EVENT_FLAG_SCAN, 0, 0, "SCAN TSV HEADER");
545 		}
546 		else
547 		{
548 			check_all_events(ses, SUB_SEC|EVENT_FLAG_SCAN, 0, 0, "SCAN TSV LINE");
549 		}
550 
551 		if (HAS_BIT(ses->flags, SES_FLAG_SCANABORT))
552 		{
553 			break;
554 		}
555 	}
556 
557 	if (HAS_BIT(ses->flags, SES_FLAG_SCANABORT))
558 	{
559 		DEL_BIT(ses->flags, SES_FLAG_SCANABORT);
560 
561 		show_message(ses, LIST_COMMAND, "#SCAN TSV: FILE {%s} PARTIALLY SCANNED.", arg1);
562 	}
563 	else
564 	{
565 		show_message(ses, LIST_COMMAND, "#SCAN TSV: FILE {%s} SCANNED.", arg1);
566 	}
567 	return ses;
568 }
569 
570 /* support routines for text files */
571 
DO_SCAN(scan_txt)572 DO_SCAN(scan_txt)
573 {
574 	char line[STRING_SIZE];
575 
576 	while (fgets(line, BUFFER_SIZE - 1, fp))
577 	{
578 		arg = strchr(line, '\r');
579 
580 		if (arg)
581 		{
582 			*arg = 0;
583 		}
584 		else
585 		{
586 
587 			arg = strchr(line, '\n');
588 
589 			if (arg)
590 			{
591 				*arg = 0;
592 			}
593 		}
594 
595 		process_mud_output(ses, line, FALSE);
596 
597 		if (HAS_BIT(ses->flags, SES_FLAG_SCANABORT))
598 		{
599 			break;
600 		}
601 	}
602 
603 	if (HAS_BIT(ses->flags, SES_FLAG_SCANABORT))
604 	{
605 		DEL_BIT(ses->flags, SES_FLAG_SCANABORT);
606 
607 		show_message(ses, LIST_COMMAND, "#SCAN TXT: FILE {%s} PARTIALLY SCANNED.", arg1);
608 	}
609 	else
610 	{
611 		show_message(ses, LIST_COMMAND, "#SCAN TXT: FILE {%s} SCANNED.", arg1);
612 	}
613 	return ses;
614 }
615 
616