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
27struct 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 */
37void 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 */
94void 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 */
112void 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 */
136void 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 */
181int 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 */
233void 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 */
291void 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
336void 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 */
380void 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 */
419void 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 */
437void 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 */
474void 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 */
490void 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 */
506void 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 */
525void 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 */
683void 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 */
745void 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 */
758void 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
780void 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 */
801void 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 */
892void 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 */
922void 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 */
955void 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 */
993void 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