1 | // This file is part of SmallBASIC |
2 | // |
3 | // SmallBASIC RTL - FILESYSTEM, FILE and DEVICE I/O |
4 | // |
5 | // This program is distributed under the terms of the GPL v2.0 or later |
6 | // Download the GNU Public License (GPL) from www.gnu.org |
7 | // |
8 | // Copyright(C) 2000 Nicholas Christopoulos |
9 | |
10 | #include "common/sys.h" |
11 | #include "common/kw.h" |
12 | #include "common/var.h" |
13 | #include "common/pproc.h" |
14 | #include "common/device.h" |
15 | #include "common/blib.h" |
16 | #include "common/messages.h" |
17 | #include "common/fs_socket_client.h" |
18 | |
19 | #include <dirent.h> |
20 | |
21 | #define LDLN_INC 256 |
22 | #define GROW_SIZE 1024 |
23 | #define BUFMAX 256 |
24 | #define CHK_ERR_CLEANUP(s) if (err_handle_error(s, &file_name)) return; |
25 | #define CHK_ERR(s) if (err_handle_error(s, NULL)) return; |
26 | |
27 | struct file_encoded_var { |
28 | byte sign; // always '$' |
29 | byte version; // |
30 | byte type; // |
31 | uint32_t size; // |
32 | }; |
33 | |
34 | /* |
35 | * OPEN "file" [FOR {INPUT|OUTPUT|APPEND}] AS #fileN |
36 | */ |
37 | void cmd_fopen() { |
38 | var_t file_name; |
39 | int flags = 0; |
40 | |
41 | // filename |
42 | par_getstr(&file_name); |
43 | if (prog_error) |
44 | return; |
45 | |
46 | // mode |
47 | if (code_peek() == kwFORSEP) { |
48 | code_skipnext(); |
49 | switch (code_peek()) { |
50 | case kwINPUTSEP: |
51 | flags = DEV_FILE_INPUT; |
52 | break; |
53 | case kwOUTPUTSEP: |
54 | flags = DEV_FILE_OUTPUT; |
55 | break; |
56 | case kwAPPENDSEP: |
57 | flags = DEV_FILE_APPEND; |
58 | break; |
59 | default: |
60 | rt_raise(ERR_SYNTAX); |
61 | v_free(&file_name); |
62 | return; |
63 | } |
64 | |
65 | code_skipnext(); |
66 | } else { |
67 | flags = 0; |
68 | } |
69 | |
70 | // file handle |
71 | if (code_peek() == kwAS) { |
72 | code_skipnext(); |
73 | |
74 | par_getsharp(); |
75 | if (!prog_error) { |
76 | int handle = par_getint(); |
77 | if (!prog_error) { |
78 | if (dev_fstatus(handle) == 0) { |
79 | dev_fopen(handle, file_name.v.p.ptr, flags); |
80 | } else { |
81 | rt_raise("OPEN: FILE IS ALREADY OPENED" ); |
82 | } |
83 | } |
84 | } |
85 | } else { |
86 | rt_raise(ERR_SYNTAX); |
87 | } |
88 | v_free(&file_name); |
89 | } |
90 | |
91 | /* |
92 | * CLOSE #fileN |
93 | */ |
94 | void cmd_fclose() { |
95 | // file handle |
96 | par_getsharp(); |
97 | if (!prog_error) { |
98 | int handle = par_getint(); |
99 | if (!prog_error) { |
100 | if (dev_fstatus(handle)) { |
101 | dev_fclose(handle); |
102 | } else { |
103 | rt_raise("CLOSE: FILE IS NOT OPENED" ); |
104 | } |
105 | } |
106 | } |
107 | } |
108 | |
109 | /* |
110 | * SEEK #fileN, pos |
111 | */ |
112 | void cmd_fseek() { |
113 | // file handle |
114 | par_getsharp(); |
115 | if (!prog_error) { |
116 | int handle = par_getint(); |
117 | if (!prog_error) { |
118 | if (dev_fstatus(handle)) { |
119 | par_getsep(); |
120 | if (!prog_error) { |
121 | uint32_t pos = par_getint(); |
122 | if (!prog_error) { |
123 | dev_fseek(handle, pos); |
124 | } |
125 | } |
126 | } else { |
127 | rt_raise("SEEK: FILE IS NOT OPENED" ); |
128 | } |
129 | } |
130 | } |
131 | } |
132 | |
133 | /* |
134 | * store a variable in binary form |
135 | */ |
136 | void write_encoded_var(int handle, var_t *var) { |
137 | struct file_encoded_var fv; |
138 | |
139 | fv.sign = '$'; |
140 | fv.version = 1; |
141 | fv.type = var->type; |
142 | switch (var->type) { |
143 | case V_INT: |
144 | fv.size = OS_INTSZ; |
145 | dev_fwrite(handle, (byte *)&fv, sizeof(struct file_encoded_var)); |
146 | dev_fwrite(handle, (byte *)&var->v.i, fv.size); |
147 | break; |
148 | case V_NUM: |
149 | fv.size = OS_REALSZ; |
150 | dev_fwrite(handle, (byte *)&fv, sizeof(struct file_encoded_var)); |
151 | dev_fwrite(handle, (byte *)&var->v.n, fv.size); |
152 | break; |
153 | case V_STR: |
154 | fv.size = strlen(var->v.p.ptr); |
155 | dev_fwrite(handle, (byte *)&fv, sizeof(struct file_encoded_var)); |
156 | dev_fwrite(handle, (byte *)var->v.p.ptr, fv.size); |
157 | break; |
158 | case V_ARRAY: |
159 | fv.size = v_asize(var); |
160 | dev_fwrite(handle, (byte *)&fv, sizeof(struct file_encoded_var)); |
161 | |
162 | // write additional data about array |
163 | dev_fwrite(handle, &v_maxdim(var), 1); |
164 | for (int i = 0; i < v_maxdim(var); i++) { |
165 | dev_fwrite(handle, (byte *)&v_lbound(var, i), sizeof(int)); |
166 | dev_fwrite(handle, (byte *)&v_ubound(var, i), sizeof(int)); |
167 | } |
168 | |
169 | // write elements |
170 | for (int i = 0; i < v_asize(var); i++) { |
171 | var_t *elem = v_elem(var, i); |
172 | write_encoded_var(handle, elem); |
173 | } |
174 | break; |
175 | }; |
176 | } |
177 | |
178 | /* |
179 | * read a variable from a binary form |
180 | */ |
181 | int read_encoded_var(int handle, var_t *var) { |
182 | struct file_encoded_var fv; |
183 | |
184 | dev_fread(handle, (byte *)&fv, sizeof(struct file_encoded_var)); |
185 | if (fv.sign != '$') { |
186 | rt_raise("READ: BAD SIGNATURE" ); |
187 | return -1; // bad signature |
188 | } |
189 | |
190 | v_free(var); |
191 | switch (fv.type) { |
192 | case V_INT: |
193 | var->type = V_INT; |
194 | dev_fread(handle, (byte *)&var->v.i, fv.size); |
195 | break; |
196 | case V_NUM: |
197 | var->type = V_NUM; |
198 | dev_fread(handle, (byte *)&var->v.n, fv.size); |
199 | break; |
200 | case V_STR: |
201 | var->type = V_STR; |
202 | var->v.p.ptr = malloc(fv.size + 1); |
203 | dev_fread(handle, (byte *)var->v.p.ptr, fv.size); |
204 | var->v.p.ptr[fv.size] = '\0'; |
205 | break; |
206 | case V_ARRAY: |
207 | v_new_array(var, fv.size); |
208 | |
209 | // read additional data about array |
210 | dev_fread(handle, &v_maxdim(var), 1); |
211 | for (int i = 0; i < v_maxdim(var); i++) { |
212 | dev_fread(handle, (byte *)&v_lbound(var, i), sizeof(int)); |
213 | dev_fread(handle, (byte *)&v_ubound(var, i), sizeof(int)); |
214 | } |
215 | |
216 | // write elements |
217 | for (int i = 0; i < v_asize(var); i++) { |
218 | var_t *elem = v_elem(var, i); |
219 | v_init(elem); |
220 | read_encoded_var(handle, elem); |
221 | } |
222 | break; |
223 | default: |
224 | return -2; // unknown data-type |
225 | }; |
226 | |
227 | return 0; |
228 | } |
229 | |
230 | /* |
231 | * WRITE #fileN; var1 [, varN] |
232 | */ |
233 | void cmd_fwrite() { |
234 | // file handle |
235 | par_getsharp(); |
236 | if (!prog_error) { |
237 | int handle = par_getint(); |
238 | if (!prog_error) { |
239 | if (code_peek() == kwTYPE_EOC || code_peek() == kwTYPE_LINE) { |
240 | // There are no parameters |
241 | if (!dev_fstatus(handle)) { |
242 | // dev_fwrite(handle, "\n", 1); |
243 | } else { |
244 | rt_raise(ERR_FILE_NOT_OPEN); |
245 | } |
246 | return; |
247 | } |
248 | |
249 | // allow commas |
250 | par_getsep(); |
251 | |
252 | if (!prog_error) { |
253 | if (dev_fstatus(handle)) { |
254 | byte code, exitf = 0; |
255 | var_t *var_p; |
256 | |
257 | do { |
258 | code = code_peek(); |
259 | switch (code) { |
260 | case kwTYPE_LINE: |
261 | case kwTYPE_EOC: |
262 | exitf = 1; |
263 | break; |
264 | case kwTYPE_SEP: |
265 | code_skipsep(); |
266 | break; |
267 | case kwTYPE_VAR: |
268 | var_p = par_getvar_ptr(); |
269 | if (!prog_error) |
270 | write_encoded_var(handle, var_p); |
271 | break; |
272 | default: |
273 | rt_raise("WRITE: ONLY VARIABLES ARE ALLOWED" ); |
274 | }; |
275 | |
276 | if (prog_error) { |
277 | return; |
278 | } |
279 | } while (!exitf); |
280 | } else { |
281 | rt_raise(ERR_FILE_NOT_OPEN); |
282 | } |
283 | } |
284 | } |
285 | } |
286 | } |
287 | |
288 | /* |
289 | * READ #fileN; var1 [, var2 [, ...]] |
290 | */ |
291 | void cmd_fread() { |
292 | // file handle |
293 | par_getsharp(); |
294 | if (!prog_error) { |
295 | int handle = par_getint(); |
296 | if (prog_error) { |
297 | return; |
298 | } |
299 | |
300 | // allow commas |
301 | par_getsep(); |
302 | if (prog_error) { |
303 | return; |
304 | } |
305 | |
306 | if (dev_fstatus(handle)) { |
307 | // get the variables |
308 | do { |
309 | if (prog_error) { |
310 | return; |
311 | } |
312 | // get variable's ptr |
313 | var_t *var_p = par_getvar_ptr(); |
314 | if (prog_error) { |
315 | return; |
316 | } |
317 | read_encoded_var(handle, var_p); |
318 | if (prog_error) { |
319 | return; |
320 | } |
321 | // next |
322 | byte code = code_peek(); |
323 | if (code == kwTYPE_SEP) { |
324 | // allow commas |
325 | par_getsep(); |
326 | } else { |
327 | break; |
328 | } |
329 | } while (1); |
330 | } else { |
331 | rt_raise(ERR_FILE_NOT_OPEN); |
332 | } |
333 | } |
334 | } |
335 | |
336 | void cmd_read_variable(int handle) { |
337 | byte code = code_peek(); |
338 | if (code != kwTYPE_VAR) { |
339 | err_syntax(kwLINEINPUT, "%P" ); |
340 | } else { |
341 | var_t *var_p = code_getvarptr(); |
342 | if (!prog_error) { |
343 | v_free(var_p); |
344 | int size = BUFMAX; |
345 | int index = 0; |
346 | byte ch; |
347 | |
348 | var_p->type = V_STR; |
349 | var_p->v.p.ptr = malloc(size); |
350 | |
351 | // READ IT |
352 | while (!dev_feof(handle)) { |
353 | dev_fread(handle, &ch, 1); |
354 | if (prog_error) { |
355 | v_free(var_p); |
356 | var_p->type = V_INT; |
357 | var_p->v.i = -1; |
358 | return; |
359 | } else if (ch == '\n') { |
360 | break; |
361 | } else if (ch != '\r') { |
362 | // store char |
363 | if (index == (size - 1)) { |
364 | size += BUFMAX; |
365 | var_p->v.p.ptr = realloc(var_p->v.p.ptr, size); |
366 | } |
367 | var_p->v.p.ptr[index] = ch; |
368 | index++; |
369 | } |
370 | } |
371 | var_p->v.p.ptr[index] = '\0'; |
372 | var_p->v.p.length = index + 1; |
373 | } |
374 | } |
375 | } |
376 | |
377 | /* |
378 | * LINE INPUT [#fileN;] var$ |
379 | */ |
380 | void cmd_flineinput() { |
381 | if (code_peek() == kwTYPE_SEP) { |
382 | // |
383 | // FILE OR DEVICE |
384 | // |
385 | par_getsharp(); |
386 | if (!prog_error) { |
387 | int handle = par_getint(); |
388 | if (!prog_error) { |
389 | // allow commas |
390 | par_getsep(); |
391 | if (!prog_error) { |
392 | if (dev_fstatus(handle)) { |
393 | cmd_read_variable(handle); |
394 | } else { |
395 | rt_raise(ERR_FILE_NOT_OPEN); |
396 | } |
397 | } |
398 | } |
399 | } |
400 | } else { |
401 | // |
402 | // CONSOLE |
403 | // |
404 | var_t *var_p = par_getvar_ptr(); |
405 | if (!prog_error) { |
406 | v_free(var_p); |
407 | var_p->type = V_STR; |
408 | var_p->v.p.ptr = calloc(SB_TEXTLINE_SIZE + 1, 1); |
409 | dev_gets(var_p->v.p.ptr, SB_TEXTLINE_SIZE); |
410 | var_p->v.p.length = strlen(var_p->v.p.ptr); |
411 | dev_print("\n" ); |
412 | } |
413 | } |
414 | } |
415 | |
416 | /* |
417 | * KILL filename |
418 | */ |
419 | void cmd_fkill() { |
420 | var_t file_name; |
421 | |
422 | // filename |
423 | v_init(&file_name); |
424 | par_getstr(&file_name); |
425 | if (prog_error) { |
426 | return; |
427 | } |
428 | if (dev_fexists(file_name.v.p.ptr)) { |
429 | dev_fremove(file_name.v.p.ptr); |
430 | } |
431 | v_free(&file_name); |
432 | } |
433 | |
434 | /* |
435 | * COPY/RENAME filem newfile |
436 | */ |
437 | void cmd_filecp(int mv) { |
438 | var_t src, dst; |
439 | |
440 | // filename |
441 | v_init(&src); |
442 | v_init(&dst); |
443 | par_getstr(&src); |
444 | if (prog_error) { |
445 | return; |
446 | } |
447 | par_getcomma(); |
448 | if (prog_error) { |
449 | v_free(&src); |
450 | return; |
451 | } |
452 | par_getstr(&dst); |
453 | if (prog_error) { |
454 | v_free(&src); |
455 | return; |
456 | } |
457 | |
458 | if (dev_fexists(src.v.p.ptr)) { |
459 | if (!mv) { |
460 | dev_fcopy(src.v.p.ptr, dst.v.p.ptr); |
461 | } else { |
462 | dev_frename(src.v.p.ptr, dst.v.p.ptr); |
463 | } |
464 | } else { |
465 | rt_raise("COPY/RENAME: FILE DOES NOT EXIST" ); |
466 | } |
467 | v_free(&src); |
468 | v_free(&dst); |
469 | } |
470 | |
471 | /* |
472 | * change the current directory |
473 | */ |
474 | void cmd_chdir() { |
475 | var_t dir; |
476 | |
477 | // filename |
478 | v_init(&dir); |
479 | par_getstr(&dir); |
480 | if (prog_error) { |
481 | return; |
482 | } |
483 | dev_chdir(dir.v.p.ptr); |
484 | v_free(&dir); |
485 | } |
486 | |
487 | /* |
488 | * remove directory |
489 | */ |
490 | void cmd_rmdir() { |
491 | var_t dir; |
492 | |
493 | // filename |
494 | v_init(&dir); |
495 | par_getstr(&dir); |
496 | if (prog_error) { |
497 | return; |
498 | } |
499 | dev_rmdir(dir.v.p.ptr); |
500 | v_free(&dir); |
501 | } |
502 | |
503 | /* |
504 | * create directory |
505 | */ |
506 | void cmd_mkdir() { |
507 | var_t dir; |
508 | |
509 | // filename |
510 | v_init(&dir); |
511 | par_getstr(&dir); |
512 | if (prog_error) { |
513 | return; |
514 | } |
515 | dev_mkdir(dir.v.p.ptr); |
516 | v_free(&dir); |
517 | } |
518 | |
519 | /* |
520 | * load text-file to string or to array |
521 | * Modified 2-May-2002 Chris Warren-Smith. Implemented buffered read |
522 | * |
523 | * TLOAD filename, variable [, type] |
524 | */ |
525 | void cmd_floadln() { |
526 | var_t file_name, *array_p = NULL, *var_p = NULL; |
527 | int flags = DEV_FILE_INPUT; |
528 | int handle; |
529 | byte ch, type = 0; |
530 | char buf[BUFMAX]; |
531 | |
532 | if (code_peek() == kwTYPE_SEP) { |
533 | // "filename" is an already open file number |
534 | flags = 0; |
535 | par_getsharp(); |
536 | CHK_ERR(FSERR_INVALID_PARAMETER); |
537 | handle = par_getint(); |
538 | CHK_ERR(FSERR_INVALID_PARAMETER); |
539 | par_getcomma(); |
540 | CHK_ERR(FSERR_INVALID_PARAMETER); |
541 | array_p = var_p = code_getvarptr(); |
542 | CHK_ERR(FSERR_INVALID_PARAMETER); |
543 | if (code_peek() == kwTYPE_SEP) { |
544 | par_getcomma(); |
545 | CHK_ERR(FSERR_INVALID_PARAMETER); |
546 | type = par_getint(); |
547 | } |
548 | |
549 | dev_file_t *f = dev_getfileptr(handle); |
550 | if (f->type == ft_http_client) { |
551 | http_read(f, var_p); // TLOAD #1, html_str |
552 | return; |
553 | } |
554 | } else { |
555 | // filename |
556 | par_getstr(&file_name); |
557 | CHK_ERR(FSERR_INVALID_PARAMETER); |
558 | par_getcomma(); |
559 | CHK_ERR_CLEANUP(FSERR_INVALID_PARAMETER); |
560 | array_p = var_p = code_getvarptr(); |
561 | CHK_ERR_CLEANUP(FSERR_INVALID_PARAMETER); |
562 | if (code_peek() == kwTYPE_SEP) { |
563 | par_getcomma(); |
564 | CHK_ERR_CLEANUP(FSERR_INVALID_PARAMETER); |
565 | type = par_getint(); |
566 | } |
567 | |
568 | handle = dev_freefilehandle(); |
569 | CHK_ERR_CLEANUP(FSERR_GENERIC); |
570 | if (dev_fstatus(handle)) { |
571 | v_free(&file_name); |
572 | rt_raise(FSERR_GENERIC); |
573 | return; |
574 | } |
575 | if (v_strlen(&file_name) == 0) { |
576 | err_throw(FSERR_NOT_FOUND); |
577 | } else { |
578 | dev_fopen(handle, file_name.v.p.ptr, flags); |
579 | } |
580 | v_free(&file_name); |
581 | CHK_ERR(FSERR_GENERIC); |
582 | } |
583 | |
584 | if (type == 0) { |
585 | // build array |
586 | int array_size = LDLN_INC; |
587 | int index = 0; |
588 | int bufIndex = 0; |
589 | int bufLen = 0; |
590 | int eof = dev_feof(handle); |
591 | uint32_t unreadBytes = eof ? 0 : dev_flength(handle); |
592 | v_toarray1(array_p, array_size); // v_free() is here |
593 | |
594 | while (!eof) { |
595 | // build var for line |
596 | var_p = v_elem(array_p, index); |
597 | int size = GROW_SIZE; |
598 | v_init_str(var_p, size); |
599 | index++; |
600 | |
601 | // process the next line |
602 | int bcount = 0; |
603 | int eol = 0; |
604 | while (!eof && !eol) { |
605 | if (bufIndex == bufLen) { // read into empty buffer |
606 | if (dev_feof(handle) || unreadBytes == 0) { |
607 | eof = 1; |
608 | break; |
609 | } |
610 | bufLen = (unreadBytes > BUFMAX) ? BUFMAX : unreadBytes; |
611 | bufIndex = 0; |
612 | unreadBytes -= bufLen; |
613 | |
614 | dev_fread(handle, (byte *)buf, bufLen); |
615 | if (prog_error) { |
616 | eof = 1; |
617 | break; |
618 | } |
619 | } |
620 | |
621 | ch = buf[bufIndex++]; |
622 | if (ch == '\n') { |
623 | eol = 1; |
624 | break; |
625 | } else if (ch != '\r') { // store char |
626 | if (bcount >= (size - 1)) { |
627 | size += GROW_SIZE; |
628 | var_p->v.p.ptr = realloc(var_p->v.p.ptr, size); |
629 | } |
630 | var_p->v.p.ptr[bcount] = ch; |
631 | bcount++; |
632 | } |
633 | } // read line |
634 | |
635 | if (prog_error) { |
636 | // clear & exit |
637 | v_free(array_p); |
638 | v_init(array_p); |
639 | break; |
640 | } |
641 | |
642 | // store text-line |
643 | var_p->v.p.ptr[bcount] = '\0'; |
644 | var_p->v.p.length = bcount + 1; |
645 | var_p->v.p.ptr = realloc(var_p->v.p.ptr, var_p->v.p.length); |
646 | |
647 | // resize array |
648 | if (index >= (array_size - 1)) { |
649 | array_size += LDLN_INC; |
650 | v_resize_array(array_p, array_size); |
651 | } |
652 | } // read file |
653 | |
654 | if (index) { |
655 | v_resize_array(array_p, index); |
656 | } else { |
657 | v_resize_array(array_p, 0); // v_free() is here |
658 | } |
659 | } else { |
660 | // type == 1, build string |
661 | v_free(var_p); |
662 | int len = dev_flength(handle); |
663 | if (len < 1 || prog_error) { |
664 | err_throw(FSERR_NOT_FOUND); |
665 | } else { |
666 | v_init_str(var_p, len); |
667 | if (var_p->v.p.length > 1) { |
668 | dev_fread(handle, (byte *)var_p->v.p.ptr, var_p->v.p.length - 1); |
669 | var_p->v.p.ptr[var_p->v.p.length - 1] = '\0'; |
670 | } |
671 | } |
672 | } |
673 | if (flags == DEV_FILE_INPUT) { |
674 | dev_fclose(handle); |
675 | } |
676 | } |
677 | |
678 | /* |
679 | * save text file |
680 | * |
681 | * TSAVE filename, array/string |
682 | */ |
683 | void cmd_fsaveln() { |
684 | var_t file_name, *array_p = NULL, *var_p = NULL; |
685 | int flags = DEV_FILE_OUTPUT; |
686 | intptr_t handle; |
687 | |
688 | if (code_peek() == kwTYPE_SEP) { |
689 | // "filename" is an already open file number |
690 | flags = 0; |
691 | par_getsharp(); |
692 | CHK_ERR(FSERR_INVALID_PARAMETER); |
693 | handle = par_getint(); |
694 | CHK_ERR(FSERR_INVALID_PARAMETER); |
695 | par_getcomma(); |
696 | CHK_ERR(FSERR_INVALID_PARAMETER); |
697 | array_p = var_p = code_getvarptr(); |
698 | CHK_ERR(FSERR_INVALID_PARAMETER); |
699 | } else { |
700 | // filename |
701 | par_getstr(&file_name); |
702 | CHK_ERR(FSERR_INVALID_PARAMETER); |
703 | par_getcomma(); |
704 | CHK_ERR_CLEANUP(FSERR_INVALID_PARAMETER); |
705 | array_p = var_p = code_getvarptr(); |
706 | CHK_ERR_CLEANUP(FSERR_INVALID_PARAMETER); |
707 | handle = dev_freefilehandle(); |
708 | CHK_ERR_CLEANUP(FSERR_GENERIC); |
709 | if (dev_fstatus(handle)) { |
710 | v_free(&file_name); |
711 | rt_raise(FSERR_GENERIC); |
712 | return; |
713 | } |
714 | |
715 | int success = dev_fopen(handle, file_name.v.p.ptr, flags); |
716 | v_free(&file_name); |
717 | CHK_ERR(FSERR_GENERIC); |
718 | if (!success) { |
719 | return; |
720 | } |
721 | } |
722 | |
723 | if (var_p->type == V_ARRAY) { |
724 | // parameter is an array |
725 | for (int i = 0; i < v_asize(array_p); i++) { |
726 | var_p = v_elem(array_p, i); |
727 | fprint_var(handle, var_p); |
728 | dev_fwrite(handle, (byte *)OS_LINESEPARATOR, OS_LINESEPARATOR_LEN); |
729 | } |
730 | } else { |
731 | // parameter is an string |
732 | fprint_var(handle, var_p); |
733 | } |
734 | |
735 | if (flags == DEV_FILE_OUTPUT) { |
736 | dev_fclose(handle); |
737 | } |
738 | } |
739 | |
740 | /* |
741 | * TODO: lock a record or an area |
742 | * |
743 | * LOCK #1, [record]|[start TO end] |
744 | */ |
745 | void cmd_flock() { |
746 | var_t str; |
747 | |
748 | par_getstr(&str); |
749 | if (prog_error) { |
750 | return; |
751 | } |
752 | v_free(&str); |
753 | } |
754 | |
755 | /* |
756 | * CHMOD file, mode |
757 | */ |
758 | void cmd_chmod() { |
759 | var_t str; |
760 | |
761 | par_getstr(&str); |
762 | if (prog_error) { |
763 | return; |
764 | } |
765 | par_getcomma(); |
766 | if (prog_error) { |
767 | v_free(&str); |
768 | return; |
769 | } |
770 | uint32_t mode = par_getint(); |
771 | if (prog_error) { |
772 | v_free(&str); |
773 | return; |
774 | } |
775 | |
776 | chmod(str.v.p.ptr, mode); |
777 | v_free(&str); |
778 | } |
779 | |
780 | void join_path(char *path, char *ext) { |
781 | int len = strlen(path); |
782 | if (path[len - 1] != OS_DIRSEP) { |
783 | if (ext[0] != OS_DIRSEP) { |
784 | strcat(path, "/" ); |
785 | strcat(path, ext); |
786 | } else { |
787 | strcat(path, ext); |
788 | } |
789 | } else { |
790 | if (ext[0] != OS_DIRSEP) { |
791 | strcat(path, ext); |
792 | } else { |
793 | strcat(path, ext + 1); |
794 | } |
795 | } |
796 | } |
797 | |
798 | /* |
799 | * walk on dirs |
800 | */ |
801 | void dirwalk(char *dir, char *wc, bcip_t use_ip, int depth) { |
802 | char path[OS_PATHNAME_SIZE]; |
803 | path[0] = '\0'; |
804 | if (dir[0] == '.') { |
805 | getcwd(path, OS_PATHNAME_SIZE - 1); |
806 | join_path(path, ++dir); |
807 | dir = path; |
808 | } else if (dir[0] == '~') { |
809 | const char *home = getenv("HOME" ); |
810 | if (home != NULL) { |
811 | strlcpy(path, home, sizeof(path)); |
812 | join_path(path, ++dir); |
813 | dir = path; |
814 | } |
815 | } |
816 | |
817 | DIR *dfd = opendir(dir); |
818 | if (dfd == NULL) { |
819 | log_printf(ERR_DIRWALK_CANT_OPEN, dir); |
820 | return; |
821 | } |
822 | |
823 | struct dirent *dp; |
824 | while ((dp = readdir(dfd)) != NULL) { |
825 | if (dev_events(0) != 0) { |
826 | break; |
827 | } |
828 | if (strcmp(dp->d_name, "." ) == 0 || strcmp(dp->d_name, ".." ) == 0) { |
829 | // skip self and parent |
830 | continue; |
831 | } |
832 | if (strlen(dir) + strlen(dp->d_name) + 2 > OS_PATHNAME_SIZE) { |
833 | rt_raise(ERR_DIRWALK_NAME, dir, dp->d_name); |
834 | } else { |
835 | // check filename |
836 | int callusr; |
837 | int contf = 1; |
838 | struct stat st; |
839 | |
840 | if (!wc) { |
841 | if (code_peek() == kwTYPE_EOC) { |
842 | rt_raise(ERR_DIRWALK_MISSING_USE); |
843 | break; |
844 | } |
845 | callusr = 1; |
846 | } else { |
847 | callusr = wc_match(wc, dp->d_name); |
848 | } |
849 | |
850 | char name[OS_PATHNAME_SIZE]; |
851 | strcpy(name, dir); |
852 | join_path(name, dp->d_name); |
853 | |
854 | if (callusr) { |
855 | // call user's function |
856 | var_t *var = v_new(); |
857 | map_init(var); |
858 | v_setstr(map_add_var(var, "path" , 0), dir); |
859 | v_setstr(map_add_var(var, "name" , 0), dp->d_name); |
860 | map_add_var(var, "depth" , depth); |
861 | if (stat(name, &st) != -1) { |
862 | map_add_var(var, "mtime" , st.st_mtime); |
863 | map_add_var(var, "size" , st.st_size); |
864 | map_add_var(var, "dir" , (st.st_mode & S_IFDIR) ? 1 : 0); |
865 | } |
866 | exec_usefunc(var, use_ip); |
867 | contf = v_getint(var); |
868 | v_free(var); |
869 | v_detach(var); |
870 | } |
871 | if (!contf) { |
872 | break; |
873 | } |
874 | |
875 | // proceed to the next |
876 | if (access(name, R_OK) == 0) { |
877 | // user-func, possible it is deleted |
878 | if (stat(name, &st) == 0 && st.st_mode & S_IFDIR) { |
879 | dirwalk(name, wc, use_ip, depth + 1); |
880 | } |
881 | } |
882 | } |
883 | } |
884 | closedir(dfd); |
885 | } |
886 | |
887 | /* |
888 | * walking on directories |
889 | * |
890 | * DIRWALK "/home" [, "*"] USE MYPRN(x) |
891 | */ |
892 | void cmd_dirwalk() { |
893 | char *dir = NULL, *wc = NULL; |
894 | |
895 | par_massget("Ss" , &dir, &wc); |
896 | if (!prog_error) { |
897 | bcip_t use_ip, exit_ip; |
898 | |
899 | // USE |
900 | if (code_peek() == kwUSE) { |
901 | code_skipnext(); |
902 | use_ip = code_getaddr(); |
903 | exit_ip = code_getaddr(); |
904 | } else { |
905 | use_ip = exit_ip = INVALID_ADDR; |
906 | } |
907 | dirwalk(dir, wc, use_ip, 0); |
908 | |
909 | if (exit_ip != INVALID_ADDR) { |
910 | code_jump(exit_ip); |
911 | } |
912 | } |
913 | |
914 | pfree2(dir, wc); |
915 | } |
916 | |
917 | /* |
918 | * write a byte to a stream |
919 | * |
920 | * BPUTC #file, byte |
921 | */ |
922 | void cmd_bputc() { |
923 | // file handle |
924 | par_getsharp(); |
925 | if (!prog_error) { |
926 | int handle = par_getint(); |
927 | if (prog_error) { |
928 | return; |
929 | } |
930 | // allow commas |
931 | par_getsep(); |
932 | if (prog_error) { |
933 | return; |
934 | } |
935 | if (dev_fstatus(handle)) { |
936 | // get variable's ptr |
937 | var_t *var_p = par_getvar_ptr(); |
938 | if (prog_error) { |
939 | return; |
940 | } |
941 | byte code = v_getint(var_p); |
942 | dev_fwrite(handle, &code, 1); |
943 | if (prog_error) { |
944 | return; |
945 | } |
946 | } |
947 | } |
948 | } |
949 | |
950 | /* |
951 | * load from file to a memory address |
952 | * |
953 | * BLOAD file[, offset] |
954 | */ |
955 | void cmd_bload() { |
956 | var_int_t ofs = -1; |
957 | char *fname = NULL; |
958 | |
959 | par_massget("Si" , &fname, &ofs); |
960 | if (!prog_error) { |
961 | int flags = DEV_FILE_INPUT; |
962 | int handle = dev_freefilehandle(); |
963 | var_int_t len; |
964 | |
965 | if (!prog_error) { |
966 | if (dev_fstatus(handle) == 0) { |
967 | dev_fopen(handle, fname, flags); |
968 | if (!prog_error) { |
969 | var_int_t *data; |
970 | var_int_t idata; |
971 | |
972 | dev_fread(handle, (byte *)&idata, sizeof(idata)); |
973 | if (ofs == -1) { |
974 | ofs = idata; |
975 | } |
976 | dev_fread(handle, (byte *)&len, sizeof(len)); |
977 | data = &ofs; |
978 | dev_fread(handle, (byte *)data, len); |
979 | dev_fclose(handle); |
980 | } |
981 | } |
982 | } |
983 | } |
984 | |
985 | pfree(fname); |
986 | } |
987 | |
988 | /* |
989 | * save memory contents to a file |
990 | * |
991 | * BSAVE file, offset, length |
992 | */ |
993 | void cmd_bsave() { |
994 | var_int_t ofs = 0, len = 0; |
995 | char *fname = NULL; |
996 | |
997 | par_massget("SII" , &fname, &ofs, &len); |
998 | if (!prog_error) { |
999 | int flags = DEV_FILE_OUTPUT; |
1000 | int handle = dev_freefilehandle(); |
1001 | if (!prog_error) { |
1002 | if (dev_fstatus(handle) == 0) { |
1003 | dev_fopen(handle, fname, flags); |
1004 | if (!prog_error) { |
1005 | dev_fwrite(handle, (byte *)&ofs, sizeof(ofs)); |
1006 | dev_fwrite(handle, (byte *)&len, sizeof(len)); |
1007 | var_int_t *data = &ofs; |
1008 | dev_fwrite(handle, (byte *)data, len); |
1009 | dev_fclose(handle); |
1010 | } |
1011 | } |
1012 | } |
1013 | } |
1014 | |
1015 | pfree(fname); |
1016 | } |
1017 | |
1018 | |