rosh: Remove unneeded cases in rosh_command() switch statements
[profile/ivi/syslinux.git] / com32 / rosh / rosh.c
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 2008-2010 Gene Cumm - All Rights Reserved
4  *
5  *   This program is free software; you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
8  *   Boston MA 02111-1307, USA; either version 2 of the License, or
9  *   (at your option) any later version; incorporated herein by reference.
10  *
11  * ----------------------------------------------------------------------- */
12
13 /*
14  * rosh.c
15  *
16  * Read-Only shell; Simple shell system designed for SYSLINUX-derivitives.
17  * Provides minimal commands utilizing the console via stdout/stderr as the
18  * sole output devices.  Designed to compile for Linux for testing/debugging.
19  */
20
21 /*
22  * ToDos:
23  * prompt:      Allow left/right arrow, home/end and more?
24  * commands     Break into argv/argc-like array
25  * rosh_cfg:    allow -s <file> to change config
26  * rosh_ls():   sorted; then multiple columns
27  */
28
29 /*#define DO_DEBUG 1
30 //*/
31 /* Uncomment the above line for debugging output; Comment to remove */
32 /*#define DO_DEBUG2 1
33 //*/
34 /* Uncomment the above line for super-debugging output; Must have regular
35  * debugging enabled; Comment to remove.
36  */
37 #include "rosh.h"
38 #include "../../version.h"
39
40 #define APP_LONGNAME    "Read-Only Shell"
41 #define APP_NAME        "rosh"
42 #define APP_AUTHOR      "Gene Cumm"
43 #define APP_YEAR        "2010"
44 #define APP_VER         "beta-b085"
45
46 /* Print version information to stdout
47  */
48 void rosh_version(int vtype)
49 {
50     char env[256];
51     env[0] = 0;
52     printf("%s v %s; (c) %s %s.\n\tFrom Syslinux %s, %s\n", APP_LONGNAME, APP_VER, APP_YEAR, APP_AUTHOR, VERSION_STR, DATE);
53     switch (vtype) {
54     case 1:
55         rosh_get_env_ver(env, 256);
56         printf("\tRunning on %s\n", env);
57     }
58 }
59
60 /* Print beta message and if DO_DEBUG/DO_DEBUG2 are active
61  */
62 void print_beta(void)
63 {
64     puts(rosh_beta_str);
65     ROSH_DEBUG("DO_DEBUG active\n");
66     ROSH_DEBUG2("DO_DEBUG2 active\n");
67 }
68
69 /* Search a string for first non-space (' ') character, starting at ipos
70  *      istr    input string to parse
71  *      ipos    input position to start at
72  */
73 int rosh_search_nonsp(const char *istr, const int ipos)
74 {
75     int curpos;
76     char c;
77
78     curpos = ipos;
79     c = istr[curpos];
80     while (c && isspace(c))
81         c = istr[++curpos];
82     return curpos;
83 }
84
85 /* Search a string for space (' '), returning the position of the next space
86  * or the '\0' at end of string
87  *      istr    input string to parse
88  *      ipos    input position to start at
89  */
90 int rosh_search_sp(const char *istr, const int ipos)
91 {
92     int curpos;
93     char c;
94
95     curpos = ipos;
96     c = istr[curpos];
97     while (c && !(isspace(c)))
98         c = istr[++curpos];
99     return curpos;
100 }
101
102 /* Parse a string for the first non-space string, returning the end position
103  * from src
104  *      dest    string to contain the first non-space string
105  *      src     string to parse
106  *      ipos    Position to start in src
107  */
108 int rosh_parse_sp_1(char *dest, const char *src, const int ipos)
109 {
110     int bpos, epos;             /* beginning and ending position of source string
111                                    to copy to destination string */
112
113     bpos = 0;
114     epos = 0;
115 /* //HERE-error condition checking */
116     bpos = rosh_search_nonsp(src, ipos);
117     epos = rosh_search_sp(src, bpos);
118     if (epos > bpos) {
119         memcpy(dest, src + bpos, epos - bpos);
120         if (dest[epos - bpos] != 0)
121             dest[epos - bpos] = 0;
122     } else {
123         epos = strlen(src);
124         dest[0] = 0;
125     }
126     return epos;
127 }
128
129 /*
130  * parse_args1: Try 1 at parsing a string to an argc/argv pair.  use free_args1 to free memory malloc'd
131  *
132  * Derived from com32/lib/sys/argv.c:__parse_argv()
133  *   Copyright 2004-2009 H. Peter Anvin - All Rights Reserved
134  *   Copyright 2009 Intel Corporation; author: H. Peter Anvin
135  */
136 int parse_args1(char ***iargv, const char *istr)
137 {
138     int argc  = 0;
139     const char *p;
140     char *q, *r, *args, **arg;
141     int sp = 1; //, qt = 0;             /* Was a space; inside a quote */
142
143     /* Scan 1: Length */
144     /* I could eliminate this if I knew a max length, like strncpy() */
145     int len = strlen(istr);
146
147     /* Scan 2: Copy, nullify and make argc */
148     if (!(args = malloc(len + 1)))
149         goto fail_args;
150     q = args;
151     for (p = istr;; p++) {
152         if (*p <= ' ') {
153             if (!sp) {
154                 sp = 1;
155                 *q++ = '\0';
156             }
157         } else {
158             if (sp) {
159                 argc++;
160                 sp = 0;
161             }
162             *q++ = *p;
163         }
164         if (!*p)
165             break;
166     }
167
168     q--;                        /* Point q to final null */
169     /* Scan 3: Build array of pointers */
170     if (!(*iargv = malloc((argc + 1) * sizeof(char *))))
171         goto fail_args_ptr;
172     arg = *iargv;
173     arg[argc] = NULL;           /* Nullify the last pointer */
174     if (*args != '\0')
175             *arg++ = args;
176     for (r = args; r < q ; r++) {
177         if (*r == '\0') {
178             *arg++ = r + 1;
179         }
180     }
181
182 fail_args:
183     return argc;
184 fail_args_ptr:
185     free(args);
186     return 0;
187 }
188
189 /* Free argv created by parse_args1()
190  *      argv    Argument Values
191  */
192 void free_args1(char ***argv)
193 {
194     char *s;
195     s = **argv;
196     free(*argv);
197     free(s);
198 }
199
200 /* Convert a string to an argc/argv pair
201  *      str     String to parse
202  *      argv    Argument Values
203  *      returns Argument Count
204  */
205 int rosh_str2argv(char ***argv, const char *str)
206 {
207     return parse_args1(argv, str);
208 }
209
210 /* Free an argv created by rosh_str2argv()
211  *      argv    Argument Values to free
212  */
213 void rosh_free_argv(char ***argv)
214 {
215      free_args1(argv);
216 }
217
218 /* Print the contents of an argc/argv pair
219  *      argc    Argument Count
220  *      argv    Argument Values
221  */
222 void rosh_pr_argv(int argc, char *argv[])
223 {
224     int i;
225     for (i = 0; i < argc; i++) {
226         printf("%s%s", argv[i], (i < argc)? " " : "");
227     }
228     puts("");
229 }
230
231 /* Print the contents of an argc/argv pair verbosely
232  *      argc    Argument Count
233  *      argv    Argument Values
234  */
235 void rosh_pr_argv_v(int argc, char *argv[])
236 {
237     int i;
238     for (i = 0; i < argc; i++) {
239         printf("%4d '%s'\n", i, argv[i]);
240     }
241 }
242
243 /* Display help
244  *      type    Help type
245  *      cmdstr  Command for which help is requested
246  */
247 void rosh_help(int type, const char *cmdstr)
248 {
249     switch (type) {
250     case 2:
251         if ((cmdstr == NULL) || (strcmp(cmdstr, "") == 0)) {
252             rosh_version(0);
253             puts(rosh_help_str2);
254         } else {
255             switch (cmdstr[0]) {
256             case 'c':
257                 puts(rosh_help_cd_str);
258                 break;
259             case 'l':
260                 puts(rosh_help_ls_str);
261                 break;
262             default:
263                 printf(rosh_help_str_adv, cmdstr);
264             }
265         }
266         break;
267     case 1:
268     default:
269         rosh_version(0);
270         puts(rosh_help_str1);
271     }
272 }
273
274 /* Handle most/all errors
275  *      ierrno  Input Error number
276  *      cmdstr  Command being executed to cause error
277  *      filestr File/parameter causing error
278  */
279 void rosh_error(const int ierrno, const char *cmdstr, const char *filestr)
280 {
281     printf("--ERROR: %s '%s': ", cmdstr, filestr);
282     switch (ierrno) {
283     case 0:
284         puts("NO ERROR");
285         break;
286     case ENOENT:
287         puts("not found");
288         /* SYSLinux-3.72 COM32 API returns this for a
289            directory or empty file */
290         ROSH_COM32("  (COM32) could be a directory or empty file\n");
291         break;
292     case EIO:
293         puts("I/O Error");
294         break;
295     case EBADF:
296         puts("Bad File Descriptor");
297         break;
298     case EACCES:
299         puts("Access DENIED");
300         break;
301     case ENOTDIR:
302         puts("not a directory");
303         ROSH_COM32("  (COM32) could be directory\n");
304         break;
305     case EISDIR:
306         puts("IS a directory");
307         break;
308     case ENOSYS:
309         puts("not implemented");
310         break;
311     default:
312         printf("returns error; errno=%d\n", ierrno);
313     }
314 }                               /* rosh_error */
315
316 /* Concatenate command line arguments into one string
317  *      cmdstr  Output command string
318  *      argc    Argument Count
319  *      argv    Argument Values
320  *      barg    Beginning Argument
321  */
322 int rosh_argcat(char *cmdstr, const int argc, char *argv[], const int barg)
323 {
324     int i, arglen, curpos;      /* index, argument length, current position
325                                    in cmdstr */
326     curpos = 0;
327     cmdstr[0] = '\0';           /* Nullify string just to be sure */
328     for (i = barg; i < argc; i++) {
329         arglen = strlen(argv[i]);
330         /* Theoretically, this should never be met in SYSLINUX */
331         if ((curpos + arglen) > (ROSH_CMD_SZ - 1))
332             arglen = (ROSH_CMD_SZ - 1) - curpos;
333         memcpy(cmdstr + curpos, argv[i], arglen);
334         curpos += arglen;
335         if (curpos >= (ROSH_CMD_SZ - 1)) {
336             /* Hopefully, curpos should not be greater than
337                (ROSH_CMD_SZ - 1) */
338             /* Still need a '\0' at the last character */
339             cmdstr[(ROSH_CMD_SZ - 1)] = 0;
340             break;              /* Escape out of the for() loop;
341                                    We can no longer process anything more */
342         } else {
343             cmdstr[curpos] = ' ';
344             curpos += 1;
345             cmdstr[curpos] = 0;
346         }
347     }
348     /* If there's a ' ' at the end, remove it.  This is normal unless
349        the maximum length is met/exceeded. */
350     if (cmdstr[curpos - 1] == ' ')
351         cmdstr[--curpos] = 0;
352     return curpos;
353 }                               /* rosh_argcat */
354
355 /*
356  * Prints a lot of the data in a struct termios
357  */
358 /*
359 void rosh_print_tc(struct termios *tio)
360 {
361         printf("  -- termios: ");
362         printf(".c_iflag=%04X ", tio->c_iflag);
363         printf(".c_oflag=%04X ", tio->c_oflag);
364         printf(".c_cflag=%04X ", tio->c_cflag);
365         printf(".c_lflag=%04X ", tio->c_lflag);
366         printf(".c_cc[VTIME]='%d' ", tio->c_cc[VTIME]);
367         printf(".c_cc[VMIN]='%d'", tio->c_cc[VMIN]);
368         printf("\n");
369 }
370 */
371
372 /*
373  * Attempts to get a single key from the console
374  *      returns key pressed
375  */
376 int rosh_getkey(void)
377 {
378     int inc;
379
380     inc = KEY_NONE;
381     while (inc == KEY_NONE)
382         inc = get_key(stdin, 6000);
383     return inc;
384 }                               /* rosh_getkey */
385
386 /*
387  * Qualifies a filename relative to the working directory
388  *      filestr Filename to qualify
389  *      pwdstr  working directory
390  *      returns qualified file name string
391  */
392 void rosh_qualify_filestr(char *filestr, const char *ifilstr,
393                           const char *pwdstr)
394 {
395     int filepos = 0;
396     if ((filestr) && (pwdstr) && (ifilstr)) {
397         if (ifilstr[0] != SEP) {
398             strcpy(filestr, pwdstr);
399             filepos = strlen(pwdstr);
400             if (filestr[filepos - 1] != SEP)
401                 filestr[filepos++] = SEP;
402         }
403         strcpy(filestr + filepos, ifilstr);
404         ROSH_DEBUG("--'%s'\n", filestr);
405     }
406 }
407
408 /* Concatenate multiple files to stdout
409  *      argc    Argument Count
410  *      argv    Argument Values
411  */
412 void rosh_cat(int argc, char *argv[])
413 {
414     FILE *f;
415     char buf[ROSH_BUF_SZ];
416     int i, numrd;
417
418     for (i = 0; i < argc; i++) {
419         printf("--File = '%s'\n", argv[i]);
420         errno = 0;
421         f = fopen(argv[i], "r");
422         if (f != NULL) {
423             numrd = fread(buf, 1, ROSH_BUF_SZ, f);
424             while (numrd > 0) {
425                 fwrite(buf, 1, numrd, stdout);
426                 numrd = fread(buf, 1, ROSH_BUF_SZ, f);
427             }
428             fclose(f);
429         } else {
430             rosh_error(errno, "cat", argv[i]);
431             errno = 0;
432         }
433     }
434 }                               /* rosh_cat */
435
436 /* Change PWD (Present Working Directory)
437  *      argc    Argument count
438  *      argv    Argument values
439  *      ipwdstr Initial PWD
440  */
441 void rosh_cd(int argc, char *argv[], const char *ipwdstr)
442 {
443     int rv = 0;
444 #ifdef DO_DEBUG
445     char filestr[ROSH_PATH_SZ];
446 #endif /* DO_DEBUG */
447     ROSH_DEBUG("CMD: \n");
448     ROSH_DEBUG_ARGV_V(argc, argv);
449     errno = 0;
450     if (argc == 2)
451         rv = chdir(argv[1]);
452     else if (argc == 1)
453         rv = chdir(ipwdstr);
454     else
455         rosh_help(2, argv[0]);
456     if (rv != 0) {
457         if (argc == 2)
458             rosh_error(errno, "cd", argv[1]);
459         else
460             rosh_error(errno, "cd", ipwdstr);
461         errno = 0;
462     } else {
463 #ifdef DO_DEBUG
464         if (getcwd(filestr, ROSH_PATH_SZ))
465             ROSH_DEBUG("  %s\n", filestr);
466 #endif /* DO_DEBUG */
467     }
468 }                               /* rosh_cd */
469
470 /* Print the syslinux config file name
471  */
472 void rosh_cfg(void)
473 {
474     printf("CFG:     '%s'\n", syslinux_config_file());
475 }                               /* rosh_cfg */
476
477 /* Echo a string back to the screen
478  *      cmdstr  command string to process
479  */
480 void rosh_echo(const char *cmdstr)
481 {
482     int bpos = 0;
483     ROSH_DEBUG("CMD: '%s'\n", cmdstr);
484     bpos = rosh_search_nonsp(cmdstr, rosh_search_sp(cmdstr, 0));
485     if (bpos > 1) {
486         ROSH_DEBUG("  bpos=%d\n", bpos);
487         printf("'%s'\n", cmdstr + bpos);
488     } else {
489         puts("");
490     }
491 }                               /* rosh_echo */
492
493 /* Process argc/argv to optarr
494  *      argc    Argument count
495  *      argv    Argument values
496  *      optarr  option array to populate
497  */
498 void rosh_ls_arg_opt(int argc, char *argv[], int *optarr)
499 {
500     int rv = 0;
501
502     optarr[0] = -1;
503     optarr[1] = -1;
504     optarr[2] = -1;
505     while (rv != -1) {
506         rv = getopt(argc, argv, rosh_ls_opt_str);
507         switch (rv) {
508         case 'l':
509             optarr[0] = 1;
510             break;
511         case 'F':
512             optarr[1] = 1;
513             break;
514         case 'i':
515             optarr[2] = 1;
516             break;
517         case '?':
518         default:
519             break;
520         }
521     }
522 }                               /* rosh_ls_arg_opt */
523
524 /* Retrieve the size of a file argument
525  *      filestr directory name of directory entry
526  *      de      directory entry
527  */
528 int rosh_ls_de_size(const char *filestr, struct dirent *de)
529 {
530     int de_size;
531     char filestr2[ROSH_PATH_SZ];
532     int fd2, file2pos;
533     struct stat fdstat;
534     int status;
535
536     filestr2[0] = 0;
537     file2pos = -1;
538     if (filestr) {
539         file2pos = strlen(filestr);
540         memcpy(filestr2, filestr, file2pos);
541         filestr2[file2pos] = '/';
542     }
543     strcpy(filestr2 + file2pos + 1, de->d_name);
544     fd2 = open(filestr2, O_RDONLY);
545     status = fstat(fd2, &fdstat);
546     fd2 = close(fd2);
547     de_size = (int)fdstat.st_size;
548     return de_size;
549 }                               /* rosh_ls_de_size */
550
551 /* Retrieve the size and mode of a file
552  *      filestr directory name of directory entry
553  *      de      directory entry
554  */
555 int rosh_ls_de_size_mode(struct dirent *de, mode_t * st_mode)
556 {
557     int de_size;
558 //    char filestr2[ROSH_PATH_SZ];
559 //     int file2pos;
560     struct stat fdstat;
561     int status;
562
563 /*    filestr2[0] = 0;
564     file2pos = -1;*/
565     fdstat.st_size = 0;
566     fdstat.st_mode = 0;
567 /*    if (filestr) {
568         file2pos = strlen(filestr);
569         memcpy(filestr2, filestr, file2pos);
570         filestr2[file2pos] = '/';
571     }
572     strcpy(filestr2 + file2pos + 1, de->d_name);*/
573     errno = 0;
574     status = stat(de->d_name, &fdstat);
575     ROSH_DEBUG2("\t--stat()=%d\terr=%d\n", status, errno);
576     if (errno) {
577         rosh_error(errno, "ls:szmd.stat", de->d_name);
578         errno = 0;
579     }
580     de_size = (int)fdstat.st_size;
581     *st_mode = fdstat.st_mode;
582     return de_size;
583 }                               /* rosh_ls_de_size_mode */
584
585 /* Returns the Inode number if fdstat contains it
586  *      fdstat  struct to extract inode from if not COM32, for now
587  */
588 long rosh_ls_d_ino(struct stat *fdstat)
589 {
590     long de_ino;
591 #ifdef __COM32__
592     if (fdstat)
593         de_ino = -1;
594     else
595         de_ino = 0;
596 #else /* __COM32__ */
597     de_ino = fdstat->st_ino;
598 #endif /* __COM32__ */
599     return de_ino;
600 }
601
602 /* Convert a d_type to a single char in human readable format
603  *      d_type  d_type to convert
604  *      returns human readable single character; a space if other
605  */
606 char rosh_d_type2char_human(unsigned char d_type)
607 {
608     char ret;
609     switch (d_type) {
610     case DT_UNKNOWN:
611         ret = 'U';
612         break;                  /* Unknown */
613     case DT_FIFO:
614         ret = 'F';
615         break;                  /* FIFO */
616     case DT_CHR:
617         ret = 'C';
618         break;                  /* Char Dev */
619     case DT_DIR:
620         ret = 'D';
621         break;                  /* Directory */
622     case DT_BLK:
623         ret = 'B';
624         break;                  /* Block Dev */
625     case DT_REG:
626         ret = 'R';
627         break;                  /* Regular File */
628     case DT_LNK:
629         ret = 'L';
630         break;                  /* Link, Symbolic */
631     case DT_SOCK:
632         ret = 'S';
633         break;                  /* Socket */
634     case DT_WHT:
635         ret = 'W';
636         break;                  /* UnionFS Whiteout */
637     default:
638         ret = ' ';
639     }
640     return ret;
641 }                               /* rosh_d_type2char_human */
642
643 /* Convert a d_type to a single char by ls's prefix standards for -l
644  *      d_type  d_type to convert
645  *      returns ls style single character; a space if other
646  */
647 char rosh_d_type2char_lspre(unsigned char d_type)
648 {
649     char ret;
650     switch (d_type) {
651     case DT_FIFO:
652         ret = 'p';
653         break;
654     case DT_CHR:
655         ret = 'c';
656         break;
657     case DT_DIR:
658         ret = 'd';
659         break;
660     case DT_BLK:
661         ret = 'b';
662         break;
663     case DT_REG:
664         ret = '-';
665         break;
666     case DT_LNK:
667         ret = 'l';
668         break;
669     case DT_SOCK:
670         ret = 's';
671         break;
672     default:
673         ret = '?';
674     }
675     return ret;
676 }                               /* rosh_d_type2char_lspre */
677
678 /* Convert a d_type to a single char by ls's classify (-F) suffix standards
679  *      d_type  d_type to convert
680  *      returns ls style single character; a space if other
681  */
682 char rosh_d_type2char_lssuf(unsigned char d_type)
683 {
684     char ret;
685     switch (d_type) {
686     case DT_FIFO:
687         ret = '|';
688         break;
689     case DT_DIR:
690         ret = '/';
691         break;
692     case DT_LNK:
693         ret = '@';
694         break;
695     case DT_SOCK:
696         ret = '=';
697         break;
698     default:
699         ret = ' ';
700     }
701     return ret;
702 }                               /* rosh_d_type2char_lssuf */
703
704 /* Converts data in the "other" place of st_mode to a ls-style string
705  *      st_mode Mode in other to analyze
706  *      st_mode_str     string to hold converted string
707  */
708 void rosh_st_mode_am2str(mode_t st_mode, char *st_mode_str)
709 {
710     st_mode_str[0] = ((st_mode & S_IROTH) ? 'r' : '-');
711     st_mode_str[1] = ((st_mode & S_IWOTH) ? 'w' : '-');
712     st_mode_str[2] = ((st_mode & S_IXOTH) ? 'x' : '-');
713 }
714
715 /* Converts st_mode to an ls-style string
716  *      st_mode mode to convert
717  *      st_mode_str     string to hold converted string
718  */
719 void rosh_st_mode2str(mode_t st_mode, char *st_mode_str)
720 {
721     st_mode_str[0] = rosh_d_type2char_lspre(IFTODT(st_mode));
722     rosh_st_mode_am2str((st_mode & S_IRWXU) >> 6, st_mode_str + 1);
723     rosh_st_mode_am2str((st_mode & S_IRWXG) >> 3, st_mode_str + 4);
724     rosh_st_mode_am2str(st_mode & S_IRWXO, st_mode_str + 7);
725     st_mode_str[10] = 0;
726 }                               /* rosh_st_mode2str */
727
728 /* Output a single entry
729  *      filestr directory name to list
730  *      de      directory entry
731  *      optarr  Array of options
732  */
733 void rosh_ls_arg_dir_de(struct dirent *de, const int *optarr)
734 {
735     int de_size;
736     mode_t st_mode;
737     char st_mode_str[11];
738     st_mode = 0;
739     if (optarr[2] > -1)
740         printf("%10d ", (int)de->d_ino);
741     if (optarr[0] > -1) {
742         de_size = rosh_ls_de_size_mode(de, &st_mode);
743         rosh_st_mode2str(st_mode, st_mode_str);
744         ROSH_DEBUG2("%04X ", st_mode);
745         printf("%s %10d ", st_mode_str, de_size);
746     }
747     ROSH_DEBUG("'");
748     printf("%s", de->d_name);
749     ROSH_DEBUG("'");
750     if (optarr[1] > -1)
751         printf("%c", rosh_d_type2char_lssuf(de->d_type));
752     printf("\n");
753 }                               /* rosh_ls_arg_dir_de */
754
755 /* Output listing of a regular directory
756  *      filestr directory name to list
757  *      d       the open DIR
758  *      optarr  Array of options
759         NOTE:This is where I could use qsort
760  */
761 void rosh_ls_arg_dir(const char *filestr, DIR * d, const int *optarr)
762 {
763     struct dirent *de;
764     int filepos;
765
766     filepos = 0;
767     errno = 0;
768     while ((de = readdir(d))) {
769         filepos++;
770         rosh_ls_arg_dir_de(de, optarr);
771     }
772     if (errno) {
773         rosh_error(errno, "ls:arg_dir", filestr);
774         errno = 0;
775     } else { if (filepos == 0)
776         ROSH_DEBUG("0 files found");
777     }
778 }                               /* rosh_ls_arg_dir */
779
780 /* Simple directory listing for one argument (file/directory) based on
781  * filestr and pwdstr
782  *      ifilstr input filename/directory name to list
783  *      pwdstr  Present Working Directory string
784  *      optarr  Option Array
785  */
786 void rosh_ls_arg(const char *filestr, const int *optarr)
787 {
788     struct stat fdstat;
789     int status;
790 //     char filestr[ROSH_PATH_SZ];
791 //     int filepos;
792     DIR *d;
793     struct dirent de;
794
795     /* Initialization; make filestr based on leading character of ifilstr
796        and pwdstr */
797 //     rosh_qualify_filestr(filestr, ifilstr, pwdstr);
798     fdstat.st_mode = 0;
799     fdstat.st_size = 0;
800     ROSH_DEBUG("\topt[0]=%d\topt[1]=%d\topt[2]=%d\n", optarr[0], optarr[1],
801                optarr[2]);
802
803     /* Now, the real work */
804     errno = 0;
805     status = stat(filestr, &fdstat);
806     if (status == 0) {
807         if (S_ISDIR(fdstat.st_mode)) {
808             ROSH_DEBUG("PATH '%s' is a directory\n", filestr);
809             if ((d = opendir(filestr))) {
810                 rosh_ls_arg_dir(filestr, d, optarr);
811                 closedir(d);
812             } else {
813                 rosh_error(errno, "ls", filestr);
814                 errno = 0;
815             }
816         } else {
817             de.d_ino = rosh_ls_d_ino(&fdstat);
818             de.d_type = (IFTODT(fdstat.st_mode));
819             strcpy(de.d_name, filestr);
820             if (S_ISREG(fdstat.st_mode)) {
821                 ROSH_DEBUG("PATH '%s' is a regular file\n", filestr);
822             } else {
823                 ROSH_DEBUG("PATH '%s' is some other file\n", filestr);
824             }
825             rosh_ls_arg_dir_de(&de, optarr);
826 /*          if (ifilstr[0] == SEP)
827                 rosh_ls_arg_dir_de(NULL, &de, optarr);
828             else
829                 rosh_ls_arg_dir_de(pwdstr, &de, optarr);*/
830         }
831     } else {
832         rosh_error(errno, "ls", filestr);
833         errno = 0;
834     }
835     return;
836 }                               /* rosh_ls_arg */
837
838 /* Parse options that may be present in the cmdstr
839  *      filestr Possible option string to parse
840  *      optstr  Current options
841  *      returns 1 if filestr does not begin with '-' else 0
842  */
843 int rosh_ls_parse_opt(const char *filestr, char *optstr)
844 {
845     int ret;
846     if (filestr[0] == '-') {
847         ret = 0;
848         if (optstr)
849             strcat(optstr, filestr + 1);
850     } else {
851         ret = 1;
852     }
853     ROSH_DEBUG("ParseOpt: '%s'\n\topt: '%s'\n\tret: %d\n", filestr, optstr,
854                ret);
855     return ret;
856 }                               /* rosh_ls_parse_opt */
857
858 /* List Directory
859  *      argc    Argument count
860  *      argv    Argument values
861  */
862 void rosh_ls(int argc, char *argv[])
863 {
864     int optarr[3];
865     int i;
866
867     rosh_ls_arg_opt(argc, argv, optarr);
868 #ifdef DO_DEBUG
869     optarr[0] = 2;
870 #endif /* DO_DEBUG */
871     ROSH_DEBUG("  argc=%d; optind=%d\n", argc, optind);
872     if (optind > argc)
873         rosh_ls_arg(".", optarr);
874     for (i = optind; i < argc; i++) {
875         rosh_ls_arg(argv[i], optarr);
876     }
877 }                               /* rosh_ls */
878
879 /* Simple directory listing; calls rosh_ls()
880  *      argc    Argument count
881  *      argv    Argument values
882  */
883 void rosh_dir(int argc, char *argv[])
884 {
885     ROSH_DEBUG("  dir implemented as ls\n");
886     rosh_ls(argc, argv);
887 }                               /* rosh_dir */
888
889 /* Page through a buffer string
890  *      buf     Buffer to page through
891  */
892 void rosh_more_buf(char *buf, int buflen, int rows, int cols, char *scrbuf)
893 {
894     char *bufp, *bufeol, *bufeol2;      /* Pointer to current and next
895                                            end-of-line position in buffer */
896     int bufpos, bufcnt;         /* current position, count characters */
897     int inc;
898     int i, numln;               /* Index, Number of lines */
899     int elpl;           /* Extra lines per line read */
900
901     (void)cols;
902
903     bufpos = 0;
904     bufp = buf + bufpos;
905     bufeol = bufp;
906     numln = rows - 1;
907     ROSH_DEBUG("--(%d)\n", buflen);
908     while (bufpos < buflen) {
909         for (i = 0; i < numln; i++) {
910             bufeol2 = strchr(bufeol, '\n');
911             if (bufeol2 == NULL) {
912                 bufeol = buf + buflen;
913                 i = numln;
914             } else {
915                 elpl = ((bufeol2 - bufeol - 1) / cols);
916                 if (elpl < 0)
917                     elpl = 0;
918                 i += elpl;
919                 ROSH_DEBUG2("  %d/%d  ", elpl, i+1);
920                 /* If this will not push too much, use it */
921                 /* but if it's the first line, use it */
922                 /* //HERE: We should probably snip the line off */
923                 if ((i < numln) || (i == elpl))
924                     bufeol = bufeol2 + 1;
925             }
926         }
927         ROSH_DEBUG2("\n");
928         bufcnt = bufeol - bufp;
929         printf("--(%d/%d @%d)\n", bufcnt, buflen, bufpos);
930         memcpy(scrbuf, bufp, bufcnt);
931         scrbuf[bufcnt] = 0;
932         printf("%s", scrbuf);
933         bufp = bufeol;
934         bufpos += bufcnt;
935         if (bufpos == buflen)
936             break;
937         inc = rosh_getkey();
938         numln = 1;
939         switch (inc) {
940         case KEY_CTRL('c'):
941         case 'q':
942         case 'Q':
943             bufpos = buflen;
944             break;
945         case ' ':
946             numln = rows - 1;
947         }
948     }
949 }                               /* rosh_more_buf */
950
951 /* Page through a single file using the open file stream
952  *      fd      File Descriptor
953  */
954 void rosh_more_fd(int fd, int rows, int cols, char *scrbuf)
955 {
956     struct stat fdstat;
957     int status;
958     char *buf;
959     int bufpos;
960     int numrd;
961     FILE *f;
962
963     status = fstat(fd, &fdstat);
964     if (S_ISREG(fdstat.st_mode)) {
965         buf = malloc((int)fdstat.st_size);
966         if (buf != NULL) {
967             f = fdopen(fd, "r");
968             bufpos = 0;
969             numrd = fread(buf, 1, (int)fdstat.st_size, f);
970             while (numrd > 0) {
971                 bufpos += numrd;
972                 numrd = fread(buf + bufpos, 1,
973                               ((int)fdstat.st_size - bufpos), f);
974             }
975             fclose(f);
976             rosh_more_buf(buf, bufpos, rows, cols, scrbuf);
977         }
978     } else {
979     }
980
981 }                               /* rosh_more_fd */
982
983 /* Page through a file like the more command
984  *      argc    Argument Count
985  *      argv    Argument Values
986  */
987 void rosh_more(int argc, char *argv[])
988 {
989     int fd, i;
990 /*    char filestr[ROSH_PATH_SZ];
991     int cmdpos;*/
992     int rows, cols;
993     char *scrbuf;
994     int ret;
995
996     ROSH_DEBUG("CMD: '%s'\n", cmdstr);
997     ret = getscreensize(1, &rows, &cols);
998     if (ret) {
999         ROSH_DEBUG("getscreensize() fail(%d); fall back\n", ret);
1000         ROSH_DEBUG("\tROWS='%d'\tCOLS='%d'\n", rows, cols);
1001         /* If either fail, go under normal size, just in case */
1002         if (!rows)
1003             rows = 20;
1004         if (!cols)
1005             cols = 75;
1006     }
1007     ROSH_DEBUG("\tUSE ROWS='%d'\tCOLS='%d'\n", rows, cols);
1008     /* 32 bit align beginning of row and over allocate */
1009     scrbuf = malloc(rows * ((cols+3)&(INT_MAX - 3)));
1010     if (!scrbuf)
1011         return;
1012
1013     if (argc) {
1014         /* There is no need to mess up the console if we don't have a
1015            file */
1016         rosh_console_raw();
1017         for (i = 0; i < argc; i++) {
1018             printf("--File = '%s'\n", argv[i]);
1019             errno = 0;
1020             fd = open(argv[i], O_RDONLY);
1021             if (fd != -1) {
1022                 rosh_more_fd(fd, rows, cols, scrbuf);
1023                 close(fd);
1024             } else {
1025                 rosh_error(errno, "more", argv[i]);
1026                 errno = 0;
1027             }
1028         }
1029         rosh_console_std();
1030     }
1031     free(scrbuf);
1032 }                               /* rosh_more */
1033
1034 /* Page a file with rewind
1035  *      argc    Argument Count
1036  *      argv    Argument Values
1037  */
1038 void rosh_less(int argc, char *argv[])
1039 {
1040     printf("  less implemented as more (for now)\n");
1041     rosh_more(argc, argv);
1042 }                               /* rosh_less */
1043
1044 /* Show PWD
1045  */
1046 void rosh_pwd(void)
1047 {
1048     char pwdstr[ROSH_PATH_SZ];
1049     errno = 0;
1050     if (getcwd(pwdstr, ROSH_PATH_SZ)) {
1051         printf("%s\n", pwdstr);
1052     } else {
1053         rosh_error(errno, "pwd", "");
1054         errno = 0;
1055     }
1056 }                               /* rosh_pwd */
1057
1058 /* Reboot; use warm reboot if one of certain options set
1059  *      argc    Argument count
1060  *      argv    Argument values
1061  */
1062 void rosh_reboot(int argc, char *argv[])
1063 {
1064     int rtype = 0;
1065     if (argc) {
1066         /* For now, just use the first */
1067         switch (argv[0][0]) {
1068         case '1':
1069         case 's':
1070         case 'w':
1071             rtype = 1;
1072             break;
1073         case '-':
1074             switch (argv[0][1]) {
1075             case '1':
1076             case 's':
1077             case 'w':
1078                 rtype = 1;
1079                 break;
1080             }
1081             break;
1082         }
1083     }
1084     syslinux_reboot(rtype);
1085 }                               /* rosh_reboot */
1086
1087 /* Run a boot string, calling syslinux_run_command
1088  *      argc    Argument count
1089  *      argv    Argument values
1090  */
1091 void rosh_run(int argc, char *argv[])
1092 {
1093     char cmdstr[ROSH_CMD_SZ];
1094     int len;
1095
1096     len = rosh_argcat(cmdstr, argc, argv, 0);
1097     if (len) {
1098         printf("--run: '%s'\n", cmdstr);
1099         syslinux_run_command(cmdstr);
1100     } else {
1101         printf(APP_NAME ":run: No arguments\n");
1102     }
1103 }                               /* rosh_run */
1104
1105 /* Process an argc/argv pair and call handling function
1106  *      argc    Argument count
1107  *      argv    Argument values
1108  *      ipwdstr Initial Present Working Directory string
1109  *      returns Whether to exit prompt
1110  */
1111 char rosh_command(int argc, char *argv[], const char *ipwdstr)
1112 {
1113     char do_exit = false;
1114     int tlen;
1115     tlen = strlen(argv[0]);
1116     ROSH_DEBUG_ARGV_V(argc, argv);
1117     switch (argv[0][0]) {
1118     case 'e':
1119     case 'E':
1120     case 'q':
1121     case 'Q':
1122         switch (argv[0][1]) {
1123         case 0:
1124         case 'x':
1125         case 'X':
1126         case 'u':
1127         case 'U':
1128             if ((strncasecmp("exit", argv[0], tlen) == 0) ||
1129                 (strncasecmp("quit", argv[0], tlen) == 0))
1130                 do_exit = true;
1131             else
1132                 rosh_help(1, NULL);
1133             break;
1134         case 'c':
1135         case 'C':
1136             if (strncasecmp("echo", argv[0], tlen) == 0)
1137                 rosh_pr_argv(argc - 1, &argv[1]);
1138             else
1139                 rosh_help(1, NULL);
1140             break;
1141         default:
1142             rosh_help(1, NULL);
1143         }
1144         break;
1145     case 'c':
1146     case 'C':                   /* run 'cd' 'cat' 'cfg' */
1147         switch (argv[0][1]) {
1148         case 'a':
1149         case 'A':
1150             if (strncasecmp("cat", argv[0], tlen) == 0)
1151                 rosh_cat(argc - 1, &argv[1]);
1152             else
1153                 rosh_help(1, NULL);
1154             break;
1155         case 'd':
1156         case 'D':
1157             if (strncasecmp("cd", argv[0], tlen) == 0)
1158                 rosh_cd(argc, argv, ipwdstr);
1159             else
1160                 rosh_help(1, NULL);
1161             break;
1162         case 'f':
1163         case 'F':
1164             if (strncasecmp("cfg", argv[0], tlen) == 0)
1165                 rosh_cfg();
1166             else
1167                 rosh_help(1, NULL);
1168             break;
1169         default:
1170             rosh_help(1, NULL);
1171         }
1172         break;
1173     case 'd':
1174     case 'D':                   /* run 'dir' */
1175         if (strncasecmp("dir", argv[0], tlen) == 0)
1176             rosh_dir(argc - 1, &argv[1]);
1177         else
1178             rosh_help(1, NULL);
1179         break;
1180     case 'h':
1181     case 'H':
1182     case '?':
1183         if ((strncasecmp("help", argv[0], tlen) == 0) || (tlen == 1))
1184             rosh_help(2, argv[1]);
1185         else
1186             rosh_help(1, NULL);
1187         break;
1188     case 'l':
1189     case 'L':                   /* run 'ls' 'less' */
1190         switch (argv[0][1]) {
1191         case 0:
1192         case 's':
1193         case 'S':
1194             if (strncasecmp("ls", argv[0], tlen) == 0)
1195                 rosh_ls(argc - 1, &argv[1]);
1196             else
1197                 rosh_help(1, NULL);
1198             break;
1199         case 'e':
1200         case 'E':
1201             if (strncasecmp("less", argv[0], tlen) == 0)
1202                 rosh_less(argc - 1, &argv[1]);
1203             else
1204                 rosh_help(1, NULL);
1205             break;
1206         default:
1207             rosh_help(1, NULL);
1208         }
1209         break;
1210     case 'm':
1211     case 'M':
1212         switch (argv[0][1]) {
1213         case 'a':
1214         case 'A':
1215             if (strncasecmp("man", argv[0], tlen) == 0)
1216                 rosh_help(2, argv[1]);
1217             else
1218                 rosh_help(1, NULL);
1219             break;
1220         case 'o':
1221         case 'O':
1222             if (strncasecmp("more", argv[0], tlen) == 0)
1223                 rosh_more(argc - 1, &argv[1]);
1224             else
1225                 rosh_help(1, NULL);
1226             break;
1227         default:
1228             rosh_help(1, NULL);
1229         }
1230         break;
1231     case 'p':
1232     case 'P':                   /* run 'pwd' */
1233         if (strncasecmp("pwd", argv[0], tlen) == 0)
1234             rosh_pwd();
1235         else
1236             rosh_help(1, NULL);
1237         break;
1238     case 'r':
1239     case 'R':                   /* run 'run' */
1240         switch (argv[0][1]) {
1241         case 0:
1242         case 'e':
1243         case 'E':
1244             if (strncasecmp("reboot", argv[0], tlen) == 0)
1245                 rosh_reboot(argc - 1, &argv[1]);
1246             else
1247                 rosh_help(1, NULL);
1248             break;
1249         case 'u':
1250         case 'U':
1251             if (strncasecmp("run", argv[0], tlen) == 0)
1252                 rosh_run(argc - 1, &argv[1]);
1253             else
1254                 rosh_help(1, NULL);
1255             break;
1256         }
1257         break;
1258     case 'v':
1259     case 'V':
1260         if (strncasecmp("version", argv[0], tlen) == 0)
1261             rosh_version(1);
1262         else
1263             rosh_help(1, NULL);
1264         break;
1265     case 0:
1266     case '\n':
1267         break;
1268     default:
1269         rosh_help(1, NULL);
1270     }                           /* switch(argv[0][0]) */
1271     return do_exit;
1272 }                               /* rosh_command */
1273
1274 /* Process the prompt for commands as read from stdin and call rosh_command
1275  * to process command line string
1276  *      icmdstr Initial command line string
1277  *      returns Exit status
1278  */
1279 int rosh_prompt(int iargc, char *iargv[])
1280 {
1281     int rv;
1282     char cmdstr[ROSH_CMD_SZ];
1283     char ipwdstr[ROSH_PATH_SZ];
1284     char do_exit;
1285     char **argv;
1286     int argc;
1287
1288     rv = 0;
1289     do_exit = false;
1290     if (!getcwd(ipwdstr, ROSH_PATH_SZ))
1291         strcpy(ipwdstr, "./");
1292     if (iargc > 1)
1293         do_exit = rosh_command(iargc - 1, &iargv[1], ipwdstr);
1294     while (!(do_exit)) {
1295         /* Extra preceeding newline */
1296         printf("\nrosh: ");
1297         /* Read a line from console */
1298         if (fgets(cmdstr, ROSH_CMD_SZ, stdin)) {
1299             argc = rosh_str2argv(&argv, cmdstr);
1300             do_exit = rosh_command(argc, argv, ipwdstr);
1301             rosh_free_argv(&argv);
1302         } else {
1303             do_exit = false;
1304         }
1305     }
1306     return rv;
1307 }
1308
1309 int main(int argc, char *argv[])
1310 {
1311     int rv;
1312
1313     /* Initialization */
1314     rv = 0;
1315     rosh_console_std();
1316     if (argc == 1) {
1317         rosh_version(0);
1318         print_beta();
1319     }
1320     rv = rosh_prompt(argc, argv);
1321     printf("--Exiting '" APP_NAME "'\n");
1322     return rv;
1323 }