char *mytty; extern char *getlogin (); /* imya pol'zovatelya */ char *userName = getlogin (); srand (getpid () + getuid ()); /* inicializirovat' * datchik sluchajnyh chisel */ /* schitaem stroki menyu */ for (nitems = 0; menuText[nitems].m_text != NULL; nitems++); /* inicializiruem terminal */ tinit (); ttinit(); mytty = ttyname(fdtty); openVisual (); A. Bogatyrev, 1992-95 - 365 - Si v 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); /* risuem menyu */ for (i = 0; i < nitems; i++) { drawItem (i, 20, Y_TOP + i, 0); } /* cikl peremeshchenij po menyu */ for (n=0; ; ) { printTime (); /* vydaem tekushchee vremya */ 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; A. Bogatyrev, 1992-95 - 366 - Si v 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 ("Nazhmi lyubuyu knopku."); normal (); /* zamusorit' ekran */ while (!(c = readkey ())) { /* sluchajnye tochki */ gotoXY (rand () % (COLS - 1), rand () % LINES); putchar ("@.*"[rand () % 3]); /* vydat' simvol */ fflush (stdout); } standout (); printf ("Nazhata knopka s kodom 0%o\n", c & 0377); standend (); if (c == ESC) { sleep (2); /* podozhdat' 2 sekundy */ goto again; } die (0); /* uspeshno zavershit'sya, * vosstanoviv rezhimy drajvera */ } A. Bogatyrev, 1992-95 - 367 - Si v UNIX /* Narisovat' stroku menyu nomer i * v koordinatah (x,y) s ili bez vydeleniya */ void drawItem (i, x, y, out) { gotoXY (x, y); if (out) { standout (); bold (); } printf ("%c %s ", menuText[i].m_label ? '-' : ' ', /* pomecheno ili net */ menuText[i].m_text /* sama stroka */ ); if (out) { standend (); normal (); } } /* narisovat' centrirovannuyu stroku v inversnom izobrazhenii */ void drawTitle (title, y) char *title; { register int n; int length = strlen (title); /* dlina stroki */ gotoXY (0, y); /* clearEOL(); */ standout (); for (n = 0; n < (COLS - length) / 2; n++) putchar (' '); printf ("%s", title); n += length; /* dorisovat' inversiej do konca ekrana */ for (; n < COLS - 1; n++) putchar (' '); standend (); } /* vydat' obshchee chislo vybrannyh strok */ void prSelects () { char buffer[30]; if (nselected == 0) { gotoXY (0, LINES - 1); clearEOL (); } else { sprintf (buffer, "Vybrano: %d/%d", nselected, nitems); drawTitle (buffer, LINES - 1); } } A. Bogatyrev, 1992-95 - 368 - Si v UNIX /* Rabota s budil'nikom -------------------------- */ #define PAUSE 4 int alarmed; /* flag budil'nika */ /* reakciya na signal "budil'nik" */ void onalarm (nsig) { alarmed = 1; } /* Prochest' simvol s klaviatury, no ne pozzhe chem cherez PAUSE sekund. * inache vernut' kod 'probel'. */ int getcharacter () { int c; fflush(stdout); /* zakazat' reakciyu na budil'nik */ signal (SIGALRM, onalarm); alarmed = 0; /* sbrosit' flag */ /* zakazat' signal "budil'nik" cherez PAUSE sekund */ alarm (PAUSE); /* zhdat' nazhatiya knopki. * |tot operator zavershitsya libo pri nazhatii knopki, * libo pri poluchenii signala. */ c = getchar (); /* proveryaem flag */ if (!alarmed) { /* byl nazhat simvol */ alarm (0); /* otmenit' zakaz budil'nika */ return c; } /* byl poluchen signal "budil'nik" */ return ' '; /* prodvinut' vybrannuyu stroku vniz */ } /* ---- NDELAY read ----------------------------- */ /* Vernut' 0 esli na klaviature nichego ne nazhato, * inache vernut' nazhatuyu knopku */ char readkey () { char c; int nread; nread = read (fdtty, &c, 1); /* obychnyj read() dozhidalsya by nazhatiya knopki. * O_NDELAY pozvolyaet ne zhdat', no vernut' "prochitano 0 simvolov". */ return (nread == 0) ? 0 : c; } A. Bogatyrev, 1992-95 - 369 - Si v UNIX /* -------- Rabota so vremenem ------------------------ */ void printTime () { time_t t; /* tekushchee vremya */ struct tm *tm; extern struct tm *localtime (); char tmbuf[30]; static char *week[7] = { "Vs", "Pn", "Vt", "Sr", "CHt", "Pt", "Sb" }; static char *month[12] = { "YAnv", "Fev", "Mar", "Apr", "Maj", "Iyun", "Iyul", "Avg", "Sen", "Okt", "Noya", "Dek" }; time (&t); /* uznat' tekushchee vremya */ tm = localtime (&t); /* razlozhit' ego na komponenty */ sprintf (tmbuf, "%2s %02d:%02d:%02d %02d-%3s-%d", week[tm -> tm_wday], /* den' nedeli (0..6) */ tm -> tm_hour, /* chasy (0..23) */ tm -> tm_min , /* minuty (0..59) */ tm -> tm_sec , /* sekundy (0..59) */ tm -> tm_mday, /* chislo mesyaca (1..31) */ month[tm -> tm_mon], /* mesyac (0..11) */ tm -> tm_year + 1900 /* god */ ); gotoXY (COLS / 2, TIMELINE); clearEOL (); gotoXY (COLS - strlen (tmbuf) - 1, TIMELINE); bold (); printf ("%s", tmbuf); normal (); } 8.14. Napishite programmu, vydayushchuyu fajl na ekran porciyami po 20 strok i ozhidayushchuyu nazhatiya klavishi. Uslozhneniya: a) dobavit' klavishu dlya vozvrata k nachalu fajla. b) ispol'zuya biblioteku termcap, ochishchat' ekran pered vydachej ocherednoj porcii teksta. c) napishite etu programmu, ispol'zuya biblioteku curses. d) ispol'zuya curses, napishite programmu parallel'nogo prosmotra 2-h fajlov v 2-h neperekryvayushchihsya oknah. e) to zhe v perekryvayushchihsya oknah. 8.15. Napishite funkcii vklyucheniya i vyklyucheniya rezhima eho-otobrazheniya nabiraemyh na klaviature simvolov (ECHO). 8.16. To zhe pro "rezhim nemedlennogo vvoda" (CBREAK). V obychnom rezhime stroka, nab- rannaya na klaviature, snachala popadaet v nekotoryj bufer v drajvere terminala|-. ____________________ |- Takie bufera nosyat nazvanie "character lists" - clist. Sushchestvuyut "syroj" (raw) clist, v kotoryj popadayut VSE simvoly, vvodimye s klaviatury; i "kanonicheskij" clist, v kotorom hranitsya otredaktirovannaya stroka - obrabotany zaboj, otmena stroki. Sami special'nye simvoly (redaktirovaniya i generacii signalov) v kanonicheskuyu ochered' ne popadayut (v rezhime ICANON). A. Bogatyrev, 1992-95 - 370 - Si v UNIX "Syraya" "Kanonicheskaya" klaviatura--->Ochered'Vvoda--*-->Ochered'Vvoda-->read | fajl-ustrojstvo drajver terminala V eho /dev/tty?? | ekran<---Ochered'Vyvoda---<--*--<-----------<---write |tot bufer ispol'zuetsya dlya predchteniya - vy mozhete nabirat' tekst na klaviature eshche do togo, kak programma zaprosit ego read-om: etot nabrannyj tekst sohranitsya v bufere i pri postuplenii zaprosa budet vydan iz bufera. Takzhe, v kanonicheskom rezhime ICANON, bufer vvoda ispol'zuetsya dlya redaktirovaniya vvedennoj stroki: zaboj otmenyaet posled- nij nabrannyj simvol, CTRL/U otmenyaet vsyu nabrannuyu stroku; a takzhe on ispol'zuetsya dlya vypolneniya nekotoryh preobrazovanij simvolov na vvode i vyvode|=. Vvedennaya stroka popadaet v programmu (kotoraya zaprosila dannye s klaviatury pri pomoshchi read, gets, putchar) tol'ko posle togo, kak vy nazhmete knopku <ENTER> s kodom '\n'. Do etogo vvodimye simvoly nakaplivayutsya v bufere, no v programmu ne peredayutsya - programma tem vremenem "spit" v vyzove read. Kak tol'ko budet nazhat simvol '\n', on sam postupit v bufer, a programma budet razbuzhena i smozhet nakonec prochest' iz bufera vvoda nabrannyj tekst. Dlya menyu, redaktorov i drugih "ekrannyh" programm etot rezhim neudoben: prishlos' by slishkom chasto nazhimat' <ENTER>. V rezhime CBREAK nazhataya bukva nemedlenno popadaet v vashu programmu (bez ozhidaniya nazhatiya '\n'). V dannom sluchae bufer drajvera ispol'- zuetsya tol'ko dlya predchteniya, no ne dlya redaktirovaniya vvodimogo teksta. Redaktiro- vanie vozlagaetsya na vas - predusmotrite ego v svoej programme sami! Zamet'te, chto kod knopki <ENTER> ("konec vvoda") - '\n' - ne tol'ko "protalki- vaet" tekst v programmu, no i sam popadaet v bufer drajvera, a zatem v vashu prog- rammu. Ne zabyvajte ego kak-to obrabatyvat'. V MS DOS funkciya chteniya knopki v rezhime ~ECHO+CBREAK nazyvaetsya getch(). V UNIX analogichno ej budet rabotat' obychnyj getchar(), esli pered ego ispol'zovaniem ustano- vit' nuzhnye rezhimy drajvera tty vyzovom ioctl. Po okonchanii programmy rezhim drajvera nado vosstanovit' (za vas eto nikto ne sdelaet). Takzhe sleduet vosstanavlivat' rezhim drajvera pri avarijnom zavershenii programmy (po lyubomu signalu|-|-). Ocheredi vvoda i vyvoda ispol'zuyutsya takzhe dlya sinhronizacii skorosti raboty programmy (skazhem, skorosti napolneniya bufera vyvoda simvolami, postupayushchimi iz prog- rammy cherez vyzovy write) i skorosti raboty ustrojstva (s kotoroj drajver vybiraet simvoly s drugogo konca ocheredi i vydaet ih na ekran); a takzhe dlya preobrazovanij simvolov na vvode i vyvode. Primer upravleniya vsemi rezhimami est' v prilozhenii. 8.17. Funkcional'nye klavishi bol'shinstva displeev posylayut v liniyu ne odin, a nes- kol'ko simvolov. Naprimer na terminalah, rabotayushchih v sisteme komand standarta ANSI, knopki so strelkami posylayut takie posledovatel'nosti: strelka vverh "\033[A" knopka Home "\033[H" strelka vniz "\033[B" knopka End "\033[F" strelka vpravo "\033[C" knopka PgUp "\033[I" strelka vlevo "\033[D" knopka PgDn "\033[G" (poskol'ku pervym simvolom upravlyayushchih posledovatel'nostej obychno yavlyaetsya simvol '\033' (escape), to ih nazyvayut eshche escape-posledovatel'nostyami). Nam zhe v programme udobno vosprinimat' takuyu posledovatel'nost' kak edinstvennyj kod s celym znacheniem bol'shim 0xFF. Sklejka posledovatel'nostej simvolov, postupayushchih ot funkcional'nyh klavish, v takoj vnutrennij kod - takzhe zadacha ekrannoj biblioteki (uchet sistemy komand displeya na vvode). ____________________ |= Rezhimy preobrazovanij, simvoly redaktirovaniya, i.t.p. upravlyayutsya sistemnym vy- zovom ioctl. Bol'shoj primer na etu temu est' v prilozhenii. |-|- Esli vasha programma zavershilas' avarijno i mody terminala ostalis' v "strannom" sostoyanii, to privesti terminal v chuvstvo mozhno komandoj stty sane A. Bogatyrev, 1992-95 - 371 - Si v UNIX Samym interesnym yavlyaetsya to, chto odinochnyj simvol '\033' tozhe mozhet prijti s klaviatury - ego posylaet klavisha Esc. Poetomu esli my stroim raspoznavatel' klavish, kotoryj pri postuplenii koda 033 nachinaet ozhidat' sostavnuyu posledovatel'nost' - my dolzhny vystavlyat' tajmaut, naprimer alarm(1); i esli po ego istechenii bol'she nikakih simvolov ne postupilo - vydavat' kod 033 kak kod klavishi Esc. Napishite raspoznavatel' kodov, postupayushchih s klaviatury. Kody obychnyh bukv vyda- vat' kak est' (0..0377), kody funkcional'nyh klavish vydavat' kak chisla >= 0400. Uchtite, chto raznye tipy displeev posylayut raznye posledovatel'nosti ot odnih i teh zhe funkcional'nyh klavish: predusmotrite nastrojku na sistemu komand DANNOGO displeya pri pomoshchi biblioteki termcap. Raspoznavatel' udobno stroit' pri pomoshchi sravneniya postu- payushchih simvolov s vetvyami dereva (spuskayas' po nuzhnoj vetvi dereva pri postuplenii ocherednogo simvola. Kak tol'ko dostigli lista dereva - vozvrashchaem kod, pripisannyj etomu listu): ---> '\033' ---> '[' ---> 'A' --> vydat' 0400 | \--> 'B' --> 0401 | \--> 'C' --> 0402 | \--> 'D' --> 0403 \--> 'X' -----------> 0404 ... Nuzhnoe derevo strojte pri nastrojke na sistemu komand dannogo displeya. Biblioteka curses uzhe imeet takoj vstroennyj raspoznavatel'. CHtoby sostavnye posledovatel'nosti skleivalis' v special'nye kody, vy dolzhny ustanovit' rezhim keypad: int c; WINDOW *window; ... keypad(window, TRUE); ... c = wgetch(window); Bez etogo wgetch() schityvaet vse simvoly poodinochke. Simvolicheskie nazvaniya kodov dlya funkcional'nyh klavish perechisleny v <curses.h> i imeyut vid KEY_LEFT, KEY_RIGHT i.t.p. Esli vy rabotaete s edinstvennym oknom razmerom s ves' ekran, to v kachestve parametra window vy dolzhny ispol'zovat' standartnoe okno stdscr (eto imya predoprede- leno v include-fajle curses.h). # ======================================== Makefile dlya getch getch: getch.o cc getch.o -o getch -ltermlib getch.o: getch.c getch.h cc -g -DUSG -c getch.c A. Bogatyrev, 1992-95 - 372 - Si v UNIX /* Razbor sostavnyh posledovatel'nostej klavish s klaviatury. */ /* ================================================== 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); A. Bogatyrev, 1992-95 - 373 - Si v 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 A. Bogatyrev, 1992-95 - 374 - Si v 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( "Klaviatura nedostupna.\n" ); exit(1); } char *strdup(const char *s) { return strcpy((char *) malloc(strlen(s)+1), s); } A. Bogatyrev, 1992-95 - 375 - Si v UNIX /* Inicializaciya tablicy strok */ 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("klavisha ne opredelena.\n"); }else{ /* Decrypted string */ S[i] = strdup(key); printf("schitano.\n"); } } init_keytry(); if( tcgetattr(INPUT_CHANNEL, &new_modes) < 0 ){ keyboard_access_denied(); } /* input flags */ /* otmenit' preobrazovanie koda '\r' v '\n' na vvode */ new_modes.c_iflag &= ~ICRNL; if ((new_modes.c_cflag & CSIZE) == CS8) /* 8-bitnyj kod */ new_modes.c_iflag &= ~ISTRIP; /* otmenit' & 0177 na vvode */ /* output flags */ /* otmenit' TAB3 - zamenu tabulyacij '\t' na probely */ /* otmenit' ONLCR - zamenu '\n' na paru '\r\n' na vyvode */ new_modes.c_oflag &= ~(TAB3 | ONLCR); /* local flags */ /* vyklyuchit' rezhim ICANON, vklyuchit' CBREAK */ /* vyklyuchit' ehootobrazhenie nabiraemyh simvolov */ new_modes.c_lflag &= ~(ICANON | ECHO); /* control chars */ /* pri vvode s klavish zhdat' ne bolee ... */ new_modes.c_cc[VMIN] = 1; /* 1 simvola i */ new_modes.c_cc[VTIME] = 0; /* 0 sekund */ /* |to sootvetstvuet rezhimu CBREAK */ /* Simvoly, nazhatie kotoryh zastavlyaet drajver terminala poslat' signal * libo otredaktirovat' nabrannuyu stroku. Znachenie 0 oznachaet, * chto sootvetstvuyushchego simvola ne budet */ new_modes.c_cc[VINTR] = '\0'; /* simvol, generyashchij SIGINT */ new_modes.c_cc[VQUIT] = '\0'; /* simvol, generyashchij SIGQUIT */ new_modes.c_cc[VERASE] = '\0'; /* zaboj (otmena poslednego simvola)*/ new_modes.c_cc[VKILL] = '\0'; /* simvol otmeny stroki */ } A. Bogatyrev, 1992-95 - 376 - Si v UNIX /* CHtenie odnogo simvola neposredstvenno s klaviatury */ 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'; /* CHtenie simvola - libo iz bufera (esli ne pust), libo s klaviatury */ #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; } /* Funkciya chteniya sostavnogo simvola */ int getch(){ int c = lgetch(TRUE); keypad_off(); return c; } /* VNIMANIE! Esli v processe budet poluchen signal, v to vremya kak process nahoditsya vnutri vyzova getch(), to sistemnyj vyzov read() vernet 0 i errno == EINTR. V etom sluchae getch() vernet '\0'. CHtoby izbezhat' etoj situacii ispol'zuetsya funkciya 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(); } A. Bogatyrev, 1992-95 - 377 - Si v 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)) /* bor'ba s russkoj klaviaturoj */ #if !defined(XENIX) || defined(VENIX) # define unify(c) ( (c)&(( (c)&0100 ) ? ~0240 : 0377 )) #else # define unify(c) (c) #endif A. Bogatyrev, 1992-95 - 378 - Si v UNIX /* ==================================================================== */ #if !defined(XENIX) && !defined(USG) && !defined(M_UNIX) && !defined(unix) /* Dlya semejstva 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 )) /* chtob svalit'sya syuda s read-a */ 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; A. Bogatyrev, 1992-95 - 379 - Si v UNIX while (--bufp > buffer) putback(*bufp); return(*bufp & 0377); } void _sigalrm(int n) { alarmed = TRUE; longjmp(jbuf, 1); } A. Bogatyrev, 1992-95 - 380 - Si v UNIX /* ==================================================================== */ #else /* XENIX or USG */ /* Dlya semejstva 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); } A. Bogatyrev, 1992-95 - 381 - Si v UNIX void _sigalrm(int n) { alarmed = TRUE; signal(SIGALRM, _sigalrm); } #endif /*XENIX*/ /* ==================================================================== */ /* ** init_keytry() ** Postroenie dereva razbora posledovatel'nostej simvolov. ** */ 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); } A. Bogatyrev, 1992-95 - 382 - Si v 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; A. Bogatyrev, 1992-95 - 383 - Si v 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; } /* Vklyuchenie al'ternativnogo rezhima klaviatury */ void keypad_on(){ if( keypadok ) return; keypadok = TRUE; if( keypad_xmit ) _puts( keypad_xmit ); } /* Vklyuchenie standartnogo rezhima klaviatury */ void keypad_off(){ if( !keypadok ) return; keypadok = FALSE; if( keypad_local ) _puts( keypad_local ); } A. Bogatyrev, 1992-95 - 384 - Si v UNIX /* Testovaya funkciya */ 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 ); A. Bogatyrev, 1992-95 - 385 - Si v UNIX } } printf("Klavisha: %s\n\r", s); if(c == ESC) break; } tcsetattr(INPUT_CHANNEL, TCSADRAIN, &saved_modes); } /* Funkciya nastrojki na sistemu komand displeya */ void tinit (void) { /* static */ char Tbuf[2048]; /* Tbuf dolzhen sohranyat'sya vse vremya, poka mogut vyzyvat'sya funkcii tgetstr(). * Dlya etogo on libo dolzhen byt' static, libo vyzov funkcii keyinit() * dolzhen nahodit'sya vnutri tinit(), chto i sdelano. */ char *tname; extern char *getenv(); if((tname = getenv("TERM")) == NULL){ printf("TERM ne opredeleno: neizvestnyj tip terminala.\n"); exit(2); } printf("Terminal: %s\n", tname); /* Prochest' opisanie terminala v Tbuf */ switch (tgetent(Tbuf, tname)) { case -1: printf ("Net fajla TERMCAP (/etc/termcap).\n"); exit (1); case 0: printf ("Terminal '%s' ne opisan.\n", tname); exit (2); case 1: break; /* OK */ } if(strlen(Tbuf) >= 1024) printf("Opisanie terminala slishkom dlinnoe - vozmozhny poteri v konce opisaniya\n"); keyinit(); /* inicializirovat' stroki, poka Tbuf[] dostupen */ } void main(void){ setlocale(LC_ALL, ""); tinit(); /* keyinit(); */ dotest(); exit(0); } Po povodu etogo algoritma nado skazat' eshche paru slov. Ego modifikaciya mozhet s uspehom primenyat'sya dlya poiska slov v tablice (komand, klyuchej v baze dannyh, itp.): spisok slov prevrashchaetsya v derevo. V takom poiskovom algoritme ne trebuyutsya tajmauty, neob- hodimye pri vvode s klaviatury, poskol'ku est' yavnye terminatory strok - simvoly '\0', kotoryh net pri vvode s klaviatury. V chem effektivnost' takogo algoritma? Sravnim posledovatel'nyj perebor pri pomoshchi strcmp i poisk v dereve bukv: A. Bogatyrev, 1992-95 - 386 - Si v UNIX "zzzzzzzzzza" "zzzzzzzzzzb" "zzzzzzzzzzbx" "zzzzzzzzzzc" "zzzzzzzzzzcx" Dlya linejnogo perebora (dazhe v otsortirovannom massive) poisk stroki zzzzzzzzzzcx potrebuet zzzzzzzzzza | 11 sravnenij, otkaz zzzzzzzzzzb | 11 sravnenij, otkaz zzzzzzzzzzbx | 12 sravnenij, otkaz zzzzzzzzzzc | 11 sravnenij, otkaz zzzzzzzzzzcx V 12 sravnenij, uspeh Vsego: 57 shagov. Dlya poiska v dereve: __z__z__z__z__z__z__z__z__z__z__a__\0 |_b__\0 | |_x__\0