This is vcs.c in view mode; [Download] [Up]
/* * Routines for VCS detection. */ #include <stdio.h> #include "config.h" #include "vcs.h" #ifndef OLDCURSES #include <curses.h> #include <term.h> #else /* OLDCURSES */ char tcbuf[1024]; #endif /* OLDCURSES */ static int putc_cnt; static char putc_buf[VCS_SIZE]; /* * Test for possible VCS (video command sequence). A character return * code means no match. An return code greater than 255 means a VCS * was found. */ int vcs_filter(c) char c; { extern int vcs_codes[NUM_VCS][VCS_SIZE], vcs_leadin[NUM_VCS]; extern int num_leadin; static int vcs_buf[VCS_SIZE], match_codes(); static int ptr = 0; register int i; int maybe, possible; /* see if possible */ possible = 0; if (ptr == 0) { /* * This is kinda crude... I'm checking to see if the * lead-in character is greater than the space character. * If so, it most probably is NOT a VCS. */ if (c >= ' ') return(c & 0xff); /* check the list */ for (i=0; i<num_leadin; i++) { if (c == vcs_leadin[i]) { possible++; break; } } if (!possible) return(c & 0xff); } /* build the string */ vcs_buf[ptr++] = c; vcs_buf[ptr] = -1; /* test for match */ maybe = 0; for (i=0; i<NUM_VCS; i++) { switch (match_codes(vcs_buf, vcs_codes[i], i)) { case YES: ptr = 0; return(i+256); case NO: break; case MAYBE: maybe++; break; } } /* abandon what you've got */ if (maybe && ptr == VCS_SIZE-1) { ptr = 0; return(c & 0xff); } /* hang on, wait and see */ if (maybe) return(MAYBE); /* a clean miss */ ptr = 0; return(c & 0xff); } /* * See if the two integer arrays "match". Character parameters are * designated by codes > 1000 and ASCII digit parameters are designated * by codes > 2000. Uses a simple linear search, so if NUM_VCS grows * this routine will have to mature a bit. */ static int match_codes(test, code, k) int test[], code[], k; { extern int vcs_param[NUM_VCS][5]; register int i, j; int pos, done; /* doesn't exist */ if (code[0] == -1) return(NO); i = 0; j = 0; while (i<VCS_SIZE && j<VCS_SIZE) { /* at the end (a match) */ if (test[i] == -1 && code[j] == -1) return(YES); /* ran out of input */ if (test[i] == -1) break; /* * The char parameter (code 1000) always matches the * next character. */ if (code[j] >= 1000 && code[j] < 2000) { pos = code[j] -1000; vcs_param[k][pos] = test[i]; i++; j++; continue; } /* * The digit parameter (code 2000) tries to match as many * ASCII digits as it can. */ if (code[j] >= 2000) { pos = code[j] -2000; /* done with this number? */ if (vcs_param[k][pos]) done = 1; else done = 0; /* only digits */ while (test[i] >= 48 && test[i] <= 57) { if (!done) vcs_param[k][pos] = (vcs_param[k][pos] * 10) + test[i] -48; i++; } /* ended in a digit */ if (test[i] == -1 && code[j+1] != -1) { vcs_param[k][pos] = 0; break; } j++; continue; } /* a clean miss */ if (test[i] != code[j]) { for (j=0; j<5; j++) vcs_param[k][j] = 0; return(NO); } i++; j++; } /* a maybe */ return(MAYBE); } /* * Build the table of VCS codes. Actually we cheat... We tell curses(3) * to build the strings to perform the function, and then we decipher * what it did. * * For example: On a vt100 the cursor motion string in terminfo is: * cup=\E[%i%p1%d;%p2%dH$<5> * * This gets translated to the integer array vcs_code[] as: * \E [ %p1%d ; %p2%d H * 27, 91, 2000, 59, 2001, 72 * * Notice that the "%p1" and "%p2" parameters get translated to 2000 and * 2001. This is to signify that the parameters are multiple digit ASCII * encoded numbers. The "%i" and "%d" codes are embedded into the vcs_opt[] * array in somewhat of a loose manner. In other words, there is no set * format for the vcs_opt[] array. The padding info "$<5>" is ignored. */ void vcs_table() { extern int vcs_codes[NUM_VCS][VCS_SIZE], vcs_opt[NUM_VCS][10]; extern int vcs_leadin[NUM_VCS], num_leadin, max_row, max_col; int i, j, k, match, temp[VCS_SIZE]; static int substr(); char *p, *strcpy(), buf[VCS_SIZE], *getenv(); char *tparm(); /* comment out, if required */ static void fake_it(); #ifdef OLDCURSES char tb[1024], *t, *cursor_home, *clr_eol, *clr_eos; char *clear_screen, *cursor_up, *cursor_down, *cursor_right; char *cursor_left, *cursor_address, *getenv(), *tgetstr(), *tgoto(); tgetent(tb, getenv("TERM")); t = tcbuf; cursor_home = tgetstr("ho", &t); clr_eol = tgetstr("ce", &t); clr_eos = tgetstr("cd", &t); clear_screen = tgetstr("cl", &t); cursor_up = tgetstr("up", &t); cursor_down = tgetstr("do", &t); cursor_right = tgetstr("nd", &t); cursor_left = tgetstr("le", &t); cursor_address = tgetstr("cm", &t); max_row = tgetnum("li"); max_col = tgetnum("co"); #else /* OLDCURSES */ setupterm(getenv("TERM"), 1, &i); max_row = lines; max_col = columns; #endif /* OLDCURSES */ /* * Do the easy ones first. These don't take positional parameters, * so all we have to do is strip the padding info. */ for (i=0; i<NUM_VCS; i++) { switch (i) { case HOME: p = cursor_home; break; case CLR_EOL: p = clr_eol; break; case CLR_EOS: p = clr_eos; break; case CLEAR: p = clear_screen; break; case MV_UP: p = cursor_up; break; case MV_DOWN: p = cursor_down; break; case MV_RIGHT: p = cursor_right; break; case MV_LEFT: p = cursor_left; break; default: p = ""; break; } /* * Either the capability doesn't exist, or we're gonna * do this one by hand (i.e.: ones with positional parameters) */ if (!p) { vcs_codes[i][0] = -1; continue; } /* fake an "output" */ fake_it(p); /* copy what it did */ j = 0; while (putc_buf[j]) { vcs_codes[i][j] = putc_buf[j]; j++; if (j == VCS_SIZE-1) break; } vcs_codes[i][j] = -1; } /* * And now for the difficult ones. The way it's done is: load the * string with a few known parameters and then find where the * parameters end up. The vcs_opt[][] array is "free-flowing" * and means something only to the routine being used. */ /* add one to the param */ if (substr(cursor_address, "%i") > 0) vcs_opt[MV_DIRECT][0] = 1; /* decimal codes used */ if (substr(cursor_address, "%d") > 0) vcs_opt[MV_DIRECT][1] = 1; /* character codes used */ if (substr(cursor_address, "%c") > 0) vcs_opt[MV_DIRECT][2] = 1; /* add an offset */ if (substr(cursor_address, "%+") > 0) vcs_opt[MV_DIRECT][3] = 1; /* subtract an offset */ if (substr(cursor_address, "%-") > 0) vcs_opt[MV_DIRECT][4] = 1; /* load with parameters 12 & 34 */ #ifdef OLDCURSES fake_it(tgoto(cursor_address, 12, 34)); #else /* OLDCURSES */ fake_it((char *)tparm(cursor_address, 12, 34)); #endif /* OLDCURSES */ j = 0; while (putc_buf[j]) { temp[j] = putc_buf[j]; j++; if (j == VCS_SIZE-1) break; } temp[j] = -1; /* if decimal parameters */ if (vcs_opt[MV_DIRECT][1]) { /* if add one */ if (vcs_opt[MV_DIRECT][0]) strcpy(buf, "13"); else strcpy(buf, "12"); /* where is the 12 (or 13)? */ if ((i = substr(putc_buf, buf)) > 0) { temp[i] = 2000; temp[i+1] = -2; } else temp[0] = -1; /* if add one */ if (vcs_opt[MV_DIRECT][0]) strcpy(buf, "35"); else strcpy(buf, "34"); /* where is the 34 (or 35)? */ if ((i = substr(putc_buf, buf)) > 0) { temp[i] = 2001; temp[i+1] = -2; } else temp[0] = -1; } /* if character parameters */ if (vcs_opt[MV_DIRECT][2]) { /* original with 12 and 34 */ strcpy(buf, putc_buf); /* change 12 to 13 */ #ifdef OLDCURSES fake_it(tgoto(cursor_address, 13, 34)); #else /* OLDCURSES */ fake_it((char *)tparm(cursor_address, 13, 34)); #endif /* OLDCURSES */ /* where are they different */ i = 0; while (buf[i] != '\0') { if (buf[i] != putc_buf[i]) break; i++; } /* sanity checking */ if (buf[i] == '\0') temp[0] = -1; /* if add, what is offset? */ if (vcs_opt[MV_DIRECT][3]) vcs_opt[MV_DIRECT][5] = temp[i] - 13; /* if subtract, what is offset? */ if (vcs_opt[MV_DIRECT][4]) vcs_opt[MV_DIRECT][5] = 13 - temp[i]; temp[i] = 1000; /* change 34 to 35 */ #ifdef OLDCURSES fake_it(tgoto(cursor_address, 12, 35)); #else /* OLDCURSES */ fake_it((char *)tparm(cursor_address, 12, 35)); #endif /* OLDCURSES */ /* where are they different */ i = 0; while (buf[i] != '\0') { if (buf[i] != putc_buf[i]) break; i++; } temp[i] = 1001; if (buf[i] == '\0') temp[0] = -1; } /* strip the -2's out, if any */ i = 0; j = 0; while (temp[i] != -1) { if (temp[i] != -2) vcs_codes[MV_DIRECT][j++] = temp[i]; i++; } vcs_codes[MV_DIRECT][j] = -1; /* * Simplify the list. Some codes are already handled by the * virtual screen routines... no need to duplicate them. */ if (vcs_codes[MV_DOWN][0] == '\n') vcs_codes[MV_DOWN][0] = -1; if (vcs_codes[MV_LEFT][0] == 8) vcs_codes[MV_LEFT][0] = -1; /* * Often the "clear screen" sequence will contain the "home" * sequence... if so, don't duplicate the "home" portion. */ fake_it(cursor_home); strcpy(buf, putc_buf); fake_it(clear_screen); /* if "home" inside "clear screen" */ if ((k = substr(putc_buf, buf)) >= 0) { /* if at the beginning */ if (k == 0) { i = 0; for (j=strlen(buf); j<VCS_SIZE; j++) vcs_codes[CLEAR][i++] = putc_buf[j]; vcs_codes[CLEAR][i] = -1; } /* if at the end */ else if (strlen(buf)+k == strlen(putc_buf)) vcs_codes[CLEAR][k] = -1; } /* is "clear screen" still unique */ k = 0; for (i=0; i<NUM_VCS; i++) { if (vcs_codes[CLEAR][i] == -1 || vcs_codes[CLR_EOS][i] == -1) break; if (vcs_codes[CLEAR][i] != vcs_codes[CLR_EOS][i]) { k++; break; } } if (k == 0) vcs_codes[CLEAR][0] = -1; /* * Make a list of unique lead-in characters to be used as a * simple hash table. */ num_leadin = 0; for (i=0; i<NUM_VCS; i++) { if (vcs_codes[i][0] == -1) continue; /* add any new lead-in character */ match = 0; for (j=0; j<num_leadin; j++) { if (vcs_leadin[j] == vcs_codes[i][0]) match++; } if (!match) vcs_leadin[num_leadin++] = vcs_codes[i][0]; } return; } /* * The routine that fakes curses(3) into outputting the string info with * the padding removed. */ static void fake_it(s) char *s; { static int fake_putc(); putc_cnt = 0; putc_buf[0] = '\0'; tputs(s, 1, fake_putc); putc_buf[putc_cnt] = '\0'; return; } static int fake_putc(c) char c; { if (c != '\0') putc_buf[putc_cnt++] = c; return(c); } /* * Is string2 contained in string1? If so, return the offset, otherwise * return a -1. */ static int substr(s1, s2) char *s1, *s2; { int i, len; len = strlen(s2); /* not possible */ if (len > strlen(s1)) return(-1); i = 0; while (*s1) { if (!strncmp(s1, s2, len)) return(i); s1++; i++; } return(-1); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.