]> git.sur5r.net Git - cc65/blob - testcode/lib/tinyshell.c
Merge pull request #849 from polluks/patch-4
[cc65] / testcode / lib / tinyshell.c
1 /*
2 ** Simple ("tiny") shell to test filename and directory functions.
3 ** Copyright (c) 2013,2016 Christian Groessler, chris@groessler.org
4 */
5
6 #define VERSION_ASC "0.91"
7
8 #ifdef __ATARI__
9 #define UPPERCASE      /* define (e.g. for Atari) to convert filenames etc. to upper case */
10 #define HAVE_SUBDIRS
11 #endif
12
13 #ifdef __APPLE2__
14 #define HAVE_SUBDIRS
15 #endif
16
17 #ifdef __CC65__
18 #define CHECK_SP
19 #endif
20
21 #define KEYB_BUFSZ 127
22 #define PROMPT ">>> "
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #ifndef __CC65__
30 #include <sys/stat.h>
31 #include <sys/param.h>
32 #define HAVE_SUBDIRS
33 #else
34 #define MAXPATHLEN 64
35 #endif
36 #include <sys/types.h>
37 #include <fcntl.h>
38 #include <dirent.h>
39
40 #ifdef CHECK_SP
41 extern unsigned int getsp(void);  /* comes from getsp.s */
42 #endif
43
44 #define CMD_NOTHING 0
45 #define CMD_INVALID 1
46 #define CMD_HELP    2
47 #define CMD_QUIT    3
48 #define CMD_LS      4
49 #define CMD_MKDIR   5
50 #define CMD_RMDIR   6
51 #define CMD_CHDIR   7
52 #define CMD_RM      8
53 #define CMD_RENAME  9
54 #define CMD_COPY    10
55 #define CMD_PWD     11
56 #define CMD_CLS     12
57 #define CMD_VERBOSE 13
58 #define CMD_EXEC    14
59
60 static unsigned char verbose;
61 static unsigned char terminate;
62 static unsigned char cmd;
63 static unsigned char *cmd_asc, *arg1, *arg2, *arg3, *args;  /* 'args': everything after command */
64 static unsigned char keyb_buf[KEYB_BUFSZ + 1];
65 static unsigned char keyb_buf2[KEYB_BUFSZ + 1];
66 static size_t cpbuf_sz = 4096;
67
68 struct cmd_table {
69     unsigned char *name;
70     unsigned char code;
71 } cmd_table[] = {
72     { "help",  CMD_HELP },
73     { "quit",  CMD_QUIT },
74     { "q",     CMD_QUIT },
75     { "exit",  CMD_QUIT },
76     { "ls",    CMD_LS },
77     { "dir",   CMD_LS },
78     { "md",    CMD_MKDIR },
79 #ifdef HAVE_SUBDIRS
80     { "mkdir", CMD_MKDIR },
81     { "rd",    CMD_RMDIR },
82     { "rmdir", CMD_RMDIR },
83     { "cd",    CMD_CHDIR },
84     { "chdir", CMD_CHDIR },
85 #endif
86     { "rm",    CMD_RM },
87     { "del",   CMD_RM },
88     { "cp",    CMD_COPY },
89     { "copy",  CMD_COPY },
90     { "mv",    CMD_RENAME },
91     { "ren",   CMD_RENAME },
92     { "pwd",   CMD_PWD },
93     { "exec",  CMD_EXEC },
94 #ifdef __ATARI__
95     { "cls",   CMD_CLS },
96 #endif
97     { "verbose", CMD_VERBOSE },
98     { NULL, 0 }
99 };
100
101 static void banner(void)
102 {
103     puts("\"tiny\" command line shell, v" VERSION_ASC);
104     puts("written by chris@groessler.org");
105     puts("type 'help' for help\n");
106 }
107
108 static void get_command(void)
109 {
110     unsigned char i = 0;
111
112 #ifdef CHECK_SP
113     static char firstcall = 1;
114     static unsigned int good_sp;
115     unsigned int sp;
116     if (firstcall)
117         sp = good_sp = getsp();
118     else
119         sp = getsp();
120
121     if (sp != good_sp) {
122         printf("SP: 0x%04X  ***MISMATCH*** 0x%04X\n", sp, good_sp);
123     }
124     else if (verbose)
125         printf("SP: 0x%04X\n", sp);
126 #endif
127
128     arg1 = arg2 = arg3 = NULL;
129
130     /* issue prompt */
131     printf(PROMPT);
132
133     /* get input from the user */
134     if (! fgets(keyb_buf, KEYB_BUFSZ, stdin)) {
135         puts("");
136         cmd = CMD_QUIT;
137         return;
138     }
139
140     /* put everything after first string into 'args' */
141
142     strcpy(keyb_buf2, keyb_buf);  /* use a backup copy for 'args' */
143
144     /* skip over the first non-whitespace item */
145     cmd_asc = strtok(keyb_buf2, " \t\n");
146     if (cmd_asc)
147         args = strtok(NULL, "");  /* get everything */
148     else
149         *args = 0;  /* no arguments */
150
151     /* split input into cmd, arg1, arg2, arg3 */
152
153     /* get and parse command */
154     cmd_asc = strtok(keyb_buf, " \t\n");
155     if (! cmd_asc) {
156         cmd = CMD_NOTHING;
157         return;
158     }
159     cmd = CMD_INVALID;
160     while (cmd_table[i].name) {
161         if (! strcmp(cmd_table[i].name, cmd_asc)) {
162             cmd = cmd_table[i].code;
163             break;
164         }
165         i++;
166     }
167
168     /* get arguments */
169     arg1 = strtok(NULL, " \t\n");
170     if (! arg1)
171         return;
172     arg2 = strtok(NULL, " \t\n");
173     if (! arg2)
174         return;
175     arg3 = strtok(NULL, " \t\n");
176 }
177
178 static void cmd_help(void)
179 {
180     puts("quit, exit -  exit shell");
181     puts("ls, dir    -  display current directory");
182     puts("              and drive contents");
183     puts("rm, del    -  delete file");
184     puts("cp, copy   -  copy file");
185     puts("mv, ren    -  rename file");
186     puts("cd, chdir  -  change directory or drive");
187     puts("md, mkdir  -  make directory or drive");
188     puts("rd, rmdir  -  remove directory or drive");
189     puts("exec       -  run program");
190 #ifdef __ATARI__
191     puts("cls        -  clear screen");
192 #endif
193     puts("verbose    -  set verbosity level");
194 }
195
196 static void cmd_ls(void)
197 {
198     DIR *dir;
199     unsigned char *arg;
200     struct dirent *dirent;
201 #ifdef __ATARI__
202     char need_free = 0;
203 #endif
204
205     if (arg2) {
206         puts("usage: ls [dir]");
207         return;
208     }
209
210     /* print directory listing */
211     if (arg1) {
212 #ifdef UPPERCASE
213         strupr(arg1);
214 #endif
215 #ifdef __ATARI__
216         /* not sure if this shouldn't be done by the runtime lib */
217         if (*(arg1 + strlen(arg1) - 1) == ':' || *(arg1 + strlen(arg1) - 1) == '>') {
218             arg = malloc(strlen(arg1) + 4);
219             if (! arg) {
220                 printf("malloc failed: %s", strerror(errno));
221                 return;
222             }
223             need_free = 1;
224             memcpy(arg, arg1, strlen(arg1) + 1);
225             strcat(arg, "*.*");
226         }
227         else
228 #endif
229             arg = arg1;
230     }
231     else
232         arg = ".";
233
234     if (verbose)
235         printf("Buffer addr: %p\n", arg);
236     dir = opendir(arg);
237 #ifdef __ATARI__
238     if (need_free) free(arg);
239 #endif
240     if (! dir) {
241         puts("opendir failed");
242         return;
243     }
244
245     while (dirent = readdir(dir))
246         puts(dirent->d_name);
247
248     closedir(dir);
249 }
250
251 static void cmd_rm(void)
252 {
253     if (!arg1 || arg2) {
254         puts("usage: rm <file>");
255         return;
256     }
257
258 #ifdef UPPERCASE
259     strupr(arg1);
260 #endif
261
262     if (unlink(arg1))
263         printf("remove failed: %s\n", strerror(errno));
264 }
265
266 #ifdef HAVE_SUBDIRS
267
268 static void cmd_mkdir(void)
269 {
270     if (!arg1 || arg2) {
271         puts("usage: mkdir <dir>");
272         return;
273     }
274
275 #ifdef UPPERCASE
276     strupr(arg1);
277 #endif
278
279     if (mkdir(arg1, 0777))
280         printf("mkdir failed: %s\n", strerror(errno));
281 }
282
283 static void cmd_rmdir(void)
284 {
285     if (!arg1 || arg2) {
286         puts("usage: rmdir <dir>");
287         return;
288     }
289
290 #ifdef UPPERCASE
291     strupr(arg1);
292 #endif
293
294     if (rmdir(arg1))
295         printf("rmdir failed: %s\n", strerror(errno));
296 }
297
298 static void cmd_chdir(void)
299 {
300     if (!arg1 || arg2) {
301         puts("usage: cddir <dir>");
302         return;
303     }
304
305 #ifdef UPPERCASE
306     strupr(arg1);
307 #endif
308
309     if (chdir(arg1))
310         printf("chdir failed: %s\n", strerror(errno));
311 }
312
313 static void cmd_pwd(void)
314 {
315     char *buf;
316
317     if (arg1) {
318         puts("usage: pwd");
319         return;
320     }
321
322     buf = malloc(MAXPATHLEN);
323     if (! buf) {
324         printf("malloc %u bytes failed: %s\n", MAXPATHLEN, strerror(errno));
325         return;
326     }
327     if (verbose)
328         printf("Buffer addr: %p\n", buf);
329     if (!getcwd(buf, MAXPATHLEN)) {
330         printf("getcwd failed: %s\n", strerror(errno));
331         free(buf);
332         return;
333     }
334
335     puts(buf);
336     free(buf);
337 }
338
339 #endif /* #ifdef HAVE_SUBDIRS */
340
341 static void cmd_rename(void)
342 {
343     if (!arg2 || arg3) {
344         puts("usage: mv <oldname> <newname>");
345         return;
346     }
347
348 #ifdef UPPERCASE
349     strupr(arg1);
350     strupr(arg2);
351 #endif
352
353     if (rename(arg1, arg2))
354         printf("rename failed: %s\n", strerror(errno));
355 }
356
357 static void cmd_exec(void)
358 {
359     unsigned char *progname, *arguments;
360
361     progname = strtok(args, " \t\n");
362     if (! progname) {
363         puts("usage: exec <progname> [arguments]");
364         return;
365     }
366     arguments = strtok(NULL, "");
367
368     /*printf("exec: %s %s\n", progname, arguments ? arguments : "");*/
369     (void)exec(progname, arguments);
370     printf("exec error: %s\n", strerror(errno));
371 }
372
373 static void cmd_copy(void)
374 {
375     int srcfd = -1, dstfd = -1;
376     unsigned char *buf;
377     int readsz, writesz;
378
379     if (!arg2 || arg3) {
380         puts("usage: cp <src> <dest>");
381         return;
382     }
383
384 #ifdef UPPERCASE
385     strupr(arg1);
386     strupr(arg2);
387 #endif
388
389     buf = malloc(cpbuf_sz);
390     if (! buf) {
391         printf("malloc %u bytes failed: %s\n", cpbuf_sz, strerror(errno));
392         return;
393     }
394     if (verbose)
395         printf("Buffer addr: %p\n", buf);
396
397     while (1) {
398         if (srcfd == -1) {
399             srcfd = open(arg1, O_RDONLY);
400             if (srcfd < 0) {
401                 printf("open(%s) failed: %s\n", arg1, strerror(errno));
402                 break;
403             }
404         }
405
406         readsz = read(srcfd, buf, cpbuf_sz);
407         if (readsz < 0) {
408             printf("read error: %s\n", strerror(errno));
409             break;
410         }
411         if (! readsz)
412             break;
413
414         if (dstfd == -1) {
415             dstfd = open(arg2, O_WRONLY | O_CREAT | O_TRUNC, 0777);
416             if (dstfd < 0) {
417                 printf("open(%s) failed: %s\n", arg2, strerror(errno));
418                 break;
419             }
420         }
421
422         writesz = write(dstfd, buf, readsz);
423         if (writesz < 0 || writesz != readsz) {
424             printf("write error: %s\n", strerror(errno));
425             break;
426         }
427         if (readsz != cpbuf_sz)
428             break;
429     }
430
431     free(buf);
432     if (srcfd >= 0) close(srcfd);
433     if (dstfd >= 0) close(dstfd);
434 }
435
436 #ifdef __ATARI__
437 static void cmd_cls(void)
438 {
439     printf("\f");
440 }
441 #endif
442
443 static void cmd_verbose(void)
444 {
445     unsigned long verb;
446     char *endptr;
447
448     if (!arg1 || arg2) {
449         puts("usage: verbose <level>");
450         return;
451     }
452
453     verb = strtoul(arg1, &endptr, 10);
454     if (verb > 255 || *endptr) {
455         puts("invalid verbosity level");
456         return;
457     }
458
459     verbose = verb;
460     printf("verbosity level set to %d\n", verbose);
461 }
462
463 static void run_command(void)
464 {
465     switch (cmd) {
466         default: puts("internal error"); return;
467         case CMD_NOTHING: return;
468         case CMD_INVALID: puts("invalid command"); return;
469         case CMD_HELP: cmd_help(); return;
470         case CMD_QUIT: terminate = 1; return;
471         case CMD_LS: cmd_ls(); return;
472         case CMD_RM: cmd_rm(); return;
473 #ifdef HAVE_SUBDIRS
474         case CMD_CHDIR: cmd_chdir(); return;
475         case CMD_MKDIR: cmd_mkdir(); return;
476         case CMD_RMDIR: cmd_rmdir(); return;
477         case CMD_PWD: cmd_pwd(); return;
478 #endif
479         case CMD_EXEC: cmd_exec(); return;
480         case CMD_RENAME: cmd_rename(); return;
481         case CMD_COPY: cmd_copy(); return;
482 #ifdef __ATARI__
483         case CMD_CLS: cmd_cls(); return;
484 #endif
485         case CMD_VERBOSE: cmd_verbose(); return;
486     }
487 }
488
489 int main(void)
490 {
491     banner();
492
493     while (! terminate) {
494         get_command();
495         run_command();
496     }
497     return 0;
498 }
499
500 /* Local Variables: */
501 /* c-file-style: "cpg" */
502 /* c-basic-offset: 4 */
503 /* End: */