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