Home | History | Annotate | Download | only in rosh
      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 }
   1364