tenex.c
来自「<B>Digital的Unix操作系统VAX 4.2源码</B>」· C语言 代码 · 共 1,873 行 · 第 1/3 页
C
1,873 行
Don't match . files on null prefix match in directory lookups (DIR_WD and CMD_WD currently). */ if (name_length == 0 && entry[0] == '.' && (findtype == DIR_WD || findtype == CMD_WD)) continue; /* * Skip non-executables if looking for commands: * Only done for directory "." for speed. * (Benchmarked with and without: * With filetype check, a full search took 10 seconds. * Without filetype check, a full search took 1 second.) * -Ken Greer */ if (findtype == CMD_WD && dot_scan && filetype (dir, entry) != '*') continue; if (command == LIST) /* LIST command */ { extern char *malloc(); register int length; if (numitems >= MAXITEMS) { csh_printf ("\nToo many %s. Max %d\n", /* 012 RNF */ (findtype == LNAM_WD) ? "names in password file" : "files", MAXITEMS); break; } length = strlen(entry) + 1; if (showpathn) length += strlen(dirflag); /* Allocate an arrayful of ptrs, if necessary. */ if (items == NULL) { items = (char **)calloc((unsigned)sizeof (items[1]), (unsigned)MAXITEMS + 1); if (items == NULL) /* Can't allocate: exit */ break; } if ((items[numitems] = malloc ((unsigned)length)) == NULL) { csh_printf ("out of mem\n"); /* 012 RNF */ break; } copyn (items[numitems], entry, MAXNAMLEN); if (showpathn) catn (items[numitems], dirflag, MAXNAMLEN); numitems++; } else /* RECOGNIZE command */ { int tmp = numitems; /* tmp is so I can get register addr */ if (recognize (extended_name, entry, name_length, &tmp)) { numitems = tmp; break; } numitems = tmp; } }/* end while "entries exist" */ /* Perform "finalizations" according to type. */ switch (findtype) { case LNAM_WD: /* Login names: close pw file */ endpwent (); break; case DIR_WD: case CMD_WD: /* cmd and dir lookups: close dir */ if (dirctr > 0) FREE_DIR (context.dir_fd); break; }/* end switch (findtype) */ } while (findtype == CMD_WD && *path && (path = get_dir_from_path (path, dir, &dirctr, dirflag), dir)); switch (command) { case RECOGNIZE: if (numitems <= 0) break; switch (findtype) { case LNAM_WD: /* Login name: put back tilde */ copyn (word, "~", 1); break; case CMD_WD: /* command word: put nothing back */ word[0] = NULL; break; case DIR_WD: /* dir lookup: put dir part back */ copyn (word, dir, max_word_length); break; case VAR_WD: /* variable: put '$' back */ copyn (word, "$", 1); break; case HIST_WD: /* History: put HIST back */ word[0] = HIST; word[1] = NULL; break; }/* end switch (findtype) */ /* add extended name */ catn (word, extended_name, max_word_length); /* either append, or print the result */ while (*wp) (*routine) (*wp++); break; case LIST: { int newcount = 0, maxwidth; /* sort everything */ qsort (items, numitems, sizeof (items[1]), fcompare); /* remove duplicates and get max item width */ newcount = remove_duplicates(items, numitems, &maxwidth); /* print the items */ print_by_column (tilded_dir, items, newcount, maxwidth, findtype); } /* Fall through */ default: numitems = 0; /* set the return value */ }/* end switch(command) */ if (items != NULL) /* release allocated item storage */ FREE_ITEMS (items); return (numitems); /* return number of pertinent items */}/* end item_search() *//* recognize Object: extend what user typed up to an ambiguity. Keep count of valid possibilities. Algorithm: On first match, copy full entry (assume it'll be the only match). On subsequent matches, shorten extended_name to the first character mismatch between extended_name and entry. If we shorten it back to the prefix length, and we've seen more than one potential match, stop searching. If the current entry is identical to the extended name, don't count that entry.*/recognize (extended_name, entry, name_length, numitems)char *extended_name, *entry;int *numitems;{ if (*numitems == 0) /* 1st match */ { copyn (extended_name, entry, MAXNAMLEN); (*numitems)++; } else /* 2nd and subsequent matches */ { register char *x, *ent; register int len = 0; for (x = extended_name, ent = entry; *x && *x == *ent; x++, len++, ent++) /* do nothing */; if (*x != *ent) /* identical? */ { *x = '\0'; /* No: shorten at 1st diff */ (*numitems)++; /* Count this one */ } /* Ambiguous to prefix and more than one chice. No need to look further. */ if (len == name_length && *numitems > 1) return(-1); } return (0);}/* end recognize() *//* is_prefix Return true if the check item's initial chars are in the template. This differs from PWB imatch in that if check is null it prefixes anything.*/staticis_prefix (check, template)char *check, *template;{ register char *check_char, *template_char; check_char = check; /* get copies of string ptrs */ template_char = template; do if (*check_char == 0) return (TRUE); while (*check_char++ == *template_char++); return (FALSE);}/* starting_a_command Returns TRUE if the word pointed to by wordstart is in a command position. A command position is defined as being the first position in an input line or the first position that occurs after a character in the cmdstart set. "Whitespace" is defined to be any character in the cmdalive set.*/starting_a_command (wordstart, inputline)register char *wordstart, *inputline;{ static char cmdstart[] = ";&(|`"; static char cmdalive[] = " \t'\""; /* search backward until start of line */ while (--wordstart >= inputline) { /* position maybe a command position: exit. */ if (index (cmdstart, *wordstart)) break; /* No command character. If not whitespace, then not a command position: return. */ if (!index (cmdalive, *wordstart)) return (FALSE); } if (wordstart > inputline && *wordstart == '&') /* Look for >& */ { while (wordstart > inputline && (*--wordstart == ' ' || *wordstart == '\t')) ; if (*wordstart == '>') return (FALSE); } return (TRUE);}/* end starting_a_command() *//* tenematch Do the TENEX-style matching. It does this by extracting the word to be recognized from the passed input line, determining on the way whether or not the word is in a command position, and then returning the status and side-effects of the item_search() function. Note that a "word" is ordinarily those characters delimited by one of the characters in the delims[] set, but this definition has been modified to allow the start of a history or variable substitution to be the valid start of a word, yet be included in the word.*/tenematch (inputline, inputline_size, num_read, command, command_routine)char *inputline; /* match string prefix */int inputline_size; /* max size of string */int num_read; /* # actually in inputline */COMMAND command; /* LIST or RECOGNIZE */int (*command_routine) (); /* either append char or display char */{ /* The first character of this delims array will be replaced by HIST, the history substitution character. */ static char delims[] = "!$ '\"\t;&<>()|^%"; /* delimiter set */ char cand_word [FILSIZ + 1];/* put the candidate word here */ register char /* fast pointers */ *str_end, /* end of user input */ *cand_start, /* start of candidate word */ *cp, /* copy pointer */ *cwp; /* candidate word ptr */ int space_left; int is_a_cmd; /* UNIX command rather than filename */ delims[0] = HIST; /* use proper history sub char */ str_end = &inputline[num_read]; /* Find LAST occurence of a delimiter in the inputline. The start of the candidate word is the next character. */ for (cand_start = str_end; cand_start > inputline; --cand_start) if (index (delims, cand_start[-1])) break; /* Keep delimiter if we are history or variable substitution */ if (cand_start[-1] == HIST || cand_start[-1] == '$') cand_start--; /* determine the expansion space left to us */ space_left = inputline_size - (cand_start - inputline) - 1; /* set flag to show if we are in a command position */ is_a_cmd = starting_a_command (cand_start, inputline); /* copy the candidate word from the input */ for (cp = cand_start, cwp = cand_word; cp < str_end; *cwp++ = *cp++); *cwp = 0; /* OK, go to work */ return item_search (cand_word, /* start of org word */ cwp, /* end of org word */ command, /* LIST/RECOGNIZE */ command_routine, /* append or print */ space_left, /* expansion space left */ is_a_cmd); /* command position? */}/* end tenematch() *//* CharAppend Routine to print recognized characters and append them to the input stream.*/char *CharPtr;staticCharAppend (c){ putchar (c); /* print the character */ *CharPtr++ = c; /* extend the input stream */ *CharPtr = 0;}/* tenex This is the routine that is called by the csh proper. All control starts here.*/#include <sys/time.h>tenex (inputline, inputline_size)char *inputline;int inputline_size;{ int tty_out = 0; register int numitems, num_read; struct sgttyb sgtty; setup_tty (ON);#ifdef CURSES termchars (); /* load vb, etc. from TERMCAP */#endif while((num_read = read (SHIN, inputline, inputline_size)) > 0) { register char *str_end, last_char, should_retype; COMMAND command; int tty_local = 0; /* tty "local mode" bits */ last_char = inputline[num_read - 1] & TRIM; /* line ended or buffer full: return */ if (last_char == '\n' || num_read == inputline_size) break; if (ioctl (SHIN, TIOCLGET, &tty_local) == -1) { /* 010 - GAG */ Perror ("ioctl"); } /* * Get into cmd line edit if the first char on the line is * the ESC char. I would also recognize other characters for * this (^P, ^E) if the t_brkc field in the tchars struct * allowed more than 1 break character, but it doesn't. */ if (last_char == ESC && num_read == 1) /* 07: synonym for !!:v */ { /* * The following abomination of code is necessary in case * the dumb user insists on hitting the uparrow key to get * into command line edit mode (should use ESC key). * The uparrow key transmits an ESC followed by 1 or 2 * characters. The 1 or 2 following characters can be slow * about arriving in the tty input queue and slow about * echoing across the network for an rlogin session. We want * to be sure that they get read from the input queue and give * them to echo BEFORE we have printed the NEWLINE, otherwise * they show up on the edit line! * * Thus we set the tty to CBREAK mode to move the 2 chars from * the raw to the canonical tty input queue; so we can be sure * (from select) that they have really arrived and can be read. * We call select and if a char is really available we call select * again, if another char is available, we call select a third * time with only a timeout argument to give the characters echo * time. Thus we always do one more select than the number of * chars that are trailing the escape (assumes a max of 2 trailers). * * Finally we flush (throw away) I/O in case any characters somehow * escaped the select and read on lines that don't behave properly. * * For the normal case where the user just typed the ESC key * to get into edit mode we will only have one select timeout delay. */ int arg = 0; int count; struct timeval timeout; if (ioctl(SHIN, TIOCGETP, &sgtty) == -1) { /* 010 - GAG */ Perror ("ioctl"); } sgtty.sg_flags |= CBREAK; if (ioctl(SHIN, TIOCSETP, &sgtty) == -1) { /* 010 - GAG */ Perror ("ioctl"); } count = 0; arg = 1<<SHIN; timeout.tv_sec = 0; timeout.tv_usec = 100000; /* 100,000 is 1/10 sec */ if (select(SHIN+1, &arg, 0, 0, &timeout) > 0) { /* Got 1 trailing character, see if there is another */ count++; num_read = read(SHIN, inputline, 1); arg = 1<<SHIN; if (select(SHIN+1, &arg, 0, 0, &timeout) > 0) { /* Got 2nd trailing char, give time for chars to echo */ count++; num_read = read(SHIN, inputline, 1); arg = select(0, 0, 0, 0, &timeout); /* timeout only */ } } sgtty.sg_flags &= ~CBREAK; if (ioctl(SHIN, TIOCSETP, &sgtty) == -1) { /* 010 - GAG */ Perror ("ioctl"); } /* end of abomination */ arg = 0; if (ioctl(SHIN, TIOCFLUSH, &arg) == -1) { /* 010 - GAG */ Perror ("ioctl"); } for (arg = 0; arg < count; arg++) csh_printf("%c\b",QUOTECHAR); /* backover ESC trailers */ /* 012 RNF 014 RNF */ if (tty_local & LCTLECH) csh_printf ("%c\b%c\b",QUOTECHAR,QUOTECHAR); /* backover ^[ (ESC) */ /* 012 RNF 014 RNF */ for (arg = 0; arg < count; arg++) csh_printf(" "); /* blank ESC trailers */ /* 012 RNF */ /* * We must do a newline since we don't know how long the * user's prompt was, ie. where on the line the cursor started. * We need to know the starting cursor postion, otherwise we * can't edit lines longer than the window size because * we wouldn't know when to wrap the cursor to a new line * when doing cursor positioning. */ if (tty_local & LCTLECH) csh_printf (" \n"); /* blank ^[ (ESC) */ /* 012 RNF */ else csh_printf ("\n");/* just output newline */ /* 012 RNF */ strcpy(inputline, "!!:v\n"); num_read = 5; pushback (inputline); } else { if (last_char == ESC) /* RECOGNIZE */ { if (tty_local & LCTLECH) /* 012 RNF 014 RNF */ csh_printf ("%c\b%c\b %c\b%c\b",QUOTECHAR,QUOTECHAR,QUOTECHAR,QUOTECHAR); /* Erase ^[ */ command = RECOGNIZE; num_read--; } else /* LIST */ command = LIST, putchar ('\n'); CharPtr = str_end = &inputline[num_read]; *str_end = '\0'; numitems = tenematch (inputline, inputline_size, num_read, command, command == LIST ? putchar : CharAppend); flush (); /* OK, let's see 'em */ if (command == RECOGNIZE) if (numitems != 1) /* Beep = No match/ambiguous */ beep (); /* * Tabs in the input line cause trouble after a pushback. * tty driver won't backspace over them because column positions * are now incorrect. This is solved by retyping over current line. */ should_retype = FALSE; if (index (inputline, '\t') /* tab in input line? */ || (tty_local & LCTLECH) == 0) /* Control chars don't echo? */ { back_to_col_1 (); should_retype = TRUE; } if (command == LIST) /* Always retype after LIST */ should_retype = TRUE; if (should_retype) printprompt (); pushback (inputline); if (should_retype) retype (); } }/* end while (num_read > 0) */ setup_tty (OFF); return (num_read);}/* end tenex() */#ifdef TEST#include <stdio.h>short SHIN = 0, SHOUT = 1;flush(){ fflush(stdout);}printprompt (){ (void) write (SHOUT, "-> ", 3); return (1);}main (argc, argv)char **argv;{ char string[128]; int n; while (printprompt () && (n = tenex (string, 127)) > 0) { string[n] = '\0'; csh_printf ("Tenex returns \"%s\"\n", string); /* 012 RNF */ }}#endif TEST#endif TENEX
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?