| /* ----------------------------------------------------------------------- * |
| * |
| * Copyright 2008-2011 Gene Cumm - All Rights Reserved |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation, Inc., 53 Temple Place Ste 330, |
| * Boston MA 02111-1307, USA; either version 2 of the License, or |
| * (at your option) any later version; incorporated herein by reference. |
| * |
| * ----------------------------------------------------------------------- */ |
| |
| /* |
| * rosh.c |
| * |
| * Read-Only shell; Simple shell system designed for SYSLINUX-derivitives. |
| * Provides minimal commands utilizing the console via stdout/stderr as the |
| * sole output devices. Designed to compile for Linux for testing/debugging. |
| */ |
| |
| /* |
| * ToDos: |
| * prompt: Allow left/right arrow, home/end and more? |
| * commands Break into argv/argc-like array |
| * rosh_cfg: allow -s <file> to change config |
| * rosh_ls(): sorted; then multiple columns |
| * prompt: Possibly honor timeout on initial entry for usage as UI |
| * Also possibly honor totaltimeout |
| */ |
| |
| /*#define DO_DEBUG 1 |
| //*/ |
| /* Uncomment the above line for debugging output; Comment to remove */ |
| /*#define DO_DEBUG2 1 |
| //*/ |
| /* Uncomment the above line for super-debugging output; Must have regular |
| * debugging enabled; Comment to remove. |
| */ |
| #include "rosh.h" |
| #include "version.h" |
| |
| #define APP_LONGNAME "Read-Only Shell" |
| #define APP_NAME "rosh" |
| #define APP_AUTHOR "Gene Cumm" |
| #define APP_YEAR "2010" |
| #define APP_VER "beta-b090" |
| |
| /* Print version information to stdout |
| */ |
| void rosh_version(int vtype) |
| { |
| char env[256]; |
| env[0] = 0; |
| printf("%s v %s; (c) %s %s.\n\tFrom Syslinux %s, %s\n", APP_LONGNAME, APP_VER, APP_YEAR, APP_AUTHOR, VERSION_STR, DATE); |
| switch (vtype) { |
| case 1: |
| rosh_get_env_ver(env, 256); |
| printf("\tRunning on %s\n", env); |
| } |
| } |
| |
| /* Print beta message and if DO_DEBUG/DO_DEBUG2 are active |
| */ |
| void print_beta(void) |
| { |
| puts(rosh_beta_str); |
| ROSH_DEBUG("DO_DEBUG active\n"); |
| ROSH_DEBUG2("DO_DEBUG2 active\n"); |
| } |
| |
| /* Search a string for first non-space (' ') character, starting at ipos |
| * istr input string to parse |
| * ipos input position to start at |
| */ |
| int rosh_search_nonsp(const char *istr, const int ipos) |
| { |
| int curpos; |
| char c; |
| |
| curpos = ipos; |
| c = istr[curpos]; |
| while (c && isspace(c)) |
| c = istr[++curpos]; |
| return curpos; |
| } |
| |
| /* Search a string for space (' '), returning the position of the next space |
| * or the '\0' at end of string |
| * istr input string to parse |
| * ipos input position to start at |
| */ |
| int rosh_search_sp(const char *istr, const int ipos) |
| { |
| int curpos; |
| char c; |
| |
| curpos = ipos; |
| c = istr[curpos]; |
| while (c && !(isspace(c))) |
| c = istr[++curpos]; |
| return curpos; |
| } |
| |
| /* Parse a string for the first non-space string, returning the end position |
| * from src |
| * dest string to contain the first non-space string |
| * src string to parse |
| * ipos Position to start in src |
| */ |
| int rosh_parse_sp_1(char *dest, const char *src, const int ipos) |
| { |
| int bpos, epos; /* beginning and ending position of source string |
| to copy to destination string */ |
| |
| bpos = 0; |
| epos = 0; |
| /* //HERE-error condition checking */ |
| bpos = rosh_search_nonsp(src, ipos); |
| epos = rosh_search_sp(src, bpos); |
| if (epos > bpos) { |
| memcpy(dest, src + bpos, epos - bpos); |
| if (dest[epos - bpos] != 0) |
| dest[epos - bpos] = 0; |
| } else { |
| epos = strlen(src); |
| dest[0] = 0; |
| } |
| return epos; |
| } |
| |
| /* |
| * parse_args1: Try 1 at parsing a string to an argc/argv pair. use free_args1 to free memory malloc'd |
| * |
| * Derived from com32/lib/sys/argv.c:__parse_argv() |
| * Copyright 2004-2009 H. Peter Anvin - All Rights Reserved |
| * Copyright 2009 Intel Corporation; author: H. Peter Anvin |
| */ |
| int parse_args1(char ***iargv, const char *istr) |
| { |
| int argc = 0; |
| const char *p; |
| char *q, *r, *args, **arg; |
| int sp = 1; //, qt = 0; /* Was a space; inside a quote */ |
| |
| /* Scan 1: Length */ |
| /* I could eliminate this if I knew a max length, like strncpy() */ |
| int len = strlen(istr); |
| |
| /* Scan 2: Copy, nullify and make argc */ |
| if (!(args = malloc(len + 1))) |
| goto fail_args; |
| q = args; |
| for (p = istr;; p++) { |
| if (*p <= ' ') { |
| if (!sp) { |
| sp = 1; |
| *q++ = '\0'; |
| } |
| } else { |
| if (sp) { |
| argc++; |
| sp = 0; |
| } |
| *q++ = *p; |
| } |
| if (!*p) |
| break; |
| } |
| |
| q--; /* Point q to final null */ |
| /* Scan 3: Build array of pointers */ |
| if (!(*iargv = malloc((argc + 1) * sizeof(char *)))) |
| goto fail_args_ptr; |
| arg = *iargv; |
| arg[argc] = NULL; /* Nullify the last pointer */ |
| if (*args != '\0') |
| *arg++ = args; |
| for (r = args; r < q ; r++) { |
| if (*r == '\0') { |
| *arg++ = r + 1; |
| } |
| } |
| |
| fail_args: |
| return argc; |
| fail_args_ptr: |
| free(args); |
| return 0; |
| } |
| |
| /* Free argv created by parse_args1() |
| * argv Argument Values |
| */ |
| void free_args1(char ***argv) |
| { |
| char *s; |
| s = **argv; |
| free(*argv); |
| free(s); |
| } |
| |
| /* Convert a string to an argc/argv pair |
| * str String to parse |
| * argv Argument Values |
| * returns Argument Count |
| */ |
| int rosh_str2argv(char ***argv, const char *str) |
| { |
| return parse_args1(argv, str); |
| } |
| |
| /* Free an argv created by rosh_str2argv() |
| * argv Argument Values to free |
| */ |
| void rosh_free_argv(char ***argv) |
| { |
| free_args1(argv); |
| } |
| |
| /* Print the contents of an argc/argv pair |
| * argc Argument Count |
| * argv Argument Values |
| */ |
| void rosh_pr_argv(int argc, char *argv[]) |
| { |
| int i; |
| for (i = 0; i < argc; i++) { |
| printf("%s%s", argv[i], (i < argc)? " " : ""); |
| } |
| puts(""); |
| } |
| |
| /* Print the contents of an argc/argv pair verbosely |
| * argc Argument Count |
| * argv Argument Values |
| */ |
| void rosh_pr_argv_v(int argc, char *argv[]) |
| { |
| int i; |
| for (i = 0; i < argc; i++) { |
| printf("%4d '%s'\n", i, argv[i]); |
| } |
| } |
| |
| /* Reset the getopt() environment |
| */ |
| void rosh_getopt_reset(void) |
| { |
| optind = 0; |
| optopt = 0; |
| } |
| |
| /* Display help |
| * type Help type |
| * cmdstr Command for which help is requested |
| */ |
| void rosh_help(int type, const char *cmdstr) |
| { |
| switch (type) { |
| case 2: |
| if ((cmdstr == NULL) || (strcmp(cmdstr, "") == 0)) { |
| rosh_version(0); |
| puts(rosh_help_str2); |
| } else { |
| switch (cmdstr[0]) { |
| case 'c': |
| puts(rosh_help_cd_str); |
| break; |
| case 'l': |
| puts(rosh_help_ls_str); |
| break; |
| default: |
| printf(rosh_help_str_adv, cmdstr); |
| } |
| } |
| break; |
| case 1: |
| default: |
| if (cmdstr) |
| printf("%s: %s: unknown command\n", APP_NAME, cmdstr); |
| rosh_version(0); |
| puts(rosh_help_str1); |
| } |
| } |
| |
| /* Handle most/all errors |
| * ierrno Input Error number |
| * cmdstr Command being executed to cause error |
| * filestr File/parameter causing error |
| */ |
| void rosh_error(const int ierrno, const char *cmdstr, const char *filestr) |
| { |
| printf("--ERROR: %s '%s': ", cmdstr, filestr); |
| switch (ierrno) { |
| case 0: |
| puts("NO ERROR"); |
| break; |
| case ENOENT: |
| puts("not found"); |
| /* SYSLinux-3.72 COM32 API returns this for a |
| directory or empty file */ |
| ROSH_COM32(" (COM32) could be a directory or empty file\n"); |
| break; |
| case EIO: |
| puts("I/O Error"); |
| break; |
| case EBADF: |
| puts("Bad File Descriptor"); |
| break; |
| case EACCES: |
| puts("Access DENIED"); |
| break; |
| case ENOTDIR: |
| puts("not a directory"); |
| ROSH_COM32(" (COM32) could be directory\n"); |
| break; |
| case EISDIR: |
| puts("IS a directory"); |
| break; |
| case ENOSYS: |
| puts("not implemented"); |
| break; |
| default: |
| printf("returns error; errno=%d\n", ierrno); |
| } |
| } /* rosh_error */ |
| |
| /* Concatenate command line arguments into one string |
| * cmdstr Output command string |
| * cmdlen Length of cmdstr |
| * argc Argument Count |
| * argv Argument Values |
| * barg Beginning Argument |
| */ |
| int rosh_argcat(char *cmdstr, const int cmdlen, const int argc, char *argv[], |
| const int barg) |
| { |
| int i, arglen, curpos; /* index, argument length, current position |
| in cmdstr */ |
| curpos = 0; |
| cmdstr[0] = '\0'; /* Nullify string just to be sure */ |
| for (i = barg; i < argc; i++) { |
| arglen = strlen(argv[i]); |
| /* Theoretically, this should never be met in SYSLINUX */ |
| if ((curpos + arglen) > (cmdlen - 1)) |
| arglen = (cmdlen - 1) - curpos; |
| memcpy(cmdstr + curpos, argv[i], arglen); |
| curpos += arglen; |
| if (curpos >= (cmdlen - 1)) { |
| /* Hopefully, curpos should not be greater than |
| (cmdlen - 1) */ |
| /* Still need a '\0' at the last character */ |
| cmdstr[(cmdlen - 1)] = 0; |
| break; /* Escape out of the for() loop; |
| We can no longer process anything more */ |
| } else { |
| cmdstr[curpos] = ' '; |
| curpos += 1; |
| cmdstr[curpos] = 0; |
| } |
| } |
| /* If there's a ' ' at the end, remove it. This is normal unless |
| the maximum length is met/exceeded. */ |
| if (cmdstr[curpos - 1] == ' ') |
| cmdstr[--curpos] = 0; |
| return curpos; |
| } /* rosh_argcat */ |
| |
| /* |
| * Prints a lot of the data in a struct termios |
| */ |
| /* |
| void rosh_print_tc(struct termios *tio) |
| { |
| printf(" -- termios: "); |
| printf(".c_iflag=%04X ", tio->c_iflag); |
| printf(".c_oflag=%04X ", tio->c_oflag); |
| printf(".c_cflag=%04X ", tio->c_cflag); |
| printf(".c_lflag=%04X ", tio->c_lflag); |
| printf(".c_cc[VTIME]='%d' ", tio->c_cc[VTIME]); |
| printf(".c_cc[VMIN]='%d'", tio->c_cc[VMIN]); |
| printf("\n"); |
| } |
| */ |
| |
| /* |
| * Attempts to get a single key from the console |
| * returns key pressed |
| */ |
| int rosh_getkey(void) |
| { |
| int inc; |
| |
| inc = KEY_NONE; |
| while (inc == KEY_NONE) |
| inc = get_key(stdin, 6000); |
| return inc; |
| } /* rosh_getkey */ |
| |
| /* |
| * Qualifies a filename relative to the working directory |
| * filestr Filename to qualify |
| * pwdstr working directory |
| * returns qualified file name string |
| */ |
| void rosh_qualify_filestr(char *filestr, const char *ifilstr, |
| const char *pwdstr) |
| { |
| int filepos = 0; |
| if ((filestr) && (pwdstr) && (ifilstr)) { |
| if (ifilstr[0] != SEP) { |
| strcpy(filestr, pwdstr); |
| filepos = strlen(pwdstr); |
| if (filestr[filepos - 1] != SEP) |
| filestr[filepos++] = SEP; |
| } |
| strcpy(filestr + filepos, ifilstr); |
| ROSH_DEBUG("--'%s'\n", filestr); |
| } |
| } |
| |
| /* Concatenate multiple files to stdout |
| * argc Argument Count |
| * argv Argument Values |
| */ |
| void rosh_cat(int argc, char *argv[]) |
| { |
| FILE *f; |
| char buf[ROSH_BUF_SZ]; |
| int i, numrd; |
| |
| for (i = 0; i < argc; i++) { |
| printf("--File = '%s'\n", argv[i]); |
| errno = 0; |
| f = fopen(argv[i], "r"); |
| if (f != NULL) { |
| numrd = fread(buf, 1, ROSH_BUF_SZ, f); |
| while (numrd > 0) { |
| fwrite(buf, 1, numrd, stdout); |
| numrd = fread(buf, 1, ROSH_BUF_SZ, f); |
| } |
| fclose(f); |
| } else { |
| rosh_error(errno, "cat", argv[i]); |
| errno = 0; |
| } |
| } |
| } /* rosh_cat */ |
| |
| /* Change PWD (Present Working Directory) |
| * argc Argument count |
| * argv Argument values |
| * ipwdstr Initial PWD |
| */ |
| void rosh_cd(int argc, char *argv[], const char *ipwdstr) |
| { |
| int rv = 0; |
| #ifdef DO_DEBUG |
| char filestr[ROSH_PATH_SZ]; |
| #endif /* DO_DEBUG */ |
| ROSH_DEBUG("CMD: \n"); |
| ROSH_DEBUG_ARGV_V(argc, argv); |
| errno = 0; |
| if (argc == 2) |
| rv = chdir(argv[1]); |
| else if (argc == 1) |
| rv = chdir(ipwdstr); |
| else |
| rosh_help(2, argv[0]); |
| if (rv != 0) { |
| if (argc == 2) |
| rosh_error(errno, "cd", argv[1]); |
| else |
| rosh_error(errno, "cd", ipwdstr); |
| errno = 0; |
| } else { |
| #ifdef DO_DEBUG |
| if (getcwd(filestr, ROSH_PATH_SZ)) |
| ROSH_DEBUG(" %s\n", filestr); |
| #endif /* DO_DEBUG */ |
| } |
| } /* rosh_cd */ |
| |
| /* Print the syslinux config file name |
| */ |
| void rosh_cfg(void) |
| { |
| printf("CFG: '%s'\n", syslinux_config_file()); |
| } /* rosh_cfg */ |
| |
| /* Echo a string back to the screen |
| * cmdstr command string to process |
| */ |
| void rosh_echo(const char *cmdstr) |
| { |
| int bpos = 0; |
| ROSH_DEBUG("CMD: '%s'\n", cmdstr); |
| bpos = rosh_search_nonsp(cmdstr, rosh_search_sp(cmdstr, 0)); |
| if (bpos > 1) { |
| ROSH_DEBUG(" bpos=%d\n", bpos); |
| printf("'%s'\n", cmdstr + bpos); |
| } else { |
| puts(""); |
| } |
| } /* rosh_echo */ |
| |
| /* Process argc/argv to optarr |
| * argc Argument count |
| * argv Argument values |
| * optarr option array to populate |
| */ |
| void rosh_ls_arg_opt(int argc, char *argv[], int optarr[]) |
| { |
| int rv = 0; |
| |
| optarr[0] = -1; |
| optarr[1] = -1; |
| optarr[2] = -1; |
| rosh_getopt_reset(); |
| while (rv != -1) { |
| ROSH_DEBUG2("getopt optind=%d rv=%d\n", optind, rv); |
| rv = getopt(argc, argv, rosh_ls_opt_str); |
| switch (rv) { |
| case 'l': |
| case 0: |
| optarr[0] = 1; |
| break; |
| case 'F': |
| case 1: |
| optarr[1] = 1; |
| break; |
| case 'i': |
| case 2: |
| optarr[2] = 1; |
| break; |
| case '?': |
| case -1: |
| default: |
| ROSH_DEBUG2("getopt optind=%d rv=%d\n", optind, rv); |
| break; |
| } |
| } |
| ROSH_DEBUG2(" end getopt optind=%d rv=%d\n", optind, rv); |
| ROSH_DEBUG2("\tIn rosh_ls_arg_opt() opt[0]=%d\topt[1]=%d\topt[2]=%d\n", optarr[0], optarr[1], |
| optarr[2]); |
| } /* rosh_ls_arg_opt */ |
| |
| /* Retrieve the size of a file argument |
| * filestr directory name of directory entry |
| * de directory entry |
| */ |
| int rosh_ls_de_size(const char *filestr, struct dirent *de) |
| { |
| int de_size; |
| char filestr2[ROSH_PATH_SZ]; |
| int fd2, file2pos; |
| struct stat fdstat; |
| |
| filestr2[0] = 0; |
| file2pos = -1; |
| if (filestr) { |
| file2pos = strlen(filestr); |
| memcpy(filestr2, filestr, file2pos); |
| filestr2[file2pos] = '/'; |
| } |
| strcpy(filestr2 + file2pos + 1, de->d_name); |
| fd2 = open(filestr2, O_RDONLY); |
| fstat(fd2, &fdstat); |
| fd2 = close(fd2); |
| de_size = (int)fdstat.st_size; |
| return de_size; |
| } /* rosh_ls_de_size */ |
| |
| /* Retrieve the size and mode of a file |
| * filestr directory name of directory entry |
| * de directory entry |
| */ |
| int rosh_ls_de_size_mode(const char *filestr, struct dirent *de, mode_t * st_mode) |
| { |
| int de_size; |
| char filestr2[ROSH_PATH_SZ]; |
| int file2pos; |
| struct stat fdstat; |
| int status; |
| |
| filestr2[0] = 0; |
| file2pos = -1; |
| memset(&fdstat, 0, sizeof fdstat); |
| ROSH_DEBUG2("ls:dsm(%s, %s) ", filestr, de->d_name); |
| if (filestr) { |
| /* FIXME: prevent string overflow */ |
| file2pos = strlen(filestr); |
| memcpy(filestr2, filestr, file2pos); |
| if (( filestr2[file2pos - 1] == SEP )) { |
| file2pos--; |
| } else { |
| filestr2[file2pos] = SEP; |
| } |
| } |
| strcpy(filestr2 + file2pos + 1, de->d_name); |
| errno = 0; |
| ROSH_DEBUG2("stat(%s) ", filestr2); |
| status = stat(filestr2, &fdstat); |
| (void)status; |
| ROSH_DEBUG2("\t--stat()=%d\terr=%d\n", status, errno); |
| if (errno) { |
| rosh_error(errno, "ls:szmd.stat", de->d_name); |
| errno = 0; |
| } |
| de_size = (int)fdstat.st_size; |
| *st_mode = fdstat.st_mode; |
| return de_size; |
| } /* rosh_ls_de_size_mode */ |
| |
| /* Returns the Inode number if fdstat contains it |
| * fdstat struct to extract inode from if not COM32, for now |
| */ |
| long rosh_ls_d_ino(struct stat *fdstat) |
| { |
| long de_ino; |
| #ifdef __COM32__ |
| if (fdstat) |
| de_ino = -1; |
| else |
| de_ino = 0; |
| #else /* __COM32__ */ |
| de_ino = fdstat->st_ino; |
| #endif /* __COM32__ */ |
| return de_ino; |
| } |
| |
| /* Convert a d_type to a single char in human readable format |
| * d_type d_type to convert |
| * returns human readable single character; a space if other |
| */ |
| char rosh_d_type2char_human(unsigned char d_type) |
| { |
| char ret; |
| switch (d_type) { |
| case DT_UNKNOWN: |
| ret = 'U'; |
| break; /* Unknown */ |
| case DT_FIFO: |
| ret = 'F'; |
| break; /* FIFO */ |
| case DT_CHR: |
| ret = 'C'; |
| break; /* Char Dev */ |
| case DT_DIR: |
| ret = 'D'; |
| break; /* Directory */ |
| case DT_BLK: |
| ret = 'B'; |
| break; /* Block Dev */ |
| case DT_REG: |
| ret = 'R'; |
| break; /* Regular File */ |
| case DT_LNK: |
| ret = 'L'; |
| break; /* Link, Symbolic */ |
| case DT_SOCK: |
| ret = 'S'; |
| break; /* Socket */ |
| case DT_WHT: |
| ret = 'W'; |
| break; /* UnionFS Whiteout */ |
| default: |
| ret = ' '; |
| } |
| return ret; |
| } /* rosh_d_type2char_human */ |
| |
| /* Convert a d_type to a single char by ls's prefix standards for -l |
| * d_type d_type to convert |
| * returns ls style single character; a space if other |
| */ |
| char rosh_d_type2char_lspre(unsigned char d_type) |
| { |
| char ret; |
| switch (d_type) { |
| case DT_FIFO: |
| ret = 'p'; |
| break; |
| case DT_CHR: |
| ret = 'c'; |
| break; |
| case DT_DIR: |
| ret = 'd'; |
| break; |
| case DT_BLK: |
| ret = 'b'; |
| break; |
| case DT_REG: |
| ret = '-'; |
| break; |
| case DT_LNK: |
| ret = 'l'; |
| break; |
| case DT_SOCK: |
| ret = 's'; |
| break; |
| default: |
| ret = '?'; |
| } |
| return ret; |
| } /* rosh_d_type2char_lspre */ |
| |
| /* Convert a d_type to a single char by ls's classify (-F) suffix standards |
| * d_type d_type to convert |
| * returns ls style single character; a space if other |
| */ |
| char rosh_d_type2char_lssuf(unsigned char d_type) |
| { |
| char ret; |
| switch (d_type) { |
| case DT_FIFO: |
| ret = '|'; |
| break; |
| case DT_DIR: |
| ret = '/'; |
| break; |
| case DT_LNK: |
| ret = '@'; |
| break; |
| case DT_SOCK: |
| ret = '='; |
| break; |
| default: |
| ret = ' '; |
| } |
| return ret; |
| } /* rosh_d_type2char_lssuf */ |
| |
| /* Converts data in the "other" place of st_mode to a ls-style string |
| * st_mode Mode in other to analyze |
| * st_mode_str string to hold converted string |
| */ |
| void rosh_st_mode_am2str(mode_t st_mode, char *st_mode_str) |
| { |
| st_mode_str[0] = ((st_mode & S_IROTH) ? 'r' : '-'); |
| st_mode_str[1] = ((st_mode & S_IWOTH) ? 'w' : '-'); |
| st_mode_str[2] = ((st_mode & S_IXOTH) ? 'x' : '-'); |
| } |
| |
| /* Converts st_mode to an ls-style string |
| * st_mode mode to convert |
| * st_mode_str string to hold converted string |
| */ |
| void rosh_st_mode2str(mode_t st_mode, char *st_mode_str) |
| { |
| st_mode_str[0] = rosh_d_type2char_lspre(IFTODT(st_mode)); |
| rosh_st_mode_am2str((st_mode & S_IRWXU) >> 6, st_mode_str + 1); |
| rosh_st_mode_am2str((st_mode & S_IRWXG) >> 3, st_mode_str + 4); |
| rosh_st_mode_am2str(st_mode & S_IRWXO, st_mode_str + 7); |
| st_mode_str[10] = 0; |
| } /* rosh_st_mode2str */ |
| |
| /* Output a single entry |
| * filestr directory name to list |
| * de directory entry |
| * optarr Array of options |
| */ |
| void rosh_ls_arg_dir_de(const char *filestr, struct dirent *de, const int *optarr) |
| { |
| int de_size; |
| mode_t st_mode; |
| char st_mode_str[11]; |
| st_mode = 0; |
| ROSH_DEBUG2("+"); |
| if (optarr[2] > -1) |
| printf("%10d ", (int)(de->d_ino)); |
| if (optarr[0] > -1) { |
| de_size = rosh_ls_de_size_mode(filestr, de, &st_mode); |
| rosh_st_mode2str(st_mode, st_mode_str); |
| ROSH_DEBUG2("%04X ", st_mode); |
| printf("%s %10d ", st_mode_str, de_size); |
| } |
| ROSH_DEBUG("'"); |
| printf("%s", de->d_name); |
| ROSH_DEBUG("'"); |
| if (optarr[1] > -1) |
| printf("%c", rosh_d_type2char_lssuf(de->d_type)); |
| printf("\n"); |
| } /* rosh_ls_arg_dir_de */ |
| |
| /* Output listing of a regular directory |
| * filestr directory name to list |
| * d the open DIR |
| * optarr Array of options |
| NOTE:This is where I could use qsort |
| */ |
| void rosh_ls_arg_dir(const char *filestr, DIR * d, const int *optarr) |
| { |
| struct dirent *de; |
| int filepos; |
| |
| filepos = 0; |
| errno = 0; |
| while ((de = readdir(d))) { |
| filepos++; |
| rosh_ls_arg_dir_de(filestr, de, optarr); |
| } |
| if (errno) { |
| rosh_error(errno, "ls:arg_dir", filestr); |
| errno = 0; |
| } else { if (filepos == 0) |
| ROSH_DEBUG("0 files found"); |
| } |
| } /* rosh_ls_arg_dir */ |
| |
| /* Simple directory listing for one argument (file/directory) based on |
| * filestr and pwdstr |
| * ifilstr input filename/directory name to list |
| * pwdstr Present Working Directory string |
| * optarr Option Array |
| */ |
| void rosh_ls_arg(const char *filestr, const int *optarr) |
| { |
| struct stat fdstat; |
| int status; |
| // char filestr[ROSH_PATH_SZ]; |
| // int filepos; |
| DIR *d; |
| struct dirent de; |
| |
| /* Initialization; make filestr based on leading character of ifilstr |
| and pwdstr */ |
| // rosh_qualify_filestr(filestr, ifilstr, pwdstr); |
| fdstat.st_mode = 0; |
| fdstat.st_size = 0; |
| ROSH_DEBUG("\topt[0]=%d\topt[1]=%d\topt[2]=%d\n", optarr[0], optarr[1], |
| optarr[2]); |
| |
| /* Now, the real work */ |
| errno = 0; |
| status = stat(filestr, &fdstat); |
| if (status == 0) { |
| if (S_ISDIR(fdstat.st_mode)) { |
| ROSH_DEBUG("PATH '%s' is a directory\n", filestr); |
| if ((d = opendir(filestr))) { |
| rosh_ls_arg_dir(filestr, d, optarr); |
| closedir(d); |
| } else { |
| rosh_error(errno, "ls", filestr); |
| errno = 0; |
| } |
| } else { |
| de.d_ino = rosh_ls_d_ino(&fdstat); |
| de.d_type = (IFTODT(fdstat.st_mode)); |
| strcpy(de.d_name, filestr); |
| if (S_ISREG(fdstat.st_mode)) { |
| ROSH_DEBUG("PATH '%s' is a regular file\n", filestr); |
| } else { |
| ROSH_DEBUG("PATH '%s' is some other file\n", filestr); |
| } |
| rosh_ls_arg_dir_de(NULL, &de, optarr); |
| /* if (ifilstr[0] == SEP) |
| rosh_ls_arg_dir_de(NULL, &de, optarr); |
| else |
| rosh_ls_arg_dir_de(pwdstr, &de, optarr);*/ |
| } |
| } else { |
| rosh_error(errno, "ls", filestr); |
| errno = 0; |
| } |
| return; |
| } /* rosh_ls_arg */ |
| |
| /* Parse options that may be present in the cmdstr |
| * filestr Possible option string to parse |
| * optstr Current options |
| * returns 1 if filestr does not begin with '-' else 0 |
| */ |
| int rosh_ls_parse_opt(const char *filestr, char *optstr) |
| { |
| int ret; |
| if (filestr[0] == '-') { |
| ret = 0; |
| if (optstr) |
| strcat(optstr, filestr + 1); |
| } else { |
| ret = 1; |
| } |
| ROSH_DEBUG("ParseOpt: '%s'\n\topt: '%s'\n\tret: %d\n", filestr, optstr, |
| ret); |
| return ret; |
| } /* rosh_ls_parse_opt */ |
| |
| /* List Directory |
| * argc Argument count |
| * argv Argument values |
| */ |
| void rosh_ls(int argc, char *argv[]) |
| { |
| int optarr[3]; |
| int i; |
| |
| rosh_ls_arg_opt(argc, argv, optarr); |
| ROSH_DEBUG2("In ls()\n"); |
| ROSH_DEBUG2_ARGV_V(argc, argv); |
| #ifdef DO_DEBUG |
| optarr[0] = 2; |
| #endif /* DO_DEBUG */ |
| ROSH_DEBUG2(" argc=%d; optind=%d\n", argc, optind); |
| if (optind >= argc) |
| rosh_ls_arg(".", optarr); |
| for (i = optind; i < argc; i++) { |
| rosh_ls_arg(argv[i], optarr); |
| } |
| } /* rosh_ls */ |
| |
| /* Simple directory listing; calls rosh_ls() |
| * argc Argument count |
| * argv Argument values |
| */ |
| void rosh_dir(int argc, char *argv[]) |
| { |
| ROSH_DEBUG(" dir implemented as ls\n"); |
| rosh_ls(argc, argv); |
| } /* rosh_dir */ |
| |
| /* Page through a buffer string |
| * buf Buffer to page through |
| */ |
| void rosh_more_buf(char *buf, int buflen, int rows, int cols, char *scrbuf) |
| { |
| char *bufp, *bufeol, *bufeol2; /* Pointer to current and next |
| end-of-line position in buffer */ |
| int bufpos, bufcnt; /* current position, count characters */ |
| int inc; |
| int i, numln; /* Index, Number of lines */ |
| int elpl; /* Extra lines per line read */ |
| |
| (void)cols; |
| |
| bufpos = 0; |
| bufp = buf + bufpos; |
| bufeol = bufp; |
| numln = rows - 1; |
| ROSH_DEBUG("--(%d)\n", buflen); |
| while (bufpos < buflen) { |
| for (i = 0; i < numln; i++) { |
| bufeol2 = strchr(bufeol, '\n'); |
| if (bufeol2 == NULL) { |
| bufeol = buf + buflen; |
| i = numln; |
| } else { |
| elpl = ((bufeol2 - bufeol - 1) / cols); |
| if (elpl < 0) |
| elpl = 0; |
| i += elpl; |
| ROSH_DEBUG2(" %d/%d ", elpl, i+1); |
| /* If this will not push too much, use it */ |
| /* but if it's the first line, use it */ |
| /* //HERE: We should probably snip the line off */ |
| if ((i < numln) || (i == elpl)) |
| bufeol = bufeol2 + 1; |
| } |
| } |
| ROSH_DEBUG2("\n"); |
| bufcnt = bufeol - bufp; |
| printf("--(%d/%d @%d)\n", bufcnt, buflen, bufpos); |
| memcpy(scrbuf, bufp, bufcnt); |
| scrbuf[bufcnt] = 0; |
| printf("%s", scrbuf); |
| bufp = bufeol; |
| bufpos += bufcnt; |
| if (bufpos == buflen) |
| break; |
| inc = rosh_getkey(); |
| numln = 1; |
| switch (inc) { |
| case KEY_CTRL('c'): |
| case 'q': |
| case 'Q': |
| bufpos = buflen; |
| break; |
| case ' ': |
| numln = rows - 1; |
| } |
| } |
| } /* rosh_more_buf */ |
| |
| /* Page through a single file using the open file stream |
| * fd File Descriptor |
| */ |
| void rosh_more_fd(int fd, int rows, int cols, char *scrbuf) |
| { |
| struct stat fdstat; |
| char *buf; |
| int bufpos; |
| int numrd; |
| FILE *f; |
| |
| fstat(fd, &fdstat); |
| if (S_ISREG(fdstat.st_mode)) { |
| buf = malloc((int)fdstat.st_size); |
| if (buf != NULL) { |
| f = fdopen(fd, "r"); |
| bufpos = 0; |
| numrd = fread(buf, 1, (int)fdstat.st_size, f); |
| while (numrd > 0) { |
| bufpos += numrd; |
| numrd = fread(buf + bufpos, 1, |
| ((int)fdstat.st_size - bufpos), f); |
| } |
| fclose(f); |
| rosh_more_buf(buf, bufpos, rows, cols, scrbuf); |
| } |
| } else { |
| } |
| |
| } /* rosh_more_fd */ |
| |
| /* Page through a file like the more command |
| * argc Argument Count |
| * argv Argument Values |
| */ |
| void rosh_more(int argc, char *argv[]) |
| { |
| int fd, i; |
| /* char filestr[ROSH_PATH_SZ]; |
| int cmdpos;*/ |
| int rows, cols; |
| char *scrbuf; |
| int ret; |
| |
| ROSH_DEBUG_ARGV_V(argc, argv); |
| ret = getscreensize(1, &rows, &cols); |
| if (ret) { |
| ROSH_DEBUG("getscreensize() fail(%d); fall back\n", ret); |
| ROSH_DEBUG("\tROWS='%d'\tCOLS='%d'\n", rows, cols); |
| /* If either fail, go under normal size, just in case */ |
| if (!rows) |
| rows = 20; |
| if (!cols) |
| cols = 75; |
| } |
| ROSH_DEBUG("\tUSE ROWS='%d'\tCOLS='%d'\n", rows, cols); |
| /* 32 bit align beginning of row and over allocate */ |
| scrbuf = malloc(rows * ((cols+3)&(INT_MAX - 3))); |
| if (!scrbuf) |
| return; |
| |
| if (argc) { |
| /* There is no need to mess up the console if we don't have a |
| file */ |
| rosh_console_raw(); |
| for (i = 0; i < argc; i++) { |
| printf("--File = '%s'\n", argv[i]); |
| errno = 0; |
| fd = open(argv[i], O_RDONLY); |
| if (fd != -1) { |
| rosh_more_fd(fd, rows, cols, scrbuf); |
| close(fd); |
| } else { |
| rosh_error(errno, "more", argv[i]); |
| errno = 0; |
| } |
| } |
| rosh_console_std(); |
| } |
| free(scrbuf); |
| } /* rosh_more */ |
| |
| /* Page a file with rewind |
| * argc Argument Count |
| * argv Argument Values |
| */ |
| void rosh_less(int argc, char *argv[]) |
| { |
| printf(" less implemented as more (for now)\n"); |
| rosh_more(argc, argv); |
| } /* rosh_less */ |
| |
| /* Show PWD |
| */ |
| void rosh_pwd(void) |
| { |
| char pwdstr[ROSH_PATH_SZ]; |
| errno = 0; |
| if (getcwd(pwdstr, ROSH_PATH_SZ)) { |
| printf("%s\n", pwdstr); |
| } else { |
| rosh_error(errno, "pwd", ""); |
| errno = 0; |
| } |
| } /* rosh_pwd */ |
| |
| /* Reboot; use warm reboot if one of certain options set |
| * argc Argument count |
| * argv Argument values |
| */ |
| void rosh_reboot(int argc, char *argv[]) |
| { |
| int rtype = 0; |
| if (argc) { |
| /* For now, just use the first */ |
| switch (argv[0][0]) { |
| case '1': |
| case 's': |
| case 'w': |
| rtype = 1; |
| break; |
| case '-': |
| switch (argv[0][1]) { |
| case '1': |
| case 's': |
| case 'w': |
| rtype = 1; |
| break; |
| } |
| break; |
| } |
| } |
| syslinux_reboot(rtype); |
| } /* rosh_reboot */ |
| |
| /* Run a boot string, calling syslinux_run_command |
| * argc Argument count |
| * argv Argument values |
| */ |
| void rosh_run(int argc, char *argv[]) |
| { |
| char cmdstr[ROSH_CMD_SZ]; |
| int len; |
| |
| len = rosh_argcat(cmdstr, ROSH_CMD_SZ, argc, argv, 0); |
| if (len) { |
| printf("--run: '%s'\n", cmdstr); |
| syslinux_run_command(cmdstr); |
| } else { |
| printf(APP_NAME ":run: No arguments\n"); |
| } |
| } /* rosh_run */ |
| |
| /* Process an argc/argv pair and call handling function |
| * argc Argument count |
| * argv Argument values |
| * ipwdstr Initial Present Working Directory string |
| * returns Whether to exit prompt |
| */ |
| char rosh_command(int argc, char *argv[], const char *ipwdstr) |
| { |
| char do_exit = false; |
| int tlen; |
| tlen = strlen(argv[0]); |
| ROSH_DEBUG_ARGV_V(argc, argv); |
| switch (argv[0][0]) { |
| case 'e': |
| case 'E': |
| case 'q': |
| case 'Q': |
| switch (argv[0][1]) { |
| case 0: |
| case 'x': |
| case 'X': |
| case 'u': |
| case 'U': |
| if ((strncasecmp("exit", argv[0], tlen) == 0) || |
| (strncasecmp("quit", argv[0], tlen) == 0)) |
| do_exit = true; |
| else |
| rosh_help(1, argv[0]); |
| break; |
| case 'c': |
| case 'C': |
| if (strncasecmp("echo", argv[0], tlen) == 0) |
| rosh_pr_argv(argc - 1, &argv[1]); |
| else |
| rosh_help(1, argv[0]); |
| break; |
| default: |
| rosh_help(1, argv[0]); |
| } |
| break; |
| case 'c': |
| case 'C': /* run 'cd' 'cat' 'cfg' */ |
| switch (argv[0][1]) { |
| case 'a': |
| case 'A': |
| if (strncasecmp("cat", argv[0], tlen) == 0) |
| rosh_cat(argc - 1, &argv[1]); |
| else |
| rosh_help(1, argv[0]); |
| break; |
| case 'd': |
| case 'D': |
| if (strncasecmp("cd", argv[0], tlen) == 0) |
| rosh_cd(argc, argv, ipwdstr); |
| else |
| rosh_help(1, argv[0]); |
| break; |
| case 'f': |
| case 'F': |
| if (strncasecmp("cfg", argv[0], tlen) == 0) |
| rosh_cfg(); |
| else |
| rosh_help(1, argv[0]); |
| break; |
| default: |
| rosh_help(1, argv[0]); |
| } |
| break; |
| case 'd': |
| case 'D': /* run 'dir' */ |
| if (strncasecmp("dir", argv[0], tlen) == 0) |
| rosh_dir(argc - 1, &argv[1]); |
| else |
| rosh_help(1, argv[0]); |
| break; |
| case 'h': |
| case 'H': |
| case '?': |
| if ((strncasecmp("help", argv[0], tlen) == 0) || (tlen == 1)) |
| rosh_help(2, argv[1]); |
| else |
| rosh_help(1, NULL); |
| break; |
| case 'l': |
| case 'L': /* run 'ls' 'less' */ |
| switch (argv[0][1]) { |
| case 0: |
| case 's': |
| case 'S': |
| if (strncasecmp("ls", argv[0], tlen) == 0) |
| rosh_ls(argc, argv); |
| else |
| rosh_help(1, argv[0]); |
| break; |
| case 'e': |
| case 'E': |
| if (strncasecmp("less", argv[0], tlen) == 0) |
| rosh_less(argc - 1, &argv[1]); |
| else |
| rosh_help(1, argv[0]); |
| break; |
| default: |
| rosh_help(1, argv[0]); |
| } |
| break; |
| case 'm': |
| case 'M': |
| switch (argv[0][1]) { |
| case 'a': |
| case 'A': |
| if (strncasecmp("man", argv[0], tlen) == 0) |
| rosh_help(2, argv[1]); |
| else |
| rosh_help(1, argv[0]); |
| break; |
| case 'o': |
| case 'O': |
| if (strncasecmp("more", argv[0], tlen) == 0) |
| rosh_more(argc - 1, &argv[1]); |
| else |
| rosh_help(1, argv[0]); |
| break; |
| default: |
| rosh_help(1, argv[0]); |
| } |
| break; |
| case 'p': |
| case 'P': /* run 'pwd' */ |
| if (strncasecmp("pwd", argv[0], tlen) == 0) |
| rosh_pwd(); |
| else |
| rosh_help(1, argv[0]); |
| break; |
| case 'r': |
| case 'R': /* run 'run' */ |
| switch (argv[0][1]) { |
| case 0: |
| case 'e': |
| case 'E': |
| if (strncasecmp("reboot", argv[0], tlen) == 0) |
| rosh_reboot(argc - 1, &argv[1]); |
| else |
| rosh_help(1, argv[0]); |
| break; |
| case 'u': |
| case 'U': |
| if (strncasecmp("run", argv[0], tlen) == 0) |
| rosh_run(argc - 1, &argv[1]); |
| else |
| rosh_help(1, argv[0]); |
| break; |
| default: |
| rosh_help(1, argv[0]); |
| } |
| break; |
| case 'v': |
| case 'V': |
| if (strncasecmp("version", argv[0], tlen) == 0) |
| rosh_version(1); |
| else |
| rosh_help(1, argv[0]); |
| break; |
| case 0: |
| case '\n': |
| break; |
| default: |
| rosh_help(1, argv[0]); |
| } /* switch(argv[0][0]) */ |
| return do_exit; |
| } /* rosh_command */ |
| |
| /* Process the prompt for commands as read from stdin and call rosh_command |
| * to process command line string |
| * icmdstr Initial command line string |
| * returns Exit status |
| */ |
| int rosh_prompt(int iargc, char *iargv[]) |
| { |
| int rv; |
| char cmdstr[ROSH_CMD_SZ]; |
| char ipwdstr[ROSH_PATH_SZ]; |
| char do_exit; |
| char **argv; |
| int argc; |
| |
| rv = 0; |
| do_exit = false; |
| if (!getcwd(ipwdstr, ROSH_PATH_SZ)) |
| strcpy(ipwdstr, "./"); |
| if (iargc > 1) |
| do_exit = rosh_command(iargc - 1, &iargv[1], ipwdstr); |
| while (!(do_exit)) { |
| /* Extra preceeding newline */ |
| printf("\nrosh: "); |
| /* Read a line from console */ |
| if (fgets(cmdstr, ROSH_CMD_SZ, stdin)) { |
| argc = rosh_str2argv(&argv, cmdstr); |
| do_exit = rosh_command(argc, argv, ipwdstr); |
| rosh_free_argv(&argv); |
| } else { |
| do_exit = false; |
| } |
| } |
| return rv; |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| int rv; |
| |
| /* Initialization */ |
| rv = 0; |
| rosh_console_std(); |
| if (argc == 1) { |
| rosh_version(0); |
| print_beta(); |
| } else { |
| #ifdef DO_DEBUG |
| char cmdstr[ROSH_CMD_SZ]; |
| rosh_argcat(cmdstr, ROSH_CMD_SZ, argc, argv, 1); |
| ROSH_DEBUG("arg='%s'\n", cmdstr); |
| #endif |
| } |
| rv = rosh_prompt(argc, argv); |
| printf("--Exiting '" APP_NAME "'\n"); |
| return rv; |
| } |