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