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