1 /* ----------------------------------------------------------------------- *
3 * Copyright 2008 Gene Cumm - All Rights Reserved
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.
11 * ----------------------------------------------------------------------- */
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.
23 * Change functions to use pwdstr
24 * In rosh_run() Reparse cmdstr relative to pwdstr
28 /* Uncomment the above line for debugging output; Comment to remove */
29 // #define DO_DEBUG2 1
30 /* Uncomment the above line for super-debugging output; Must have regular debugging enabled; Comment to remove */
34 #define APP_LONGNAME "Read-Only Shell"
35 #define APP_NAME "rosh"
36 #define APP_AUTHOR "Gene Cumm"
37 #define APP_YEAR "2008"
38 #define APP_VER "beta-b032"
40 void rosh_version(void)
42 printf("%s v %s; (c) %s %s.\n", APP_LONGNAME
, APP_VER
, APP_YEAR
,
46 void rosh_help(int type
)
59 /* Determine if a character is whitespace
61 * returns 0 if not whitespace
63 int rosh_issp(char inc
)
77 /* Search a string for first non-space (' ') character, starting at ipos
78 * istr input string to parse
79 * ipos input position to start at
81 int rosh_search_nonsp(const char *istr
, const int ipos
)
88 while (rosh_issp(c
) && c
!= 0)
93 /* Search a string for space (' '), returning the position of the next space
94 * or the '\0' at end of string
95 * istr input string to parse
96 * ipos input position to start at
98 int rosh_search_sp(const char *istr
, const int ipos
)
105 while (!(rosh_issp(c
)) && c
!= 0)
110 /* Parse a string for the first non-space string, returning the end position
112 * dest string to contain the first non-space string
113 * src string to parse
114 * ipos Position to start in src
116 int rosh_parse_sp_1(char *dest
, const char *src
, const int ipos
)
118 int bpos
, epos
; /* beginning and ending position of source string
119 to copy to destination string */
123 /* //HERE-error condition checking */
124 bpos
= rosh_search_nonsp(src
, ipos
);
125 epos
= rosh_search_sp(src
, bpos
);
127 memcpy(dest
, src
+ bpos
, epos
- bpos
);
128 if (dest
[epos
- bpos
] != 0)
129 dest
[epos
- bpos
] = 0;
137 /* Handle most/all errors
138 * ierrno Input Error number
139 * cmdstr Command being executed to cause error
140 * filestr File/parameter causing error
142 void rosh_error(const int ierrno
, const char *cmdstr
, const char *filestr
)
144 printf("--ERROR: %s '%s': ", cmdstr
, filestr
);
147 printf("Access DENIED\n");
150 printf("not found\n");
151 /* SYSLinux-3.72 COM32 API returns this for a
152 directory or empty file */
153 ROSH_COM32(" (COM32) could be a directory or empty file\n");
156 printf("not a directory\n");
157 ROSH_COM32(" (COM32) could be directory\n");
160 printf("not implemented");
163 printf("returns error; errno=%d\n", ierrno
);
167 /* Concatenate command line arguments into one string
168 * cmdstr Output command string
169 * argc Argument Count
170 * argv Argument Values
171 * barg Beginning Argument
173 int rosh_argcat(char *cmdstr
, const int argc
, char *argv
[], const int barg
)
175 int i
, arglen
, curpos
; /* index, argument length, current position
178 cmdstr
[0] = '\0'; /* Nullify string just to be sure */
179 for (i
= barg
; i
< argc
; i
++) {
180 arglen
= strlen(argv
[i
]);
181 /* Theoretically, this should never be met in SYSLINUX */
182 if ((curpos
+ arglen
) > (ROSH_CMD_SZ
- 1))
183 arglen
= (ROSH_CMD_SZ
- 1) - curpos
;
184 memcpy(cmdstr
+ curpos
, argv
[i
], arglen
);
186 if (curpos
>= (ROSH_CMD_SZ
- 1)) {
187 /* Hopefully, curpos should not be greater than
189 /* Still need a '\0' at the last character */
190 cmdstr
[(ROSH_CMD_SZ
- 1)] = 0;
191 break; /* Escape out of the for() loop;
192 We can no longer process anything more */
194 cmdstr
[curpos
] = ' ';
199 /* If there's a ' ' at the end, remove it. This is normal unless
200 the maximum length is met/exceeded. */
201 if (cmdstr
[curpos
- 1] == ' ')
202 cmdstr
[--curpos
] = 0;
207 * Prints a lot of the data in a struct termios
210 void rosh_print_tc(struct termios *tio)
212 printf(" -- termios: ");
213 printf(".c_iflag=%04X ", tio->c_iflag);
214 printf(".c_oflag=%04X ", tio->c_oflag);
215 printf(".c_cflag=%04X ", tio->c_cflag);
216 printf(".c_lflag=%04X ", tio->c_lflag);
217 printf(".c_cc[VTIME]='%d' ", tio->c_cc[VTIME]);
218 printf(".c_cc[VMIN]='%d'", tio->c_cc[VMIN]);
224 * Switches console over to raw input mode. Allows get_key to get just
225 * 1 key sequence (without delay or display)
227 void rosh_console_raw(void)
229 // struct termios itio, ntio;
230 // tcgetattr(0, &itio);
231 // rosh_print_tc(&itio);
233 ntio.c_lflag &= ~(ICANON|ECHO);
234 tcsetattr(0, TCSAFLUSH, &ntio);*/
235 console_ansi_raw(); /* Allows get_key to get just 1 key sequence
236 (w/o delay or display */
237 // tcgetattr(0, &ntio);
238 // rosh_print_tc(&ntio);
242 * Switches back to standard getline mode.
244 void rosh_console_std(void)
246 // struct termios itio, ntio;
248 // tcsetattr(0, TCSANOW, &itio);
252 * Attempts to get a single key from the console
253 * returns key pressed
255 int rosh_getkey(void)
260 // rosh_console_raw();
261 while (inc
== KEY_NONE
) {
262 inc
= get_key(stdin
, 6000);
264 // rosh_console_std();
268 /* Template for command functions
269 * cmdstr command string to process
270 * pwdstr Present Working Directory string
271 * ipwdstr Initial PWD
273 void rosh_1(const char *cmdstr
, const char *pwdstr
, const char *ipwdstr
)
275 ROSH_DEBUG("CMD: '%s'\npwd: '%s'\npwd: '%s'\n", cmdstr
, pwdstr
, ipwdstr
);
278 /* Concatenate multiple files to stdout
279 * cmdstr command string to process
280 * pwdstr Present Working Directory string
282 void rosh_cat(const char *cmdstr
, const char *pwdstr
)
285 char filestr
[ROSH_PATH_SZ
+ 1];
286 char buf
[ROSH_BUF_SZ
];
290 ROSH_DEBUG("CMD: '%s'\npwd: '%s'\n", cmdstr
, pwdstr
);
294 /* skip the first word */
295 cmdpos
= rosh_parse_sp_1(filestr
, cmdstr
, cmdpos
);
296 cmdpos
= rosh_parse_sp_1(filestr
, cmdstr
, cmdpos
);
297 while (strlen(filestr
) > 0) {
298 printf("--File = '%s'\n", filestr
);
299 f
= fopen(filestr
, "r");
301 numrd
= fread(buf
, 1, ROSH_BUF_SZ
, f
);
303 fwrite(buf
, 1, numrd
, stdout
);
304 numrd
= fread(buf
, 1, ROSH_BUF_SZ
, f
);
308 rosh_error(errno
, "cat", filestr
);
310 cmdpos
= rosh_parse_sp_1(filestr
, cmdstr
, cmdpos
);
314 /* Change PWD (Present Working Directory)
315 * cmdstr command string to process
316 * pwdstr Present Working Directory string
317 * ipwdstr Initial PWD
319 void rosh_cd(const char *cmdstr
, char *pwdstr
, const char *ipwdstr
)
322 char filestr
[ROSH_PATH_SZ
+ 1];
324 ROSH_DEBUG("CMD: '%s'\npwd: '%s'\n", cmdstr
, pwdstr
);
329 /* skip the first word */
330 cmdpos
= rosh_parse_sp_1(filestr
, cmdstr
, cmdpos
);
331 cmdpos
= rosh_parse_sp_1(filestr
, cmdstr
, cmdpos
);
333 (" -- cd (Change Directory) not implemented for use with run and exit.\n");
334 if (strlen(filestr
) != 0)
339 rosh_error(errno
, "cd", filestr
);
341 getcwd(pwdstr
, ROSH_PATH_SZ
+ 1);
342 printf(" %s\n", pwdstr
);
346 /* Print the syslinux config file name
347 * cmdstr command string to process
348 * pwdstr Present Working Directory string
350 void rosh_cfg(const char *cmdstr
, const char *pwdstr
)
352 ROSH_DEBUG("CMD: '%s'\npwd: '%s'\n", cmdstr
, pwdstr
);
353 printf("CFG: '%s'\n", syslinux_config_file());
356 /* Simple directory listing for one argument (file/directory) based on
358 * ifilstr input filename/directory name to list
359 * pwdstr Present Working Directory string
361 void rosh_dir_arg(const char *ifilstr
, const char *pwdstr
)
366 char filestr
[ROSH_PATH_SZ
+ 1];
371 char filestr2
[ROSH_PATH_SZ
+ 1];
376 #endif /* __COM32__ */
377 #endif /* DO_DEBUG */
379 /* Initialization; make filestr based on leading character of ifilstr
381 if (ifilstr
[0] == SEP
) {
382 strcpy(filestr
, ifilstr
);
384 strcpy(filestr
, pwdstr
);
385 filepos
= strlen(pwdstr
);
386 if (filestr
[filepos
- 1] != SEP
)
387 filestr
[filepos
++] = SEP
;
388 strcpy(filestr
+ filepos
, ifilstr
);
389 ROSH_DEBUG("--'%s'\n", filestr
);
391 fd
= open(filestr
, O_RDONLY
);
393 status
= fstat(fd
, &fdstat
);
394 if (S_ISDIR(fdstat
.st_mode
)) {
395 ROSH_DEBUG("PATH '%s' is a directory\n", ifilstr
);
401 file2pos
= strlen(filestr
);
402 memcpy(filestr2
, filestr
, file2pos
);
403 filestr2
[file2pos
] = '/';
404 strcpy(filestr2
+ file2pos
+ 1, de
->d_name
);
405 fd2
= open(filestr2
, O_RDONLY
);
406 status
= fstat(fd2
, &fdstat
);
407 printf("@%8d:%8d:", (int)de
->d_ino
, (int)fdstat
.st_size
);
409 #endif /* DO_DEBUG */
410 printf("%s\n", de
->d_name
);
412 // inchar = fgetc(stdin);
413 #endif /* DO_DEBUG */
417 } else if (S_ISREG(fdstat
.st_mode
)) {
418 ROSH_DEBUG("PATH '%s' is a regular file\n", ifilstr
);
419 printf("%8d:%s\n", (int)fdstat
.st_size
, ifilstr
);
421 ROSH_DEBUG("PATH '%s' is some other file\n", ifilstr
);
422 printf(" :%s\n", ifilstr
);
426 if (filestr
[strlen(filestr
) - 1] == SEP
) {
429 d
= opendir(filestr
);
431 printf("DIR:'%s' %8d %8d\n", d
->dd_name
, d
->dd_fd
,
437 // if (strlen(de->d_name) > 25) de->d_name[25] = 0;
438 switch (de
->d_mode
) {
448 printf("@%8d:%8d:%4d ", (int)de
->d_ino
, (int)de
->d_size
,
450 #endif /* DO_DEBUG */
451 // printf("%s\n", de->d_name);
452 printf("'%s'\n", de
->d_name
);
454 // inchar = fgetc(stdin);
455 // fgets(instr, ROSH_CMD_SZ, stdin);
456 #endif /* DO_DEBUG */
459 // if(filepos>15){ de = NULL; printf("Force Break\n");}
461 printf("Dir.dd_fd: '%8d'\n", d
->dd_fd
);
464 rosh_error(0, "dir:NULL", filestr
);
467 rosh_error(errno
, "dir_c32", filestr
);
470 rosh_error(errno
, "dir", filestr
);
471 #endif /* __COM32__ */
475 /* Simple directory listing based on cmdstr and pwdstr
476 * cmdstr command string to process
477 * pwdstr Present Working Directory string
479 void rosh_dir(const char *cmdstr
, const char *pwdstr
)
481 char filestr
[ROSH_PATH_SZ
+ 1];
482 int cmdpos
; /* Position within cmdstr */
484 ROSH_DEBUG("CMD: '%s'\npwd: '%s'\n", cmdstr
, pwdstr
);
488 /* skip the first word */
489 cmdpos
= rosh_parse_sp_1(filestr
, cmdstr
, cmdpos
);
490 cmdpos
= rosh_parse_sp_1(filestr
, cmdstr
, cmdpos
);
491 /* If there are no real arguments, substitute PWD */
492 if (strlen(filestr
) == 0)
493 strcpy(filestr
, pwdstr
);
494 while (strlen(filestr
) > 0) {
495 rosh_dir_arg(filestr
, pwdstr
);
496 cmdpos
= rosh_parse_sp_1(filestr
, cmdstr
, cmdpos
);
500 /* List Directory; Calls rosh_dir() for now.
501 * cmdstr command string to process
502 * pwdstr Present Working Directory string
504 void rosh_ls(const char *cmdstr
, const char *pwdstr
)
506 printf(" ls implemented as dir (for now)\n");
507 rosh_dir(cmdstr
, pwdstr
);
510 /* Page through a buffer string
511 * buf Buffer to page through
513 void rosh_more_buf(char *buf
, int buflen
, int rows
, int cols
)
515 char *bufp
, *bufeol
; /* Pointer to current and next end-of-line
516 position in buffer */
517 int bufpos
, bufcnt
; /* current position, count characters */
518 char scrbuf
[ROSH_SBUF_SZ
];
520 int i
, numln
; /* Index, Number of lines */
526 printf("--(%d)\n", buflen
);
527 // printf("--termIOS CONSTS: ");
528 // printf("ISIG=%08X ", ISIG);
529 // printf("ICANON=%08X ", ICANON);
530 // printf("ECHO=%08X ", ECHO);
531 // printf("=%08X", );
533 while (bufpos
< buflen
) {
534 for (i
= 0; i
< numln
; i
++) {
535 bufeol
= strchr(bufeol
, '\n');
536 if (bufeol
== NULL
) {
537 bufeol
= buf
+ buflen
;
542 // printf("--readln\n");
544 bufcnt
= bufeol
- bufp
;
545 printf("--(%d/%d @%d)\n", bufcnt
, buflen
, bufpos
);
546 memcpy(scrbuf
, bufp
, bufcnt
);
548 printf("%s", scrbuf
);
551 if (bufpos
== buflen
)
566 /*tcgetattr(0, &tio);
568 printf("\n--END\n");*/
569 } /* rosh_more_buf */
571 /* Page through a single file using the open file stream
574 void rosh_more_fd(int fd
, int rows
, int cols
)
583 status
= fstat(fd
, &fdstat
);
584 if (S_ISREG(fdstat
.st_mode
)) {
585 buf
= malloc((int)fdstat
.st_size
);
589 numrd
= fread(buf
, 1, (int)fdstat
.st_size
, f
);
592 numrd
= fread(buf
+ bufpos
, 1,
593 ((int)fdstat
.st_size
- bufpos
), f
);
596 rosh_more_buf(buf
, bufpos
, rows
, cols
);
603 /* Page through a file like the more command
604 * cmdstr command string to process
605 * pwdstr Present Working Directory string
606 * ipwdstr Initial PWD
608 void rosh_more(const char *cmdstr
, const char *pwdstr
)
609 /*, const char *ipwdstr) */
612 char filestr
[ROSH_PATH_SZ
+ 1];
616 ROSH_DEBUG("CMD: '%s'\npwd: '%s'\n", cmdstr
, pwdstr
);
620 if (getscreensize(1, &rows
, &cols
)) {
621 ROSH_DEBUG("getscreensize() fail; fall back\n");
622 ROSH_DEBUG("\tROWS='%d'\tCOLS='%d'\n", rows
, cols
);
623 /* If either fail, go under normal size, just in case */
629 ROSH_DEBUG("\tROWS='%d'\tCOLS='%d'\n", rows
, cols
);
631 /* skip the first word */
632 cmdpos
= rosh_parse_sp_1(filestr
, cmdstr
, cmdpos
);
633 cmdpos
= rosh_parse_sp_1(filestr
, cmdstr
, cmdpos
);
634 if (strlen(filestr
) > 0) {
635 /* There is no need to mess up the console if we don't have a
638 while (strlen(filestr
) > 0) {
639 printf("--File = '%s'\n", filestr
);
640 fd
= open(filestr
, O_RDONLY
);
642 rosh_more_fd(fd
, rows
, cols
);
645 rosh_error(errno
, "more", filestr
);
647 cmdpos
= rosh_parse_sp_1(filestr
, cmdstr
, cmdpos
);
653 /* Page a file with rewind
654 * cmdstr command string to process
655 * pwdstr Present Working Directory string
656 * ipwdstr Initial PWD
658 void rosh_less(const char *cmdstr
, const char *pwdstr
)
660 printf(" less implemented as more (for now)\n");
661 rosh_more(cmdstr
, pwdstr
);
665 * cmdstr command string to process
666 * pwdstr Present Working Directory string
668 void rosh_pwd(const char *cmdstr
, const char *pwdstr
)
671 ROSH_DEBUG("CMD: '%s'\npwd: '%s'\n", cmdstr
, pwdstr
);
672 printf("%s\n", pwdstr
);
673 istr
= htonl(*(int *)pwdstr
);
674 ROSH_DEBUG(" --%08X\n", istr
);
677 /* Run a boot string, calling syslinux_run_command
678 * cmdstr command string to process
679 * pwdstr Present Working Directory string
680 * ipwdstr Initial PWD
682 void rosh_run(const char *cmdstr
, const char *pwdstr
, const char *ipwdstr
)
686 char istr
[ROSH_CMD_SZ
]; /* input command string */
689 ROSH_DEBUG("CMD: '%s'\npwd: '%s'\n", cmdstr
, pwdstr
);
690 /* skip the first word */
691 cmdpos
= rosh_search_sp(cmdstr
, cmdpos
);
693 cmdpos
= rosh_search_nonsp(cmdstr
, cmdpos
);
694 cmdptr
= (char *)(cmdstr
+ cmdpos
);
695 printf("--run: '%s'\n", cmdptr
);
696 /* //HERE--Reparse if pwdstr != ipwdstr; seems a little daunting as
697 detecting params vs filenames is difficult/impossible */
698 if (strcmp(pwdstr
, ipwdstr
) != 0) {
699 /* For now, just prompt for verification */
700 printf(" from directory '%s'? (y/N):", pwdstr
);
701 fgets(istr
, ROSH_CMD_SZ
, stdin
);
702 if ((istr
[0] != 'y') && (istr
[0] != 'Y')) {
703 printf("Aborting run\n");
706 printf("Run anyways\n");
708 syslinux_run_command(cmdptr
);
711 /* Process a single command string and call handling function
712 * cmdstr command string to process
713 * pwdstr Present Working Directory string
714 * ipwdstr Initial Present Working Directory string
715 * returns Whether to exit prompt
717 char rosh_command(const char *cmdstr
, char *pwdstr
, const char *ipwdstr
)
721 ROSH_DEBUG("--cmd:'%s'\n", cmdstr
);
730 case 'C': /* run 'cd' 'cat' 'cfg' */
734 rosh_cat(cmdstr
, pwdstr
);
738 rosh_cd(cmdstr
, pwdstr
, ipwdstr
);
742 rosh_cfg(cmdstr
, pwdstr
);
749 case 'D': /* run 'dir' */
750 rosh_dir(cmdstr
, pwdstr
);
758 case 'L': /* run 'ls' 'less' */
763 rosh_ls(cmdstr
, pwdstr
);
767 rosh_less(cmdstr
, pwdstr
);
782 rosh_more(cmdstr
, pwdstr
);
789 case 'P': /* run 'pwd' */
790 rosh_pwd(cmdstr
, pwdstr
);
793 case 'R': /* run 'run' */
794 rosh_run(cmdstr
, pwdstr
, ipwdstr
);
805 } /* switch(cmdstr[0]) */
809 /* Process the prompt for commands as read from stdin and call rosh_command
810 * to process command line string
811 * icmdstr Initial command line string
812 * returns Exit status
814 int rosh_prompt(const char *icmdstr
)
817 char cmdstr
[ROSH_CMD_SZ
];
818 char pwdstr
[ROSH_PATH_SZ
+ 1], ipwdstr
[ROSH_PATH_SZ
+ 1];
826 getcwd(pwdstr
, ROSH_PATH_SZ
+ 1);
827 strcpy(ipwdstr
, pwdstr
); /* Retain the original PWD */
828 if (icmdstr
[0] != '\0')
829 do_exit
= rosh_command(icmdstr
, pwdstr
, ipwdstr
);
833 /* Read a line from console */
834 fgets(cmdstr
, ROSH_CMD_SZ
, stdin
);
835 /* remove newline from input string */
836 c
= strchr(cmdstr
, '\n');
838 do_exit
= rosh_command(cmdstr
, pwdstr
, ipwdstr
);
840 if (strcmp(pwdstr
, ipwdstr
) != 0) {
841 /* Change directory to the original directory */
842 strcpy(cmdstr
, "cd ");
843 strcpy(cmdstr
+ 3, ipwdstr
);
844 rosh_cd(cmdstr
, pwdstr
, ipwdstr
);
849 int main(int argc
, char *argv
[])
852 char cmdstr
[ROSH_CMD_SZ
];
857 // console_ansi_raw();
859 rv
= rosh_argcat(cmdstr
, argc
, argv
, 1);
864 rv
= rosh_prompt(cmdstr
);
865 printf("--Exiting '%s'\n", APP_NAME
);