char *mytty; extern char *getlogin (); /* */ char *userName = getlogin (); srand (getpid () + getuid ()); /* * */ /* */ for (nitems = 0; menuText[nitems].m_text != NULL; nitems++); /* */ tinit (); ttinit(); mytty = ttyname(fdtty); openVisual (); . , 1992-95 - 365 - UNIX again: clearScreen (); if (mytty != NULL && userName != NULL) { gotoXY (0, TIMELINE); bold (); printf ("%s", userName); normal (); printf (" at %s (%s)", mytty, tname); } drawTitle ("", Y_TOP - 4); drawTitle (title, Y_TOP - 3); drawTitle ("", Y_TOP - 2); /* */ for (i = 0; i < nitems; i++) { drawItem (i, 20, Y_TOP + i, 0); } /* */ for (n=0; ; ) { printTime (); /* */ drawItem (n, 20, Y_TOP + n, 1); c = getcharacter (); drawItem (n, 20, Y_TOP + n, 0); switch (c) { case ' ': go_down: n++; if (n == nitems) n = 0; break; case '\b': case 0177: n--; if (n < 0) n = nitems - 1; break; case ESC: goto out; case '\t': /* Unselect item */ if (menuText[n].m_label != 0) { menuText[n].m_label = 0; drawItem (n, 20, Y_TOP + n, 0); nselected--; prSelects (); } goto go_down; . , 1992-95 - 366 - UNIX case '\r': /* Select item */ case '\n': bold (); drawTitle (menuText[n].m_text, LINES - 2); /* last but two line */ normal (); if (menuText[n].m_label == 0) { menuText[n].m_label = 1; drawItem (n, 20, Y_TOP + n, 0); nselected++; prSelects (); } goto go_down; default: goto go_down; } } out: clearScreen (); gotoXY (COLS / 3, LINES / 2); bold (); printf (" ."); normal (); /* */ while (!(c = readkey ())) { /* */ gotoXY (rand () % (COLS - 1), rand () % LINES); putchar ("@.*"[rand () % 3]); /* */ fflush (stdout); } standout (); printf (" 0%o\n", c & 0377); standend (); if (c == ESC) { sleep (2); /* 2 */ goto again; } die (0); /* , * */ } . , 1992-95 - 367 - UNIX /* i * (x,y) */ void drawItem (i, x, y, out) { gotoXY (x, y); if (out) { standout (); bold (); } printf ("%c %s ", menuText[i].m_label ? '-' : ' ', /* */ menuText[i].m_text /* */ ); if (out) { standend (); normal (); } } /* */ void drawTitle (title, y) char *title; { register int n; int length = strlen (title); /* */ gotoXY (0, y); /* clearEOL(); */ standout (); for (n = 0; n < (COLS - length) / 2; n++) putchar (' '); printf ("%s", title); n += length; /* */ for (; n < COLS - 1; n++) putchar (' '); standend (); } /* */ void prSelects () { char buffer[30]; if (nselected == 0) { gotoXY (0, LINES - 1); clearEOL (); } else { sprintf (buffer, ": %d/%d", nselected, nitems); drawTitle (buffer, LINES - 1); } } . , 1992-95 - 368 - UNIX /* -------------------------- */ #define PAUSE 4 int alarmed; /* */ /* "" */ void onalarm (nsig) { alarmed = 1; } /* , PAUSE . * ''. */ int getcharacter () { int c; fflush(stdout); /* */ signal (SIGALRM, onalarm); alarmed = 0; /* */ /* "" PAUSE */ alarm (PAUSE); /* . * , * . */ c = getchar (); /* */ if (!alarmed) { /* */ alarm (0); /* */ return c; } /* "" */ return ' '; /* */ } /* ---- NDELAY read ----------------------------- */ /* 0 , * */ char readkey () { char c; int nread; nread = read (fdtty, &c, 1); /* read() . * O_NDELAY , " 0 ". */ return (nread == 0) ? 0 : c; } . , 1992-95 - 369 - UNIX /* -------- ------------------------ */ void printTime () { time_t t; /* */ struct tm *tm; extern struct tm *localtime (); char tmbuf[30]; static char *week[7] = { "", "", "", "", "", "", "" }; static char *month[12] = { "", "", "", "", "", "", "", "", "", "", "", "" }; time (&t); /* */ tm = localtime (&t); /* */ sprintf (tmbuf, "%2s %02d:%02d:%02d %02d-%3s-%d", week[tm -> tm_wday], /* (0..6) */ tm -> tm_hour, /* (0..23) */ tm -> tm_min , /* (0..59) */ tm -> tm_sec , /* (0..59) */ tm -> tm_mday, /* (1..31) */ month[tm -> tm_mon], /* (0..11) */ tm -> tm_year + 1900 /* */ ); gotoXY (COLS / 2, TIMELINE); clearEOL (); gotoXY (COLS - strlen (tmbuf) - 1, TIMELINE); bold (); printf ("%s", tmbuf); normal (); } 8.14. , 20 . : a) . b) termcap, . c) , curses. d) curses, 2- 2- . e) . 8.15. - (ECHO). 8.16. " " (CBREAK). , - , |-. ____________________ |- "character lists" - clist. "" (raw) clist, , ; "" clist, - , . ( ) ( ICANON). . , 1992-95 - 370 - UNIX "" "" --->--*-->-->read | - V /dev/tty?? | <------<--*--<-----------<---write -  , read-: . , ICANON, : - , CTRL/U ; |=. ( read, gets, putchar) , <ENTER> '\n'. , - "" read. '\n', , . , "" : <ENTER>. CBREAK  ( '\n'). - , . - - ! , <ENTER> (" ") - '\n' - "- " , , - . - . MS DOS ~ECHO+CBREAK getch(). UNIX getchar(), - tty ioctl. ( ). ( |-|-). (, , - write) ( ); . . 8.17.  , - . , ANSI, :   "\033[A"  Home "\033[H"   "\033[B"  End "\033[F"   "\033[C"  PgUp "\033[I"   "\033[D"  PgDn "\033[G" ( '\033' (escape), escape-).  0xFF. , , - ( ). ____________________ |= , , ... - ioctl. . |-|- "" , stty sane . , 1992-95 - 371 - UNIX ,  '\033' - Esc. , 033 - , alarm(1); - 033 Esc. , . - (0..0377), >= 0400. , : termcap. - ( . - , ): ---> '\033' ---> '[' ---> 'A' --> 0400 | \--> 'B' --> 0401 | \--> 'C' --> 0402 | \--> 'D' --> 0403 \--> 'X' -----------> 0404 ... . curses . , keypad: int c; WINDOW *window; ... keypad(window, TRUE); ... c = wgetch(window); wgetch() . <curses.h> KEY_LEFT, KEY_RIGHT ... , window stdscr ( - include- curses.h). # ======================================== Makefile getch getch: getch.o cc getch.o -o getch -ltermlib getch.o: getch.c getch.h cc -g -DUSG -c getch.c . , 1992-95 - 372 - UNIX /* . */ /* ================================================== getch.h */ #define FALSE 0 #define TRUE 1 #define BOOLEAN unsigned char #define INPUT_CHANNEL 0 #define OUTPUT_CHANNEL 1 #define KEY_DOWN 0400 #define KEY_UP 0401 #define KEY_LEFT 0402 #define KEY_RIGHT 0403 #define KEY_PGDN 0404 #define KEY_PGUP 0405 #define KEY_HOME 0406 #define KEY_END 0407 #define KEY_BACKSPACE 0410 #define KEY_BACKTAB 0411 #define KEY_DC 0412 #define KEY_IC 0413 #define KEY_DL 0414 #define KEY_IL 0415 #define KEY_F(n) (0416+n) #define ESC ' 33' extern char *tgetstr(); void _put(char c); void _puts(char *s); void keyboard_access_denied(void); char *strdup(const char *s); void keyinit(void); int getc_raw(void); void keyreset(void); int getch(void); int lgetch(BOOLEAN); int ggetch(BOOLEAN); int kgetch(void); void _sigalrm(int n); void init_keytry(void); void add_to_try(char *str, short code); void keypad_on(void); void keypad_off(void); int dotest(void); void tinit(void); void main(void); . , 1992-95 - 373 - UNIX /* ===================================================== getch.c * The source version of getch.c file was * written by Pavel Curtis. * */ #include <stdio.h> #include <signal.h> #include <setjmp.h> #include <termios.h> #include <ctype.h> #include <string.h> #include <locale.h> #include "getch.h" #define keypad_local S[0] #define keypad_xmit S[1] #define key_backspace S[2] #define key_backtab S[3] #define key_left S[4] #define key_right S[5] #define key_up S[6] #define key_down S[7] #define key_ic S[8] #define key_dc S[9] #define key_il S[10] #define key_dl S[11] #define key_f1 S[12] #define key_f2 S[13] #define key_f3 S[14] #define key_f4 S[15] #define key_f5 S[16] #define key_f6 S[17] #define key_f7 S[18] #define key_f8 S[19] #define key_f9 S[20] #define key_f10 S[21] /* f0 */ #define key_f11 S[22] /* f11 */ #define key_f12 S[23] /* f12 */ #define key_home S[24] #define key_end S[25] #define key_npage S[26] #define key_ppage S[27] #define TOTAL 28 . , 1992-95 - 374 - UNIX /* descriptors for keys */ char *KEYS[TOTAL+1] = { "ke", "ks", "kb", "kB", "kl", "kr", "ku", "kd", "kI", "kD", "kA", "kL", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f0", "f.", "f-", "kh", "kH", "kN", "kP", NULL }, *S[TOTAL]; void _put (char c) { write( INPUT_CHANNEL, &c, 1 ); } void _puts(char *s) { tputs ( s, 1, _put ); } static int _backcnt = 0; static char _backbuf[30]; static struct try { struct try *child; struct try *sibling; char ch; short value; } *_keytry; BOOLEAN keypadok = FALSE; struct termios new_modes; void keyboard_access_denied(){ printf( " .\n" ); exit(1); } char *strdup(const char *s) { return strcpy((char *) malloc(strlen(s)+1), s); } . , 1992-95 - 375 - UNIX /* */ void keyinit(){ char *key, nkey[80], *p; register i; keyreset(); for( i=0; i < TOTAL; i++ ){ p = nkey; printf("tgetstr(%s)...", KEYS[i]); key = tgetstr(KEYS[i], &p); if(S[i]) free(S[i]); if(key == NULL){ S[i] = NULL; /* No such key */ printf(" .\n"); }else{ /* Decrypted string */ S[i] = strdup(key); printf(".\n"); } } init_keytry(); if( tcgetattr(INPUT_CHANNEL, &new_modes) < 0 ){ keyboard_access_denied(); } /* input flags */ /* '\r' '\n' */ new_modes.c_iflag &= ~ICRNL; if ((new_modes.c_cflag & CSIZE) == CS8) /* 8- */ new_modes.c_iflag &= ~ISTRIP; /* & 0177 */ /* output flags */ /* TAB3 - '\t' */ /* ONLCR - '\n' '\r\n' */ new_modes.c_oflag &= ~(TAB3 | ONLCR); /* local flags */ /* ICANON, CBREAK */ /* */ new_modes.c_lflag &= ~(ICANON | ECHO); /* control chars */ /* ... */ new_modes.c_cc[VMIN] = 1; /* 1 */ new_modes.c_cc[VTIME] = 0; /* 0 */ /* CBREAK */ /* , * . 0 , * */ new_modes.c_cc[VINTR] = '\0'; /* , SIGINT */ new_modes.c_cc[VQUIT] = '\0'; /* , SIGQUIT */ new_modes.c_cc[VERASE] = '\0'; /* ( )*/ new_modes.c_cc[VKILL] = '\0'; /* */ } . , 1992-95 - 376 - UNIX /* */ int getc_raw(){ int n; char c; n = read(INPUT_CHANNEL, &c, 1); if (n <= 0) return EOF; return (c & 0xFF); } static BOOLEAN _getback = FALSE; static char _backchar = '\0'; /* - ( ), */ #define nextc() (_backcnt > 0 ? _backbuf[--_backcnt] : \ _getback ? _getback = FALSE, _backchar : \ getc_raw()) #define putback(ch) _backbuf[_backcnt++] = ch void keyreset(){ _backcnt = 0; _backchar = '\0'; _getback = FALSE; } /* */ int getch(){ int c = lgetch(TRUE); keypad_off(); return c; } /* ! , getch(), read() 0 errno == EINTR. getch() '\0'. lgetch() */ int lgetch(BOOLEAN kpad) { int c; while((c = ggetch(kpad)) <= 0); return c; } int ggetch(BOOLEAN kpad) { int kgetch(); if( kpad ) keypad_on(); else keypad_off(); return keypadok ? kgetch() : nextc(); } . , 1992-95 - 377 - UNIX /* ** int kgetch() ** ** Get an input character, but take care of keypad sequences, returning ** an appropriate code when one matches the input. After each character ** is received, set a one-second alarm call. If no more of the sequence ** is received by the time the alarm goes off, pass through the sequence ** gotten so far. ** */ #define CRNL(c) (((c) == '\r') ? '\n' : (c)) /* */ #if !defined(XENIX) || defined(VENIX) # define unify(c) ( (c)&(( (c)&0100 ) ? ~0240 : 0377 )) #else # define unify(c) (c) #endif . , 1992-95 - 378 - UNIX /* ==================================================================== */ #if !defined(XENIX) && !defined(USG) && !defined(M_UNIX) && !defined(unix) /* BSD */ static BOOLEAN alarmed; jmp_buf jbuf; int kgetch() { register struct try *ptr; int ch; char buffer[10]; /* Assume no sequences longer than 10 */ register char *bufp = buffer; void (*oldsig)(); void _sigalrm(); ptr = _keytry; oldsig = signal(SIGALRM, _sigalrm); alarmed = FALSE; if( setjmp( jbuf )) /* read- */ ch = EOF; do { if( alarmed ) break; ch = nextc(); if (ch != EOF) /* getc() returns EOF on error, too */ *(bufp++) = ch; if (alarmed) break; while (ptr != (struct try *)NULL && (ch == EOF || unify(CRNL(ptr->ch)) != unify(CRNL(ch)) )) ptr = ptr->sibling; if (ptr != (struct try *)NULL) { if (ptr->value != 0) { alarm(0); signal(SIGALRM, oldsig); return(ptr->value); } else { ptr = ptr->child; alarm(1); } } } while (ptr != (struct try *)NULL); alarm(0); signal(SIGALRM, oldsig); if (ch == EOF && bufp == buffer) return ch; . , 1992-95 - 379 - UNIX while (--bufp > buffer) putback(*bufp); return(*bufp & 0377); } void _sigalrm(int n) { alarmed = TRUE; longjmp(jbuf, 1); } . , 1992-95 - 380 - UNIX /* ==================================================================== */ #else /* XENIX or USG */ /* SYSTEM V */ static BOOLEAN alarmed; int kgetch() { register struct try *ptr; int ch; char buffer[10]; /* Assume no sequences longer than 10 */ register char *bufp = buffer; void (*oldsig)(); void _sigalrm(); ptr = _keytry; oldsig = signal(SIGALRM, _sigalrm); alarmed = FALSE; do { ch = nextc(); if (ch != EOF) /* getc() returns EOF on error, too */ *(bufp++) = ch; if (alarmed) break; while (ptr != (struct try *)NULL && (ch == EOF || unify(CRNL(ptr->ch)) != unify(CRNL(ch)) )) ptr = ptr->sibling; if (ptr != (struct try *)NULL) { if (ptr->value != 0) { alarm(0); signal(SIGALRM, oldsig); return(ptr->value); } else { ptr = ptr->child; alarm(1); } } } while (ptr != (struct try *)NULL); alarm(0); signal(SIGALRM, oldsig); if (ch == EOF && bufp == buffer) return ch; while (--bufp > buffer) putback(*bufp); return(*bufp & 0377); } . , 1992-95 - 381 - UNIX void _sigalrm(int n) { alarmed = TRUE; signal(SIGALRM, _sigalrm); } #endif /*XENIX*/ /* ==================================================================== */ /* ** init_keytry() ** . ** */ void init_keytry() { _keytry = (struct try *) NULL; add_to_try(key_backspace, KEY_BACKSPACE); add_to_try("\b", KEY_BACKSPACE); add_to_try("\177", KEY_BACKSPACE); add_to_try(key_backtab, KEY_BACKTAB); add_to_try(key_dc, KEY_DC); add_to_try(key_dl, KEY_DL); add_to_try(key_down, KEY_DOWN); add_to_try(key_f1, KEY_F(1)); add_to_try(key_f2, KEY_F(2)); add_to_try(key_f3, KEY_F(3)); add_to_try(key_f4, KEY_F(4)); add_to_try(key_f5, KEY_F(5)); add_to_try(key_f6, KEY_F(6)); add_to_try(key_f7, KEY_F(7)); add_to_try(key_f8, KEY_F(8)); add_to_try(key_f9, KEY_F(9)); add_to_try(key_f10, KEY_F(10)); add_to_try(key_f11, KEY_F(11)); add_to_try(key_f12, KEY_F(12)); add_to_try(key_home, KEY_HOME); add_to_try(key_ic, KEY_IC); add_to_try(key_il, KEY_IL); add_to_try(key_left, KEY_LEFT); add_to_try(key_npage, KEY_PGDN); add_to_try(key_ppage, KEY_PGUP); add_to_try(key_right, KEY_RIGHT); add_to_try(key_up, KEY_UP); add_to_try(key_end, KEY_END); } . , 1992-95 - 382 - UNIX void add_to_try(char *str, short code) { static BOOLEAN out_of_memory = FALSE; struct try *ptr, *savedptr; if (str == NULL || out_of_memory) return; if (_keytry != (struct try *) NULL) { ptr = _keytry; for (;;) { while (ptr->ch != *str && ptr->sibling != (struct try *)NULL) ptr = ptr->sibling; if (ptr->ch == *str) { if (*(++str)) { if (ptr->child != (struct try *)NULL) ptr = ptr->child; else break; } else { ptr->value = code; return; } } else { if ((ptr->sibling = (struct try *) malloc(sizeof *ptr)) == (struct try *)NULL) { out_of_memory = TRUE; return; } savedptr = ptr = ptr->sibling; ptr->child = ptr->sibling = (struct try *)NULL; ptr->ch = *str++; ptr->value = 0; break; } } /* end for (;;) */ } else /* _keytry == NULL :: First sequence to be added */ { savedptr = ptr = _keytry = (struct try *) malloc(sizeof *ptr); if (ptr == (struct try *) NULL) { out_of_memory = TRUE; return; } ptr->child = ptr->sibling = (struct try *) NULL; . , 1992-95 - 383 - UNIX ptr->ch = *(str++); ptr->value = 0; } /* at this point, we are adding to the try. ptr->child == NULL */ while (*str) { ptr->child = (struct try *) malloc(sizeof *ptr); ptr = ptr->child; if (ptr == (struct try *)NULL) { out_of_memory = TRUE; ptr = savedptr; while (ptr != (struct try *)NULL) { savedptr = ptr->child; free(ptr); ptr = savedptr; } return; } ptr->child = ptr->sibling = (struct try *)NULL; ptr->ch = *(str++); ptr->value = 0; } ptr->value = code; return; } /* */ void keypad_on(){ if( keypadok ) return; keypadok = TRUE; if( keypad_xmit ) _puts( keypad_xmit ); } /* */ void keypad_off(){ if( !keypadok ) return; keypadok = FALSE; if( keypad_local ) _puts( keypad_local ); } . , 1992-95 - 384 - UNIX /* */ int dotest() { struct termios saved_modes; int c; char *s; char keyname[20]; if( tcgetattr(INPUT_CHANNEL, &saved_modes) < 0 ){ err: keyboard_access_denied(); } if( tcsetattr(INPUT_CHANNEL, TCSADRAIN, &new_modes) < 0 ) goto err; keyreset(); for(;;){ c = getch(); switch(c){ case KEY_DOWN: s = "K_DOWN" ; break; case KEY_UP: s = "K_UP" ; break; case KEY_LEFT: s = "K_LEFT" ; break; case KEY_RIGHT: s = "K_RIGHT" ; break; case KEY_PGDN: s = "K_PGDN" ; break; case KEY_PGUP: s = "K_PGUP" ; break; case KEY_HOME: s = "K_HOME" ; break; case KEY_END: s = "K_END" ; break; case KEY_BACKSPACE: s = "K_BS" ; break; case '\t': s = "K_TAB" ; break; case KEY_BACKTAB: s = "K_BTAB" ; break; case KEY_DC: s = "K_DEL" ; break; case KEY_IC: s = "K_INS" ; break; case KEY_DL: s = "K_DL" ; break; case KEY_IL: s = "K_IL" ; break; case KEY_F(1): s = "K_F1" ; break; case KEY_F(2): s = "K_F2" ; break; case KEY_F(3): s = "K_F3" ; break; case KEY_F(4): s = "K_F4" ; break; case KEY_F(5): s = "K_F5" ; break; case KEY_F(6): s = "K_F6" ; break; case KEY_F(7): s = "K_F7" ; break; case KEY_F(8): s = "K_F8" ; break; case KEY_F(9): s = "K_F9" ; break; case KEY_F(10): s = "K_F10" ; break; case KEY_F(11): s = "K_F11" ; break; case KEY_F(12): s = "K_F12" ; break; case ESC: s = "ESC" ; break; case EOF: s = "K_EOF" ; break; case '\r': s = "K_RETURN"; break; case '\n': s = "K_ENTER" ; break; default: s = keyname; if( c >= 0400 ){ sprintf(keyname, "K_F%d", c - KEY_F(0)); } else if( iscntrl(c)){ sprintf(keyname, "CTRL(%c)", c + 'A' - 1); } else { sprintf(keyname, "%c", c ); . , 1992-95 - 385 - UNIX } } printf(": %s\n\r", s); if(c == ESC) break; } tcsetattr(INPUT_CHANNEL, TCSADRAIN, &saved_modes); } /* */ void tinit (void) { /* static */ char Tbuf[2048]; /* Tbuf , tgetstr(). * static, keyinit() * tinit(), . */ char *tname; extern char *getenv(); if((tname = getenv("TERM")) == NULL){ printf("TERM : .\n"); exit(2); } printf(": %s\n", tname); /* Tbuf */ switch (tgetent(Tbuf, tname)) { case -1: printf (" TERMCAP (/etc/termcap).\n"); exit (1); case 0: printf (" '%s' .\n", tname); exit (2); case 1: break; /* OK */ } if(strlen(Tbuf) >= 1024) printf(" - \n"); keyinit(); /* , Tbuf[] */ } void main(void){ setlocale(LC_ALL, ""); tinit(); /* keyinit(); */ dotest(); exit(0); } . (, , .): . , - , - '\0', . ? strcmp : . , 1992-95 - 386 - UNIX "zzzzzzzzzza" "zzzzzzzzzzb" "zzzzzzzzzzbx" "zzzzzzzzzzc" "zzzzzzzzzzcx" ( ) zzzzzzzzzzcx zzzzzzzzzza | 11 , zzzzzzzzzzb | 11 , zzzzzzzzzzbx | 12 , zzzzzzzzzzc | 11 , zzzzzzzzzzcx V 12 , : 57 . : __z__z__z__z__z__z__z__z__z__z__a__\0 |_b__\0 | |_x__\0