This is term.c in view mode; [Download] [Up]
/* vi:ts=4:sw=4 * * VIM - Vi IMproved by Bram Moolenaar * * Read the file "credits.txt" for a list of people who contributed. * Read the file "uganda.txt" for copying and usage conditions. */ /* * * term.c: functions for controlling the terminal * * primitive termcap support for Amiga and MSDOS included * * NOTE: padding and variable substitution is not performed, * when compiling without TERMCAP, we use tputs() and tgoto() dummies. */ #include "vim.h" #include "globals.h" #include "param.h" #include "proto.h" #ifdef TERMCAP # ifdef linux # include <termcap.h> # if 0 /* only required for old versions, it's now in termcap.h */ typedef int (*outfuntype) (int); # endif # define TPUTSFUNCAST (outfuntype) # else # define TPUTSFUNCAST # ifdef AMIGA # include "proto/termlib.pro" # endif # endif #endif static void parse_builtin_tcap __ARGS((Tcarr *tc, char_u *s)); /* * Builtin_tcaps must always contain DFLT_TCAP as the first entry! * DFLT_TCAP is used, when no terminal is specified with -T option or $TERM. * The entries are compact, therefore they normally are included even when * TERMCAP is defined. * When TERMCAP is defined, the builtin entries can be accessed with * "builtin_amiga", "builtin_ansi", "builtin_debug", etc. */ static char_u *builtin_tcaps[] = { #ifndef NO_BUILTIN_TCAPS (char_u *)DFLT_TCAP, /* almost allways included */ # if !defined(UNIX) && (defined(ALL_BUILTIN_TCAPS) || defined(SOME_BUILTIN_TCAPS)) (char_u *)ANSI_TCAP, /* default for unix */ # endif # if !defined(AMIGA) && (defined(ALL_BUILTIN_TCAPS) || defined(SOME_BUILTIN_TCAPS)) (char_u *)AMIGA_TCAP, /* default for amiga */ # endif # if !defined(MSDOS) && (defined(ALL_BUILTIN_TCAPS) || defined(SOME_BUILTIN_TCAPS)) (char_u *)PCTERM_TCAP, /* default for MSdos */ # endif # if defined(MSDOS) || defined(ALL_BUILTIN_TCAPS) (char_u *)PCANSI_TCAP, # endif # if !defined(ATARI) && defined(ALL_BUILTIN_TCAPS) (char_u *)ATARI_TCAP, /* default for Atari */ # endif # if defined(UNIX) || defined(ALL_BUILTIN_TCAPS) || defined(SOME_BUILTIN_TCAPS) (char_u *)XTERM_TCAP, /* always included on unix */ # endif # ifdef ALL_BUILTIN_TCAPS (char_u *)VT52_TCAP, # endif # if defined(DEBUG) || defined(ALL_BUILTIN_TCAPS) (char_u *)DEBUG_TCAP, /* always included when debugging */ # endif #else /* NO_BUILTIN_TCAPS */ (char_u *)DUMB_TCAP, /* minimal termcap, used when everything else fails */ #endif /* NO_BUILTIN_TCAPS */ NULL, }; /* * Term_strings contains currently used terminal strings. * It is initialized with the default values by parse_builtin_tcap(). * The values can be changed by setting the parameter with the same name. */ Tcarr term_strings; /* * Parsing of the builtin termcap entries. * The terminal's name is not set, as this is already done in termcapinit(). * Chop builtin termcaps, string entries are already '\0' terminated. * not yet implemented: * boolean entries could be empty strings; * numeric entries would need a flag (e.g. high bit of the skip byte), * so that parse_builtin_tcap can handle them. */ static void parse_builtin_tcap(tc, s) Tcarr *tc; char_u *s; { char_u **p = &tc->t_name; p++; for (;;) { while (*s++) ; p += *s++; if (!*s) return; *p++ = s; } } #ifdef TERMCAP # ifndef linux /* included in <termlib.h> */ # ifndef AMIGA /* included in proto/termlib.pro */ int tgetent(); int tgetnum(); char *tgetstr(); int tgetflag(); int tputs(); # endif /* AMIGA */ # ifndef hpux extern short ospeed; # endif # endif /* linux */ # ifndef hpux char *UP, *BC, PC; /* should be extern, but some don't have them */ # endif #endif /* TERMCAP */ #ifdef linux # define TGETSTR(s, p) (char_u *)tgetstr((s), (char **)(p)) #else # define TGETSTR(s, p) (char_u *)tgetstr((s), (char *)(p)) #endif void set_term(term) char_u *term; { char_u **p = builtin_tcaps; #ifdef TERMCAP int builtin = 0; #endif int width = 0, height = 0; if (!STRNCMP(term, "builtin_", (size_t)8)) { term += 8; #ifdef TERMCAP builtin = 1; #endif } #ifdef TERMCAP else { char_u *p; static char_u tstrbuf[TBUFSZ]; char_u tbuf[TBUFSZ]; char_u *tp = tstrbuf; int i; i = tgetent(tbuf, term); if (i == -1) { EMSG("Cannot open termcap file"); builtin = 1; } else if (i == 0) { EMSG("terminal entry not found"); builtin = 1; } else { clear_termparam(); /* clear old parameters */ /* output strings */ T_EL = TGETSTR("ce", &tp); T_IL = TGETSTR("al", &tp); T_CIL = TGETSTR("AL", &tp); T_DL = TGETSTR("dl", &tp); T_CDL = TGETSTR("DL", &tp); T_CS = TGETSTR("cs", &tp); T_ED = TGETSTR("cl", &tp); T_CI = TGETSTR("vi", &tp); T_CV = TGETSTR("ve", &tp); T_CVV = TGETSTR("vs", &tp); T_TP = TGETSTR("me", &tp); T_TI = TGETSTR("mr", &tp); T_TB = TGETSTR("md", &tp); T_SE = TGETSTR("se", &tp); T_SO = TGETSTR("so", &tp); T_CM = TGETSTR("cm", &tp); T_SR = TGETSTR("sr", &tp); T_CRI = TGETSTR("RI", &tp); T_VB = TGETSTR("vb", &tp); T_KS = TGETSTR("ks", &tp); T_KE = TGETSTR("ke", &tp); T_TS = TGETSTR("ti", &tp); T_TE = TGETSTR("te", &tp); /* key codes */ term_strings.t_ku = TGETSTR("ku", &tp); term_strings.t_kd = TGETSTR("kd", &tp); term_strings.t_kl = TGETSTR("kl", &tp); /* if cursor-left == backspace, ignore it (televideo 925) */ if (term_strings.t_kl != NULL && *term_strings.t_kl == Ctrl('H')) term_strings.t_kl = NULL; term_strings.t_kr = TGETSTR("kr", &tp); /* term_strings.t_sku = TGETSTR("", &tp); termcap code unknown */ /* term_strings.t_skd = TGETSTR("", &tp); termcap code unknown */ #ifdef ARCHIE /* Termcap code made up! */ term_strings.t_sku = tgetstr("su", &tp); term_strings.t_skd = tgetstr("sd", &tp); #else term_strings.t_sku = NULL; term_strings.t_skd = NULL; #endif term_strings.t_skl = TGETSTR("#4", &tp); term_strings.t_skr = TGETSTR("%i", &tp); term_strings.t_f1 = TGETSTR("k1", &tp); term_strings.t_f2 = TGETSTR("k2", &tp); term_strings.t_f3 = TGETSTR("k3", &tp); term_strings.t_f4 = TGETSTR("k4", &tp); term_strings.t_f5 = TGETSTR("k5", &tp); term_strings.t_f6 = TGETSTR("k6", &tp); term_strings.t_f7 = TGETSTR("k7", &tp); term_strings.t_f8 = TGETSTR("k8", &tp); term_strings.t_f9 = TGETSTR("k9", &tp); term_strings.t_f10 = TGETSTR("k;", &tp); term_strings.t_sf1 = TGETSTR("F1", &tp); /* really function keys 11-20 */ term_strings.t_sf2 = TGETSTR("F2", &tp); term_strings.t_sf3 = TGETSTR("F3", &tp); term_strings.t_sf4 = TGETSTR("F4", &tp); term_strings.t_sf5 = TGETSTR("F5", &tp); term_strings.t_sf6 = TGETSTR("F6", &tp); term_strings.t_sf7 = TGETSTR("F7", &tp); term_strings.t_sf8 = TGETSTR("F8", &tp); term_strings.t_sf9 = TGETSTR("F9", &tp); term_strings.t_sf10 = TGETSTR("FA", &tp); term_strings.t_help = TGETSTR("%1", &tp); term_strings.t_undo = TGETSTR("&8", &tp); height = tgetnum("li"); width = tgetnum("co"); T_MS = tgetflag("ms") ? (char_u *)"yes" : (char_u *)NULL; # ifndef hpux BC = (char *)TGETSTR("bc", &tp); UP = (char *)TGETSTR("up", &tp); p = TGETSTR("pc", &tp); if (p) PC = *p; ospeed = 0; # endif } } if (builtin) #endif { while (*p && STRCMP(term, *p)) p++; if (!*p) { fprintf(stderr, "'%s' not builtin. Available terminals are:\r\n", term); for (p = builtin_tcaps; *p; p++) #ifdef TERMCAP fprintf(stderr, "\tbuiltin_%s\r\n", *p); #else fprintf(stderr, "\t%s\r\n", *p); #endif if (!starting) /* when user typed :set term=xxx, quit here */ { wait_return(TRUE); return; } sleep(2); fprintf(stderr, "defaulting to '%s'\r\n", *builtin_tcaps); sleep(2); p = builtin_tcaps; free(term_strings.t_name); term_strings.t_name = strsave(term = *p); } clear_termparam(); /* clear old parameters */ parse_builtin_tcap(&term_strings, *p); } /* * special: There is no info in the termcap about whether the cursor positioning * is relative to the start of the screen or to the start of the scrolling region. * We just guess here. Only msdos pcterm is known to do it relative. */ if (STRCMP(term, "pcterm") == 0) T_CSC = (char_u *)"yes"; else T_CSC = NULL; #if defined(AMIGA) || defined(MSDOS) /* DFLT_TCAP indicates that it is the machine console. */ if (STRCMP(term, *builtin_tcaps)) term_console = FALSE; else { term_console = TRUE; # ifdef AMIGA win_resize_on(); /* enable window resizing reports */ # endif } #endif ttest(TRUE); /* display initial screen after ttest() checking. jw. */ if (width <= 0 || height <= 0) { /* termcap failed to report size */ /* set defaults, in case mch_get_winsize also fails */ width = 80; #ifdef MSDOS height = 25; /* console is often 25 lines */ #else height = 24; /* most terminals are 24 lines */ #endif } set_winsize(width, height, FALSE); /* may change Rows */ } #if defined(TERMCAP) && defined(UNIX) /* * Get Columns and Rows from the termcap. Used after a window signal if the * ioctl() fails. It doesn't make sense to call tgetent each time if the "co" * and "li" entries never change. But this may happen on some systems. */ void getlinecol() { char_u tbuf[TBUFSZ]; if (term_strings.t_name != NULL && tgetent(tbuf, term_strings.t_name) > 0) { if (Columns == 0) Columns = tgetnum("co"); if (Rows == 0) Rows = tgetnum("li"); } } #endif static char_u *tltoa __PARMS((unsigned long)); static char_u * tltoa(i) unsigned long i; { static char_u buf[16]; char_u *p; p = buf + 15; *p = '\0'; do { --p; *p = i % 10 + '0'; i /= 10; } while (i > 0 && p > buf); return p; } #ifndef TERMCAP /* * minimal tgoto() implementation. * no padding and we only parse for %i %d and %+char */ char * tgoto(cm, x, y) char *cm; int x, y; { static char buf[30]; char *p, *s, *e; if (!cm) return "OOPS"; e = buf + 29; for (s = buf; s < e && *cm; cm++) { if (*cm != '%') { *s++ = *cm; continue; } switch (*++cm) { case 'd': p = (char *)tltoa((unsigned long)y); y = x; while (*p) *s++ = *p++; break; case 'i': x++; y++; break; case '+': *s++ = (char)(*++cm + y); y = x; break; case '%': *s++ = *cm; break; default: return "OOPS"; } } *s = '\0'; return buf; } #endif /* TERMCAP */ /* * Termcapinit is called from main() to initialize the terminal. * The optional argument is given with the -T command line option. */ void termcapinit(term) char_u *term; { if (!term) term = vimgetenv((char_u *)"TERM"); if (!term || !*term) term = *builtin_tcaps; term_strings.t_name = strsave(term); set_term(term); } /* * the number of calls to mch_write is reduced by using the buffer "outbuf" */ #undef BSIZE /* hpux has BSIZE in sys/param.h */ #define BSIZE 2048 static char_u outbuf[BSIZE]; static int bpos = 0; /* number of chars in outbuf */ /* * flushbuf(): flush the output buffer */ void flushbuf() { if (bpos != 0) { mch_write(outbuf, bpos); bpos = 0; } } /* * outchar(c): put a character into the output buffer. * Flush it if it becomes full. */ void outchar(c) unsigned c; { #ifdef UNIX if (c == '\n') /* turn LF into CR-LF (CRMOD does not seem to do this) */ outchar('\r'); #endif outbuf[bpos] = c; if (p_nb) /* for testing: unbuffered screen output (not for MSDOS) */ mch_write(outbuf, 1); else ++bpos; if (bpos >= BSIZE) flushbuf(); } /* * a never-padding outstr. * use this whenever you don't want to run the string through tputs. * tputs above is harmless, but tputs from the termcap library * is likely to strip off leading digits, that it mistakes for padding * information. (jw) */ void outstrn(s) char_u *s; { if (bpos > BSIZE - 20) /* avoid terminal strings being split up */ flushbuf(); while (*s) outchar(*s++); } /* * outstr(s): put a string character at a time into the output buffer. * If TERMCAP is defined use the termcap parser. (jw) */ void outstr(s) register char_u *s; { if (bpos > BSIZE - 20) /* avoid terminal strings being split up */ flushbuf(); if (s) #ifdef TERMCAP tputs(s, 1, TPUTSFUNCAST outchar); #else while (*s) outchar(*s++); #endif } /* * cursor positioning using termcap parser. (jw) */ void windgoto(row, col) int row; int col; { OUTSTR(tgoto((char *)T_CM, col, row)); } /* * Set cursor to current position. * Should be optimized for minimal terminal output. */ void setcursor() { if (!RedrawingDisabled) windgoto(curwin->w_winpos + curwin->w_row, curwin->w_col); } void ttest(pairs) int pairs; { char buf[70]; char *s = "terminal capability %s required.\n"; char *t = NULL; /* hard requirements */ if (!T_ED || !*T_ED) /* erase display */ t = "cl"; if (!T_CM || !*T_CM) /* cursor motion */ t = "cm"; if (t) { sprintf(buf, s, t); EMSG(buf); } /* * if "cs" defined, use a scroll region, it's faster. */ if (T_CS && *T_CS != NUL) scroll_region = TRUE; else scroll_region = FALSE; if (pairs) { /* optional pairs */ /* TP goes to normal mode for TI (invert) and TB (bold) */ if ((!T_TP || !*T_TP)) T_TP = T_TI = T_TB = NULL; if ((!T_SO || !*T_SO) ^ (!T_SE || !*T_SE)) T_SO = T_SE = NULL; /* T_CV is needed even though T_CI is not defined */ if ((!T_CV || !*T_CV)) T_CI = NULL; /* if 'mr' or 'me' is not defined use 'so' and 'se' */ if (T_TP == NULL || *T_TP == NUL) { T_TP = T_SE; T_TI = T_SO; T_TB = T_SO; } /* if 'so' or 'se' is not defined use 'mr' and 'me' */ if (T_SO == NULL || *T_SO == NUL) { T_SE = T_TP; if (T_TI == NULL) T_SO = T_TB; else T_SO = T_TI; } } } /* * inchar() - get one character from * 1. a scriptfile * 2. the keyboard * * As much characters as we can get (upto 'maxlen') are put in buf and * NUL terminated (buffer length must be 'maxlen' + 1). * * If we got an interrupt all input is read until none is available. * * If time == 0 there is no waiting for the char. * If time == n we wait for n msec for a character to arrive. * If time == -1 we wait forever for a character to arrive. * * Return the number of obtained characters. */ int inchar(buf, maxlen, time) char_u *buf; int maxlen; int time; /* milli seconds */ { int len; int retesc = FALSE; /* return ESC with gotint */ register int c; register int i; if (time == -1 || time > 100) /* flush output before waiting */ { cursor_on(); flushbuf(); } did_outofmem_msg = FALSE; /* display out of memory message (again) */ /* * first try script file * If interrupted: Stop reading script files. */ retry: if (scriptin[curscript] != NULL) { if (got_int || (c = getc(scriptin[curscript])) < 0) /* reached EOF */ { /* when reading script file is interrupted, return an ESC to get back to normal mode */ if (got_int) retesc = TRUE; fclose(scriptin[curscript]); scriptin[curscript] = NULL; if (curscript > 0) --curscript; goto retry; /* may read other script if this one was nested */ } if (c == 0) c = K_ZERO; /* replace ^@ with special code */ *buf++ = c; *buf = NUL; return 1; } /* * If we got an interrupt, skip all previously typed characters and * return TRUE if quit reading script file. */ if (got_int) /* skip typed characters */ { while (GetChars(buf, maxlen, T_PEEK)) ; return retesc; } len = GetChars(buf, maxlen, time); for (i = len; --i >= 0; ++buf) if (*buf == 0) *(char_u *)buf = K_ZERO; /* replace ^@ with special code */ *buf = NUL; /* add trailing NUL */ return len; } /* * Check if buf[] begins with a terminal key code. * Return 0 for no match, -1 for partial match, > 0 for full match. * With a match the replacement code is put in buf[0], the match is * removed and the number characters in buf is returned. * * Note: should always be called with buf == typestr! */ int check_termcode(buf) char_u *buf; { char_u **p; int slen; int len; len = STRLEN(buf); for (p = (char_u **)&term_strings.t_ku; p != (char_u **)&term_strings.t_undo + 1; ++p) { if (*p == NULL || (slen = STRLEN(*p)) == 0) /* empty entry */ continue; if (STRNCMP(*p, buf, (size_t)(slen > len ? len : slen)) == 0) { if (len >= slen) /* got the complete sequence */ { len -= slen; /* remove matched chars, taking care of noremap */ del_typestr(slen - 1); /* this relies on the Key numbers to be consecutive! */ buf[0] = K_UARROW + (p - (char_u **)&term_strings.t_ku); return (len + 1); } return -1; /* got a partial sequence */ } } return 0; /* no match found */ } /* * outnum - output a (big) number fast */ void outnum(n) register long n; { OUTSTRN(tltoa((unsigned long)n)); } void check_winsize() { if (Columns < MIN_COLUMNS) Columns = MIN_COLUMNS; else if (Columns > MAX_COLUMNS) Columns = MAX_COLUMNS; if (Rows < MIN_ROWS + 1) /* need room for one window and command line */ Rows = MIN_ROWS + 1; screen_new_rows(); /* may need to update window sizes */ } /* * set window size * If 'mustset' is TRUE, we must set Rows and Columns, do not get real * window size (this is used for the :win command). * If 'mustset' is FALSE, we may try to get the real window size and if * it fails use 'width' and 'height'. */ void set_winsize(width, height, mustset) int width, height; int mustset; { register int tmp; if (width < 0 || height < 0) /* just checking... */ return; if (State == HITRETURN || State == SETWSIZE) /* postpone the resizing */ { State = SETWSIZE; return; } screenclear(); #ifdef AMIGA flushbuf(); /* must do this before mch_get_winsize for some obscure reason */ #endif /* AMIGA */ if (mustset || mch_get_winsize() == FAIL) { Rows = height; Columns = width; check_winsize(); /* always check, to get p_scroll right */ mch_set_winsize(); } else check_winsize(); /* always check, to get p_scroll right */ if (State == HELP) (void)redrawhelp(); else if (!starting) { tmp = RedrawingDisabled; RedrawingDisabled = FALSE; comp_Botline_all(); updateScreen(CURSUPD); RedrawingDisabled = tmp; if (State == CMDLINE) redrawcmdline(); else setcursor(); } flushbuf(); } void settmode(raw) int raw; { static int oldraw = FALSE; if (oldraw == raw) /* skip if already in desired mode */ return; oldraw = raw; mch_settmode(raw); /* machine specific function */ } void starttermcap() { outstr(T_TS); /* start termcap mode */ outstr(T_KS); /* start "keypad transmit" mode */ flushbuf(); termcap_active = TRUE; } void stoptermcap() { outstr(T_KE); /* stop "keypad transmit" mode */ flushbuf(); termcap_active = FALSE; cursor_on(); /* just in case it is still off */ outstr(T_TE); /* stop termcap mode */ } /* * enable cursor, unless in Visual mode or no inversion possible */ static int cursor_is_off = FALSE; void cursor_on() { if (cursor_is_off && (!VIsual.lnum || highlight == NULL)) { outstr(T_CV); cursor_is_off = FALSE; } } void cursor_off() { if (!cursor_is_off) outstr(T_CI); /* disable cursor */ cursor_is_off = TRUE; } /* * set scrolling region for window 'wp' */ void scroll_region_set(wp) WIN *wp; { OUTSTR(tgoto((char *)T_CS, wp->w_winpos + wp->w_height - 1, wp->w_winpos)); } /* * reset scrolling region to the whole screen */ void scroll_region_reset() { OUTSTR(tgoto((char *)T_CS, (int)Rows - 1, 0)); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.