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  */
rosh_version(int vtype)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  */
print_beta(void)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  */
rosh_search_nonsp(const char * istr,const int ipos)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  */
rosh_search_sp(const char * istr,const int ipos)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  */
rosh_parse_sp_1(char * dest,const char * src,const int ipos)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  */
parse_args1(char *** iargv,const char * istr)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  */
free_args1(char *** argv)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  */
rosh_str2argv(char *** argv,const char * str)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  */
rosh_free_argv(char *** argv)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  */
rosh_pr_argv(int argc,char * argv[])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  */
rosh_pr_argv_v(int argc,char * argv[])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  */
rosh_getopt_reset(void)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  */
rosh_help(int type,const char * cmdstr)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  */
rosh_error(const int ierrno,const char * cmdstr,const char * filestr)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  */
rosh_argcat(char * cmdstr,const int cmdlen,const int argc,char * argv[],const int barg)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  */
rosh_getkey(void)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  */
rosh_qualify_filestr(char * filestr,const char * ifilstr,const char * pwdstr)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  */
rosh_cat(int argc,char * argv[])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  */
rosh_cd(int argc,char * argv[],const char * ipwdstr)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  */
rosh_cfg(void)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  */
rosh_echo(const char * cmdstr)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  */
rosh_ls_arg_opt(int argc,char * argv[],int optarr[])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  */
rosh_ls_de_size(const char * filestr,struct dirent * de)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  */
rosh_ls_de_size_mode(const char * filestr,struct dirent * de,mode_t * st_mode)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  */
rosh_ls_d_ino(struct stat * fdstat)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  */
rosh_d_type2char_human(unsigned char d_type)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  */
rosh_d_type2char_lspre(unsigned char d_type)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  */
rosh_d_type2char_lssuf(unsigned char d_type)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  */
rosh_st_mode_am2str(mode_t st_mode,char * st_mode_str)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  */
rosh_st_mode2str(mode_t st_mode,char * st_mode_str)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  */
rosh_ls_arg_dir_de(const char * filestr,struct dirent * de,const int * optarr)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  */
rosh_ls_arg_dir(const char * filestr,DIR * d,const int * optarr)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  */
rosh_ls_arg(const char * filestr,const int * optarr)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  */
rosh_ls_parse_opt(const char * filestr,char * optstr)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  */
rosh_ls(int argc,char * argv[])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  */
rosh_dir(int argc,char * argv[])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  */
rosh_more_buf(char * buf,int buflen,int rows,int cols,char * scrbuf)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  */
rosh_more_fd(int fd,int rows,int cols,char * scrbuf)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  */
rosh_more(int argc,char * argv[])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  */
rosh_less(int argc,char * argv[])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  */
rosh_pwd(void)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  */
rosh_reboot(int argc,char * argv[])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  */
rosh_run(int argc,char * argv[])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  */
rosh_command(int argc,char * argv[],const char * ipwdstr)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  */
rosh_prompt(int iargc,char * iargv[])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 
main(int argc,char * argv[])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