VIS

src/cmd/cmdFile.c

Go to the documentation of this file.
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