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