/* * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* * @OSF_COPYRIGHT@ */ /* * HISTORY * * Revision 1.1.1.1 1998/09/22 21:05:48 wsanchez * Import of Mac OS X kernel (~semeria) * * Revision 1.1.1.1 1998/03/07 02:26:09 wsanchez * Import of OSF Mach kernel (~mburg) * * Revision 1.3.10.2 1994/09/23 01:19:37 ezf * change marker to not FREE * [1994/09/22 21:10:05 ezf] * * Revision 1.3.10.1 1994/06/11 21:11:48 bolinger * Merge up to NMK17.2. * [1994/06/11 20:01:41 bolinger] * * Revision 1.3.8.2 1994/02/11 14:21:41 paire * Added string.h header file for strlen declaration. * [94/02/09 paire] * * Revision 1.3.8.1 1994/02/08 10:57:55 bernadat * Added db_auto_completion variable. * [93/08/17 paire] * * Added support of symbol completion by typing '\t'. * [93/08/14 paire] * [94/02/07 bernadat] * * Revision 1.3.2.4 1993/08/11 20:37:51 elliston * Add ANSI Prototypes. CR #9523. * [1993/08/11 03:33:21 elliston] * * Revision 1.3.2.3 1993/07/27 18:27:30 elliston * Add ANSI prototypes. CR #9523. * [1993/07/27 18:12:01 elliston] * * Revision 1.3.2.2 1993/06/09 02:20:13 gm * CR9176 - ANSI C violations: trailing tokens on CPP * directives, extra semicolons after decl_ ..., asm keywords * [1993/06/07 18:57:14 jeffc] * * Added to OSF/1 R1.3 from NMK15.0. * [1993/06/02 20:56:26 jeffc] * * Revision 1.3 1993/04/19 16:02:17 devrcs * Replaced ^R (redraw) with ^L [barbou@gr.osf.org] * * Added ^R and ^S commands for history search commands * ^U does not erase end of the line anymore. (only erases * from the beginning of the line to current position). * [barbou@gr.osf.org] * * ^C now erases the entire line. [barbou@gr.osf.org] * [92/12/03 bernadat] * * Fixed history management: Do not store repeated typed * command. Null terminate current command in case it is a * substring of the last command. * [92/10/02 bernadat] * * Revision 1.2 1992/11/25 01:04:24 robert * integrate changes for norma_14 below * * Philippe Bernadat (bernadat) at gr.osf.org 02-Oct-92 * Fixed history management: Do not store repeated typed * command. Null terminate current command in case it is a * substring of the last command. * [1992/11/20 00:56:07 robert] * * integrate changes below for norma_14 * [1992/11/13 19:21:34 robert] * * Revision 1.1 1992/09/30 02:01:08 robert * Initial revision * * $EndLog$ */ /* CMU_HIST */ /* * Revision 2.7.3.2 92/09/15 17:14:26 jeffreyh * Fixed history code. (Only one char. out of 2 was checked to * compare to last command) * [barbou@gr.osf.org] * * Revision 2.7.3.1 92/03/03 16:13:30 jeffreyh * Pick up changes from TRUNK * [92/02/26 10:59:36 jeffreyh] * * Revision 2.8 92/02/19 15:07:44 elf * Added delete_line (Ctrl-U). * [92/02/17 kivinen] * * Added command line history. Ctrl-P = previous, Ctrl-N = next. If * DB_HISTORY_SIZE is 0 then command history is disabled. * [92/02/17 kivinen] * * Revision 2.7 91/10/09 16:00:03 af * Revision 2.6.2.1 91/10/05 13:06:12 jeffreyh * Fixed incorrect db_lbuf_end setting. * [91/08/29 tak] * * Revision 2.6.2.1 91/10/05 13:06:12 jeffreyh * Fixed incorrect db_lbuf_end setting. * [91/08/29 tak] * * Revision 2.6 91/07/09 23:15:49 danner * Add include of machine/db_machdep.h to allow machine-specific * overrides via defines. * [91/07/08 danner] * * Revision 2.5 91/05/14 15:34:03 mrt * Correcting copyright * * Revision 2.4 91/02/14 14:41:53 mrt * Add input line editing. * [90/11/11 dbg] * * Revision 2.3 91/02/05 17:06:32 mrt * Changed to new Mach copyright * [91/01/31 16:18:13 mrt] * * Revision 2.2 90/08/27 21:51:03 dbg * Reduce lint. * [90/08/07 dbg] * Created. * [90/07/25 dbg] * */ /* CMU_ENDHIST */ /* * Mach Operating System * Copyright (c) 1991,1990 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. */ /* */ /* * Author: David B. Golub, Carnegie Mellon University * Date: 7/90 */ #include #include #include #include #include #include #include #include #include #ifndef DB_HISTORY_SIZE #define DB_HISTORY_SIZE 4000 #endif /* DB_HISTORY_SIZE */ /* * Character input and editing. */ /* * We don't track output position while editing input, * since input always ends with a new-line. We just * reset the line position at the end. */ char * db_lbuf_start; /* start of input line buffer */ char * db_lbuf_end; /* end of input line buffer */ char * db_lc; /* current character */ char * db_le; /* one past last character */ int db_completion; /* number of incomplete symbols matched */ int db_auto_completion = 10; /* number of line to display without asking */ #if DB_HISTORY_SIZE != 0 char db_history[DB_HISTORY_SIZE]; /* start of history buffer */ int db_history_size = DB_HISTORY_SIZE;/* size of history buffer */ char * db_history_curr = db_history; /* start of current line */ char * db_history_last = db_history; /* start of last line */ char * db_history_prev = (char *) 0; /* start of previous line */ int db_hist_unmodified = 0; /* unmodified line from history */ int db_hist_search = 0; /* are we in hist search mode ? */ char db_hist_search_string[DB_LEX_LINE_SIZE];/* the string to look for */ int db_hist_ignore_dups = 0; /* don't duplicate commands in hist */ #endif #define CTRL(c) ((c) & 0x1f) #define isspace(c) ((c) == ' ' || (c) == '\t') #define BLANK ' ' #define BACKUP '\b' /* Prototypes for functions local to this file. XXX -- should be static! */ void db_putstring(const char *s, int count); void db_putnchars( int c, int count); void db_delete( int n, int bwd); void db_delete_line(void); boolean_t db_hist_substring( char *string, char *substring); boolean_t db_inputchar(int c); extern jmp_buf_t *db_recover; void db_putstring(const char *s, int count) { while (--count >= 0) cnputc(*s++); } void db_putnchars( int c, int count) { while (--count >= 0) cnputc(c); } /* * Delete N characters, forward or backward */ #define DEL_FWD 0 #define DEL_BWD 1 void db_delete( int n, int bwd) { register char *p; if (bwd) { db_lc -= n; db_putnchars(BACKUP, n); } for (p = db_lc; p < db_le-n; p++) { *p = *(p+n); cnputc(*p); } db_putnchars(BLANK, n); db_putnchars(BACKUP, db_le - db_lc); db_le -= n; } void db_delete_line(void) { db_delete(db_le - db_lc, DEL_FWD); db_delete(db_lc - db_lbuf_start, DEL_BWD); db_le = db_lc = db_lbuf_start; } #if DB_HISTORY_SIZE != 0 #define INC_DB_CURR() \ do { \ db_history_curr++; \ if (db_history_curr > \ db_history + db_history_size - 1) \ db_history_curr = db_history; \ } while (0) #define DEC_DB_CURR() \ do { \ db_history_curr--; \ if (db_history_curr < db_history) \ db_history_curr = db_history + \ db_history_size - 1; \ } while (0) #endif /* returs TRUE if "substring" is a substring of "string" */ boolean_t db_hist_substring( char *string, char *substring) { register char *cp1, *cp2; cp1 = string; while (*cp1) cp1++; cp2 = substring; while (*cp2) cp2++; while (cp2 > substring) { cp1--; cp2--; } while (cp1 >= string) { register char *cp3; cp2 = substring; cp3 = cp1; while (*cp2 && *cp2 == *cp3) { cp2++; cp3++; } if (*cp2 == '\0') { return TRUE; } cp1--; } return FALSE; } /* returns TRUE at end-of-line */ boolean_t db_inputchar(int c) { char *sym; char *start; char *restart; jmp_buf_t db_jmpbuf; jmp_buf_t *local_prev; char *p; int len; switch(db_completion) { case -1: db_putchar('\n'); local_prev = db_recover; if (_setjmp(db_recover = &db_jmpbuf) == 0 && (c == 'y' || c == ' ' || c == '\t')) db_print_completion(db_tok_string); db_recover = local_prev; db_completion = 0; db_reset_more(); db_output_prompt(); if (db_le > db_lbuf_start) { for (start = db_lbuf_start; start < db_le; start++) db_putchar(*start); db_putnchars(BACKUP, db_le - db_lc); } return(FALSE); case 0: break; default: if (c == '\t') { db_printf("\nThere are %d possibilities. ", db_completion); db_printf("Do you really wish to see them all [n] ? "); db_force_whitespace(); db_completion = -1; db_reset_more(); return(FALSE); } db_completion = 0; break; } switch (c) { case '\t': /* symbol completion */ if (db_lc == db_lbuf_start || db_auto_completion == 0) break; if (db_le == db_lbuf_end) { cnputc('\007'); break; } start = db_lc - 1; while (start >= db_lbuf_start && ((*start >= 'A' && *start <= 'Z') || (*start >= 'a' && *start <= 'z') || (*start >= '0' && *start <= '9') || *start == '_' || *start == ':')) start--; if (start == db_lc - 1) break; if (start > db_lbuf_start && *start == '$') { cnputc('\007'); break; } sym = db_tok_string; restart = ++start; do { *sym++ = *start++; } while (start != db_lc && sym != db_tok_string + sizeof(db_tok_string)); if (sym == db_tok_string + sizeof(db_tok_string)) { cnputc('\007'); break; } *sym = '\0'; db_completion = db_lookup_incomplete(db_tok_string, sizeof(db_tok_string)); if (db_completion == 0) { /* symbol unknown */ cnputc('\007'); break; } len = strlen(db_tok_string) - (start - restart); if (db_completion == 1 && (db_le == db_lc || ((db_le > db_lc) && *db_lc != ' '))) len++; for (p = db_le - 1; p >= db_lc; p--) *(p + len) = *p; db_le += len; for (sym = &db_tok_string[start - restart]; *sym != '\0'; sym++) *db_lc++ = *sym; if (db_completion == 1 || db_completion > db_auto_completion) { for (sym = &db_tok_string[start - restart]; *sym != '\0'; sym++) cnputc(*sym); if (db_completion == 1) { if (db_le == db_lc || ((db_le > db_lc) && *db_lc != ' ')) { cnputc(' '); *db_lc++ = ' '; } db_completion = 0; } db_putstring(db_lc, db_le - db_lc); db_putnchars(BACKUP, db_le - db_lc); } if (db_completion > 1) { cnputc('\007'); if (db_completion <= db_auto_completion) { db_putchar('\n'); db_print_completion(db_tok_string); db_completion = 0; db_reset_more(); db_output_prompt(); if (db_le > db_lbuf_start) { for (start = db_lbuf_start; start < db_le; start++) db_putchar(*start); db_putnchars(BACKUP, db_le - db_lc); } } } break; case CTRL('b'): /* back up one character */ if (db_lc > db_lbuf_start) { cnputc(BACKUP); db_lc--; } break; case CTRL('f'): /* forward one character */ if (db_lc < db_le) { cnputc(*db_lc); db_lc++; } break; case CTRL('a'): /* beginning of line */ while (db_lc > db_lbuf_start) { cnputc(BACKUP); db_lc--; } break; case CTRL('e'): /* end of line */ while (db_lc < db_le) { cnputc(*db_lc); db_lc++; } break; case CTRL('h'): case 0177: /* erase previous character */ if (db_lc > db_lbuf_start) db_delete(1, DEL_BWD); break; case CTRL('d'): /* erase next character */ if (db_lc < db_le) db_delete(1, DEL_FWD); break; case CTRL('k'): /* delete to end of line */ if (db_lc < db_le) db_delete(db_le - db_lc, DEL_FWD); break; case CTRL('u'): /* delete to beginning of line */ if (db_lc > db_lbuf_start) db_delete(db_lc - db_lbuf_start, DEL_BWD); break; case CTRL('t'): /* twiddle last 2 characters */ if (db_lc >= db_lbuf_start + 2) { c = db_lc[-2]; db_lc[-2] = db_lc[-1]; db_lc[-1] = c; cnputc(BACKUP); cnputc(BACKUP); cnputc(db_lc[-2]); cnputc(db_lc[-1]); } break; case CTRL('c'): case CTRL('g'): db_delete_line(); #if DB_HISTORY_SIZE != 0 db_history_curr = db_history_last; if (c == CTRL('g') && db_hist_search) { for (p = db_hist_search_string, db_le = db_lbuf_start; *p; ) { *db_le++ = *p++; } db_lc = db_le; *db_le = '\0'; db_putstring(db_lbuf_start, db_le - db_lbuf_start); } #endif break; #if DB_HISTORY_SIZE != 0 case CTRL('r'): if (db_hist_search++ == 0) { /* starting an history lookup */ register char *cp1, *cp2; for (cp1 = db_lbuf_start, cp2 = db_hist_search_string; cp1 < db_le; cp1++, cp2++) *cp2 = *cp1; *cp2 = '\0'; db_hist_search++; } /* FALL THROUGH */ case CTRL('p'): { char * old_history_curr = db_history_curr; if (db_hist_unmodified++ == 0) db_hist_unmodified++; DEC_DB_CURR(); while (db_history_curr != db_history_last) { DEC_DB_CURR(); if (*db_history_curr == '\0') { INC_DB_CURR(); if (db_hist_search <= 1) { if (*db_history_curr == '\0') cnputc('\007'); else DEC_DB_CURR(); break; } if (*db_history_curr == '\0') { cnputc('\007'); db_history_curr = old_history_curr; DEC_DB_CURR(); break; } if (db_history_curr != db_history_last && db_hist_substring(db_history_curr, db_hist_search_string)) { DEC_DB_CURR(); break; } DEC_DB_CURR(); } } if (db_history_curr == db_history_last) { cnputc('\007'); db_history_curr = old_history_curr; } else { INC_DB_CURR(); db_delete_line(); for (p = db_history_curr, db_le = db_lbuf_start; *p; ) { *db_le++ = *p++; if (p == db_history + db_history_size) { p = db_history; } } db_lc = db_le; *db_le = '\0'; db_putstring(db_lbuf_start, db_le - db_lbuf_start); } break; } case CTRL('s'): if (db_hist_search++ == 0) { /* starting an history lookup */ register char *cp1, *cp2; for (cp1 = db_lbuf_start, cp2 = db_hist_search_string; cp1 < db_le; cp1++, cp2++) *cp2 = *cp1; *cp2 = '\0'; db_hist_search++; } /* FALL THROUGH */ case CTRL('n'): { char *old_history_curr = db_history_curr; if (db_hist_unmodified++ == 0) db_hist_unmodified++; while (db_history_curr != db_history_last) { if (*db_history_curr == '\0') { if (db_hist_search <= 1) break; INC_DB_CURR(); if (db_history_curr != db_history_last && db_hist_substring(db_history_curr, db_hist_search_string)) { DEC_DB_CURR(); break; } DEC_DB_CURR(); } INC_DB_CURR(); } if (db_history_curr != db_history_last) { INC_DB_CURR(); if (db_history_curr != db_history_last) { db_delete_line(); for (p = db_history_curr, db_le = db_lbuf_start; *p;) { *db_le++ = *p++; if (p == db_history + db_history_size) { p = db_history; } } db_lc = db_le; *db_le = '\0'; db_putstring(db_lbuf_start, db_le - db_lbuf_start); } else { cnputc('\007'); db_history_curr = old_history_curr; } } else { cnputc('\007'); db_history_curr = old_history_curr; } break; } #endif /* refresh the command line */ case CTRL('l'): db_putstring("^L\n", 3); if (db_le > db_lbuf_start) { db_putstring(db_lbuf_start, db_le - db_lbuf_start); db_putnchars(BACKUP, db_le - db_lc); } break; case '\n': case '\r': #if DB_HISTORY_SIZE != 0 /* Check if it same than previous line */ if (db_history_prev) { char *pc; /* Is it unmodified */ for (p = db_history_prev, pc = db_lbuf_start; pc != db_le && *p;) { if (*p != *pc) break; if (++p == db_history + db_history_size) { p = db_history; } if (++pc == db_history + db_history_size) { pc = db_history; } } if (!*p && pc == db_le) { /* Repeted previous line, not saved */ db_history_curr = db_history_last; *db_le++ = c; db_hist_search = 0; db_hist_unmodified = 0; return (TRUE); } } if (db_le != db_lbuf_start && (db_hist_unmodified == 0 || !db_hist_ignore_dups)) { db_history_prev = db_history_last; for (p = db_lbuf_start; p != db_le; p++) { *db_history_last++ = *p; if (db_history_last == db_history + db_history_size) { db_history_last = db_history; } } *db_history_last++ = '\0'; } db_history_curr = db_history_last; #endif *db_le++ = c; db_hist_search = 0; db_hist_unmodified = 0; return (TRUE); default: if (db_le == db_lbuf_end) { cnputc('\007'); } else if (c >= ' ' && c <= '~') { for (p = db_le; p > db_lc; p--) *p = *(p-1); *db_lc++ = c; db_le++; cnputc(c); db_putstring(db_lc, db_le - db_lc); db_putnchars(BACKUP, db_le - db_lc); } break; } if (db_hist_search) db_hist_search--; if (db_hist_unmodified) db_hist_unmodified--; return (FALSE); } int db_readline( char * lstart, int lsize) { db_force_whitespace(); /* synch output position */ db_lbuf_start = lstart; db_lbuf_end = lstart + lsize - 1; db_lc = lstart; db_le = lstart; while (!db_inputchar(cngetc())) continue; db_putchar('\n'); /* synch output position */ *db_le = 0; return (db_le - db_lbuf_start); } void db_check_interrupt(void) { register int c; c = cnmaygetc(); switch (c) { case -1: /* no character */ return; case CTRL('c'): db_error((char *)0); /*NOTREACHED*/ case CTRL('s'): do { c = cnmaygetc(); if (c == CTRL('c')) db_error((char *)0); } while (c != CTRL('q')); break; default: /* drop on floor */ break; } }