VIS
|
00001 00032 #include "cmdInt.h" 00033 00034 #if HAVE_READLINE_READLINE_H 00035 #include <readline/readline.h> 00036 #else 00037 EXTERN char *readline(char *); 00038 #endif 00039 #if HAVE_READLINE_HISTORY_H 00040 #include <readline/history.h> 00041 #else 00042 EXTERN void add_history(char *); 00043 #endif 00044 00045 static char rcsid[] UNUSED = "$Id: cmdFile.c,v 1.24 2009/04/11 18:25:50 fabio Exp $"; 00046 00047 /*---------------------------------------------------------------------------*/ 00048 /* Constant declarations */ 00049 /*---------------------------------------------------------------------------*/ 00050 #ifdef ESC 00051 #undef ESC 00052 #endif 00053 #define ESC '\033' 00054 #define BEEP '\007' 00055 #define HIST '%' 00056 #define SUBST '^' 00057 00058 #define STDIN 0 00059 #define STDOUT 1 00060 00061 00062 /*---------------------------------------------------------------------------*/ 00063 /* Variable declarations */ 00064 /*---------------------------------------------------------------------------*/ 00065 static char visHistChar = HIST; /* can be changed by "set hist_char" */ 00066 static char *seperator = " \t\n;"; 00067 00068 00071 /*---------------------------------------------------------------------------*/ 00072 /* Static function prototypes */ 00073 /*---------------------------------------------------------------------------*/ 00074 00075 #if HAVE_IOCTL_WITH_TIOCGETC 00076 static int cmp(const void * s1, const void * s2); 00077 static int match(char * newmatch, char * lastmatch, char * actual); 00078 #endif 00079 static int getnum(char ** linep); 00080 static char * getarg(char * line, int num); 00081 static char * bad_event(int n); 00082 static char * do_subst(char * dest, char * new_); 00083 static void print_prompt(char * prompt); 00084 #if HAVE_LIBREADLINE 00085 static char * removeWhiteSpaces(char *string); 00086 #endif 00087 00091 /*---------------------------------------------------------------------------*/ 00092 /* Definition of exported functions */ 00093 /*---------------------------------------------------------------------------*/ 00094 00095 00096 00113 FILE * 00114 Cmd_FileOpen( 00115 char * fileName, 00116 char * mode, 00117 char ** realFileName_p, 00118 int silent) 00119 { 00120 char *realFileName, *path, *user_path; 00121 char *lib_name; 00122 FILE *fp; 00123 00124 if (strcmp(fileName, "-") == 0) { 00125 if (strcmp(mode, "w") == 0) { 00126 realFileName = util_strsav("stdout"); 00127 fp = stdout; 00128 } 00129 else { 00130 realFileName = util_strsav("stdin"); 00131 fp = stdin; 00132 } 00133 } 00134 else { 00135 realFileName = NIL(char); 00136 if (strcmp(mode, "r") == 0) { 00137 user_path = Cmd_FlagReadByName("open_path"); 00138 if (user_path != NIL(char)) { 00139 lib_name = Vm_VisObtainLibrary(); 00140 path = ALLOC(char, strlen(user_path)+strlen(lib_name)+10); 00141 (void) sprintf(path, "%s:%s", user_path, lib_name); 00142 00143 /* 00144 * If the fileName begins with ./, ../, ~/, or /, AND the file doesn't 00145 * actually exist, then VIS will look in the open path (which includes 00146 * the sis library) for the file. This could lead to unexpected behavior: 00147 * the user is looking for ./msu.genlib, and since that isn't there, the 00148 * users gets sis_lib/msu.genlib, and no error is reported. The following 00149 * pseudo code fixes this: 00150 * 00151 * if (the beginning of file_name is : ./ || ../ || ~/ || /) { 00152 * realFileName = util_file_search(fileName, NIL(char), "r"); 00153 * } else 00154 */ 00155 realFileName = util_file_search(fileName, path, "r"); 00156 FREE(path); 00157 FREE(lib_name); 00158 } 00159 } 00160 if (realFileName == NIL(char)) { 00161 realFileName = util_tilde_expand(fileName); 00162 } 00163 if (strcmp(mode, "r") == 0 && !util_check_file(realFileName, mode)) { 00164 FREE(realFileName); 00165 return NIL(FILE); 00166 } 00167 if ((fp = fopen(realFileName, mode)) == NIL(FILE)) { 00168 if (! silent) { 00169 perror(realFileName); 00170 } 00171 } 00172 } 00173 if (realFileName_p != 0) { 00174 *realFileName_p = realFileName; 00175 } 00176 else { 00177 FREE(realFileName); 00178 } 00179 return fp; 00180 } 00181 00182 /*---------------------------------------------------------------------------*/ 00183 /* Definition of internal and static functions */ 00184 /*---------------------------------------------------------------------------*/ 00185 00186 #if HAVE_IOCTL_WITH_TIOCGETC 00187 00188 /* 00189 * Words are seperated by any of the characters in `seperator'. The seperator 00190 * is used to distinguish words from each other in file completion and history 00191 * substitution. The recommeded seperator string is " \t\n;". 00192 */ 00193 00194 static int cmp(const void * s1, const void * s2); 00195 static int match(char * newmatch, char * lastmatch, char * actual); 00196 static int getnum(char ** linep); 00197 00217 char * 00218 CmdFgetsFilec( 00219 char * buf, 00220 int size, 00221 FILE * stream, 00222 char * prompt) 00223 { 00224 int n_read, i, len, maxlen, col, sno, modname; 00225 struct tchars tchars, oldtchars; 00226 DIR *dir; 00227 struct dirent *dp; 00228 #if HAVE_LIBREADLINE 00229 char *dupline; 00230 char *cleanLine; 00231 #endif 00232 00233 #if HAVE_TERM_INTERRUPTS 00234 int omask; 00235 struct sgttyb tty, oldtty; /* To mask interuupts */ 00236 int pending = LPENDIN; 00237 #endif 00238 00239 char *last_word, *file, *path, *name, *line; 00240 char last_char, found[MAXNAMLEN]; 00241 array_t *names = NIL(array_t); /* initialize so that lint doesn't complain */ 00242 00243 sno = fileno(stream); 00244 if (sno != STDIN || !isatty(sno)){ 00245 if (prompt != NIL(char)){ 00246 (void) print_prompt(prompt); 00247 (void) fflush(stdout); 00248 } 00249 return (fgets(buf, size, stream)); 00250 } 00251 else if (Cmd_FlagReadByName("filec") == NIL(char)) { 00252 #if HAVE_LIBREADLINE 00253 /* Effectively read one line of input printing the prompt */ 00254 dupline = (char *)readline(prompt); 00255 cleanLine = removeWhiteSpaces(dupline); 00256 00257 /* Check if an EOF has been read */ 00258 if (cleanLine != NIL(char)) { 00259 /* If the line is non empty, add it to the history */ 00260 if (*cleanLine) { 00261 add_history(cleanLine); 00262 } 00263 00264 /* Copy the contents of cleanLine to buf, to simulate fgets */ 00265 strncpy(buf, cleanLine, size); 00266 if (strlen(cleanLine) >= size) { 00267 buf[size-1] = '\0'; 00268 } 00269 line = buf; 00270 } 00271 else { 00272 line = NIL(char); 00273 } 00274 FREE(dupline); 00275 #else 00276 /* Get rid of the trailing newline */ 00277 if (prompt != NIL(char)){ 00278 (void) print_prompt(prompt); 00279 (void) fflush(stdout); 00280 } 00281 line = fgets(buf, size, stream); 00282 if (line != NIL(char)) { 00283 len = strlen(line); 00284 if (len > 0 && line[len-1] == '\n') { 00285 line[len-1] = '\0'; 00286 } 00287 } 00288 #endif 00289 return line; 00290 } else { 00291 if (prompt != NIL(char)){ 00292 (void) print_prompt(prompt); 00293 (void) fflush(stdout); 00294 } 00295 } 00296 00297 /* Allow hitting ESCAPE to break a read() */ 00298 00299 (void) ioctl(sno, TIOCGETC, (char *) &tchars); 00300 oldtchars = tchars; 00301 tchars.t_brkc = ESC; 00302 (void) ioctl(sno, TIOCSETC, (char *) &tchars); 00303 00304 while ((n_read = read(sno, buf, size)) > 0) { 00305 buf[n_read] = '\0'; 00306 last_word = &buf[n_read - 1]; 00307 last_char = *last_word; 00308 if (last_char == '\n' || n_read == size) { 00309 (void) ioctl(sno, TIOCSETC, (char *) &oldtchars); 00310 *last_word = '\0'; 00311 return(buf); 00312 } 00313 if (last_char == ESC) { 00314 *last_word-- = '\0'; 00315 (void) fprintf(stdout, "\b\b \b\b"); 00316 } 00317 else { 00318 names = array_alloc(char *, 10); 00319 (void) fputc('\n', stdout); 00320 } 00321 for (; last_word >= buf; --last_word) { 00322 if (strchr(seperator, *last_word) != NIL(char)) { 00323 break; 00324 } 00325 } 00326 last_word++; 00327 file = strrchr(buf, '/'); 00328 if (file == NIL(char)) { 00329 file = last_word; 00330 modname = 0; 00331 path = "."; 00332 } 00333 else { 00334 *file++ = '\0'; 00335 modname = 1; 00336 path = (*last_word == '~') ? util_tilde_expand(last_word) : 00337 last_word; 00338 } 00339 len = strlen(file); 00340 dir = opendir(path); 00341 if (dir == NIL(DIR) || len > MAXNAMLEN) { 00342 (void) fputc(BEEP, stdout); 00343 } 00344 else { 00345 *found = '\0'; 00346 maxlen = 0; 00347 while ((dp = readdir(dir)) != NIL(struct dirent)) { 00348 if (strncmp(file, dp->d_name, len) == 0) { 00349 if (last_char == ESC) { 00350 if (match(dp->d_name, found, file) == 0) { 00351 break; 00352 } 00353 } 00354 else if (len != 0 || *(dp->d_name) != '.') { 00355 if (maxlen < NAMLEN(dp)) { 00356 maxlen = NAMLEN(dp); 00357 } 00358 array_insert_last(char *, names, util_strsav(dp->d_name)); 00359 } 00360 } 00361 } 00362 (void) closedir(dir); 00363 if (last_char == ESC) { 00364 if (*found == '\0' || strcmp(found, file) == 0) { 00365 (void) fputc(BEEP, stdout); 00366 } 00367 else { 00368 (void) strcpy(file, found); 00369 (void) fprintf(stdout, "%s", &buf[n_read - 1]); 00370 } 00371 } 00372 else { 00373 maxlen += 2; 00374 col = maxlen; 00375 array_sort(names, cmp); 00376 for (i = 0; i < array_n(names); i++) { 00377 name = array_fetch(char *, names, i); 00378 (void) fprintf(stdout, "%-*s", maxlen, name); 00379 FREE(name); 00380 col += maxlen; 00381 if (col >= 80) { 00382 col = maxlen; 00383 (void) fputc('\n', stdout); 00384 } 00385 } 00386 array_free(names); 00387 if (col != maxlen) { 00388 (void) fputc('\n', stdout); 00389 } 00390 } 00391 } 00392 (void) fflush(stdout); 00393 if (modname != 0) { 00394 if (path != last_word) { 00395 FREE(path); 00396 } 00397 *--file = '/'; 00398 } 00399 00400 #if HAVE_TERM_INTERRUPTS 00401 /* mask interrupts temporarily */ 00402 omask = sigblock(sigmask(SIGINT)); 00403 (void) ioctl(STDOUT, TIOCGETP, (char *)&tty); 00404 oldtty = tty; 00405 tty.sg_flags &= ~(ECHO|CRMOD); 00406 (void) ioctl(STDOUT, TIOCSETN, (char *)&tty); 00407 #endif 00408 00409 /* reprint prompt */ 00410 (void) write(STDOUT, "\r", 1); 00411 print_prompt(prompt); 00412 00413 /* shove chars from buf back into the input queue */ 00414 for (i = 0; buf[i]; i++) { 00415 (void) ioctl(STDOUT, TIOCSTI, &buf[i]); 00416 } 00417 #if HAVE_TERM_INTERRUPTS 00418 /* restore interrupts */ 00419 (void) ioctl(STDOUT, TIOCSETN, (char *)&oldtty); 00420 (void) sigsetmask(omask); 00421 (void) ioctl(STDOUT, TIOCLBIS, (char *) &pending); 00422 #endif 00423 } 00424 /* restore read() behavior */ 00425 (void) ioctl(sno, TIOCSETC, (char *) &oldtchars); 00426 return(NIL(char)); 00427 } 00428 00429 #else 00430 00442 char * 00443 CmdFgetsFilec( 00444 char * buf, 00445 int size, 00446 FILE * stream, 00447 char * prompt) 00448 { 00449 #if HAVE_LIBREADLINE 00450 char *dupline; 00451 char *cleanLine; 00452 #endif 00453 char *line; 00454 int sno; 00455 #if !HAVE_LIBREADLINE 00456 int len; 00457 #endif 00458 00459 sno = fileno(stream); 00460 if (sno != STDIN || !isatty(sno)){ 00461 if (prompt != NIL(char)){ 00462 (void) print_prompt(prompt); 00463 (void) fflush(stdout); 00464 } 00465 return (fgets(buf, size, stream)); 00466 } else { 00467 #if HAVE_LIBREADLINE 00468 /* Effectively read one line of input printing the prompt */ 00469 dupline = (char *)readline(prompt); 00470 cleanLine = removeWhiteSpaces(dupline); 00471 00472 /* Check if an EOF has been read */ 00473 if (cleanLine != NIL(char)) { 00474 /* If the line is non empty, add it to the history */ 00475 if (*cleanLine) { 00476 add_history(cleanLine); 00477 } 00478 00479 /* Copy the contents of cleanLine to buf, to simulate fgets */ 00480 strncpy(buf, cleanLine, size); 00481 if ((int) strlen(cleanLine) >= size) { 00482 buf[size-1] = '\0'; 00483 } 00484 line = buf; 00485 } 00486 else { 00487 line = NIL(char); 00488 } 00489 FREE(dupline); 00490 #else 00491 /* Get rid of the trailing newline */ 00492 if (prompt != NIL(char)){ 00493 (void) print_prompt(prompt); 00494 (void) fflush(stdout); 00495 } 00496 line = fgets(buf, size, stream); 00497 if (line != NIL(char)) { 00498 len = strlen(line); 00499 if (len > 0 && line[len-1] == '\n') { 00500 line[len-1] = '\0'; 00501 } 00502 } 00503 #endif 00504 return line; 00505 } 00506 00507 } 00508 00509 #endif /* HAVE_IOCTL_WITH_TIOCGETC */ 00510 00511 00512 #if HAVE_IOCTL_WITH_TIOCGETC 00513 00524 static int 00525 cmp( 00526 const void * s1, 00527 const void * s2) 00528 { 00529 return(strcmp(*(char **)s1, *(char **)s2)); 00530 } 00531 00532 00544 static int 00545 match( 00546 char * newmatch, 00547 char * lastmatch, 00548 char * actual) 00549 { 00550 int i = 0; 00551 00552 if (*actual == '\0' && *newmatch == '.') { 00553 return(1); 00554 } 00555 if (*lastmatch == '\0') { 00556 (void) strcpy(lastmatch, newmatch); 00557 return(1); 00558 } 00559 while (*newmatch++ == *lastmatch) { 00560 lastmatch++; 00561 i++; 00562 } 00563 *lastmatch = '\0'; 00564 return(i); 00565 } 00566 /* #endif */ /* defined(hpux) */ 00567 00568 #endif 00569 00570 00571 00600 char * 00601 CmdHistorySubstitution( 00602 char * line, 00603 int * changed) 00604 { 00605 static char buf[1024], c; 00606 char *value; 00607 char *last, *old, *new_, *start, *b, *l; 00608 int n, len, i, num, internal_change; 00609 00610 *changed = 0; 00611 internal_change = 0; 00612 while (isspace((int)(*line))) { 00613 line++; 00614 } 00615 if (*line == '\0') { 00616 return(line); 00617 } 00618 n = array_n(vm_commandHistoryArray); 00619 last = (n > 0) ? 00620 array_fetch(char *, vm_commandHistoryArray, n - 1) : (char *) ""; 00621 00622 b = buf; 00623 if (*line == SUBST) { 00624 old = line + 1; 00625 new_ = strchr(old, SUBST); 00626 if (new_ == NIL(char)) { 00627 goto bad_modify; 00628 } 00629 *new_++ = '\0'; /* makes change in contents of line */ 00630 start = strstr(last, old); 00631 if (start == NIL(char)) { 00632 *--new_ = SUBST; 00633 bad_modify: 00634 (void) fprintf(vis_stderr, "** cmd error: Modifier failed\n"); 00635 return(NIL(char)); 00636 } 00637 while (last != start) { 00638 *b++ = *last++; 00639 } 00640 b = do_subst(b, new_); 00641 last += strlen(old); 00642 while ((*b++ = *last++)) { 00643 } 00644 *changed = 1; 00645 return(buf); 00646 } 00647 00648 if ((value = Cmd_FlagReadByName("history_char")) != NIL(char)){ 00649 visHistChar = *value; 00650 } 00651 00652 for (l = line; (*b = *l); l++) { 00653 if (*l == visHistChar) { 00654 /* 00655 * If a \ immediately preceeds a HIST char, pass just HIST char 00656 * Otherwise pass both \ and the character. 00657 */ 00658 if (l > line && l[-1] == '\\') { 00659 b[-1] = visHistChar; 00660 internal_change = 1; 00661 continue; 00662 } 00663 if (n == 0) { 00664 return(bad_event(0)); 00665 } 00666 l++; 00667 /* Cannot use a switch since the history char is a variable !!! */ 00668 if (*l == visHistChar){ 00669 /* replace !! in line with last */ 00670 b = do_subst(b, last); 00671 } 00672 else if (*l == '$'){ 00673 /* replace !$ in line with last arg of last */ 00674 b = do_subst(b, getarg(last, -1)); 00675 } 00676 else if (*l == '*'){ 00677 b = do_subst(b, getarg(last, -2)); 00678 } 00679 else if (*l == ':'){ 00680 /* replace !:n in line with n'th arg of last */ 00681 l++; 00682 num = getnum(&l); 00683 new_ = getarg(last, num); 00684 if (new_ == NIL(char)) { 00685 (void) fprintf(vis_stderr, "** cmd error: Bad %c arg selector\n", visHistChar); 00686 return(NIL(char)); 00687 } 00688 b = do_subst(b, new_); 00689 } 00690 else if (*l == '-'){ 00691 /* replace !-n in line with n'th prev cmd */ 00692 l++; 00693 num = getnum(&l); 00694 if (num > n || num == 0) { 00695 return(bad_event(n - num + 1)); 00696 } 00697 b = do_subst(b, array_fetch(char *, vm_commandHistoryArray, n - num)); 00698 } 00699 else { 00700 /* replace !n in line with n'th command */ 00701 if (isdigit((int)(*l))) { 00702 num = getnum(&l); 00703 if (num > n || num == 0) { 00704 return(bad_event(num)); 00705 } 00706 b = do_subst(b, array_fetch(char *, vm_commandHistoryArray, num - 1)); 00707 } 00708 else { /* replace !boo w/ last cmd beginning w/ boo */ 00709 start = l; 00710 while (*l && strchr(seperator, *l) == NIL(char)) { 00711 l++; 00712 } 00713 c = *l; 00714 *l = '\0'; 00715 len = strlen(start); 00716 for (i = n - 1; i >= 0; i--) { 00717 old = array_fetch(char *, vm_commandHistoryArray, i); 00718 if (strncmp(old, start, len) == 0) { 00719 b = do_subst(b, old); 00720 break; 00721 } 00722 } 00723 if (i < 0) { 00724 (void) fprintf(vis_stderr, "** cmd error: Event not found: %s\n", start); 00725 *l = c; 00726 return(NIL(char)); 00727 } 00728 *l-- = c; 00729 00730 } 00731 } 00732 *changed = 1; 00733 } 00734 else { 00735 b++; 00736 } 00737 } 00738 if (*changed != 0 || internal_change != 0) { 00739 return(buf); 00740 } 00741 return(line); 00742 } 00743 00744 00756 static int 00757 getnum( 00758 char ** linep) 00759 { 00760 int num = 0; 00761 char *line = *linep; 00762 00763 for (; isdigit((int)(*line)); line++) { 00764 num *= 10; 00765 num += *line - '0'; 00766 } 00767 *linep = line - 1; 00768 return(num); 00769 } 00770 00771 00783 static char * 00784 getarg( 00785 char * line, 00786 int num) 00787 { 00788 static char buf[128]; 00789 char *b, *c; 00790 int i; 00791 00792 if (num == -1) { 00793 i = 123456; 00794 } 00795 else if (num == -2) { 00796 i = 1; 00797 } 00798 else { 00799 i = num; 00800 } 00801 00802 c = line; 00803 do { 00804 b = line = c; 00805 while (*line && strchr(seperator, *line) == NIL(char)) { 00806 line++; 00807 } 00808 c = line; 00809 while (*c && strchr(seperator, *c) != NIL(char)) { 00810 c++; 00811 } 00812 if (*c == '\0') { 00813 break; 00814 } 00815 } while (--i >= 0); 00816 00817 if (i > 0) { 00818 if (num == -1) { 00819 return(b); 00820 } 00821 return(NIL(char)); 00822 } 00823 if (num < 0) { 00824 return(b); 00825 } 00826 c = buf; 00827 do { 00828 *c++ = *b++; 00829 } while (b < line && c < &buf[127]); 00830 *c = '\0'; 00831 return(buf); 00832 } 00833 00834 00846 static char * 00847 bad_event( 00848 int n) 00849 { 00850 (void) fprintf(vis_stderr, "** cmd error: Event %d not found\n", n); 00851 return(NIL(char)); 00852 } 00853 00854 00866 static char * 00867 do_subst( 00868 char * dest, 00869 char * new_) 00870 { 00871 while ((*dest = *new_++)) { 00872 dest++; 00873 } 00874 return(dest); 00875 } 00876 00877 00889 static void 00890 print_prompt( 00891 char * prompt) 00892 { 00893 char buf[256]; 00894 00895 if (prompt == NIL(char)) return; 00896 00897 while (*prompt != '\0') { 00898 if (*prompt == visHistChar) { 00899 (void) sprintf(buf, "%d", array_n(vm_commandHistoryArray) + 1); 00900 if (write(STDOUT, buf, (int) strlen(buf)) == -1) exit(-1); 00901 } 00902 else { 00903 if (write(STDOUT, prompt, 1) == -1) exit(-1); 00904 } 00905 prompt++; 00906 } 00907 } 00908 00909 #ifdef HAVE_LIBREADLINE 00910 00917 static char * 00918 removeWhiteSpaces( 00919 char *string) 00920 { 00921 char *left; 00922 char *right; 00923 00924 if (string == NIL(char)) { 00925 return NIL(char); 00926 } 00927 00928 /* Traverse the beginning of the string */ 00929 for (left = string; *left == ' ' || *left == '\t'; left++); 00930 00931 /* If we reached the end of the string */ 00932 if (*left == 0) { 00933 return left; 00934 } 00935 00936 /* Traverse the end of the string */ 00937 right = left + strlen(left) - 1; 00938 while (right > left && (*right == ' ' || *right == '\t')) { 00939 right--; 00940 } 00941 /* Set the new end of string */ 00942 *++right = '\0'; 00943 00944 return left; 00945 } /* End of removeWhiteSpaces */ 00946 #endif