Home | History | Annotate | Line # | Download | only in warp
term.c revision 1.4
      1 /* Header: term.c,v 7.0.1.2 86/12/12 17:04:09 lwall Exp */
      2 
      3 /* Log:	term.c,v
      4  * Revision 7.0.1.2  86/12/12  17:04:09  lwall
      5  * Baseline for net release.
      6  *
      7  * Revision 7.0.1.1  86/10/16  10:53:20  lwall
      8  * Added Damage.  Fixed random bugs.
      9  *
     10  * Revision 7.0  86/10/08  15:14:02  lwall
     11  * Split into separate files.  Added amoebas and pirates.
     12  *
     13  */
     14 
     15 #include "EXTERN.h"
     16 #include "warp.h"
     17 #include "bang.h"
     18 #include "intrp.h"
     19 #include "object.h"
     20 #include "play.h"
     21 #include "score.h"
     22 #include "sig.h"
     23 #include "us.h"
     24 #include "util.h"
     25 #include "weapon.h"
     26 #include "INTERN.h"
     27 #include "term.h"
     28 
     29 int typeahead = false;
     30 
     31 char tcarea[TCSIZE];	/* area for "compiled" termcap strings */
     32 
     33 /* guarantee capability pointer != NULL */
     34 /* (I believe terminfo will ignore the &tmpaddr argument.) */
     35 
     36 #define Tgetstr(key) ((tstr = tgetstr(key,&tmpaddr)) ? tstr : nullstr)
     37 
     38 #ifdef PUSHBACK
     39 struct keymap {
     40     char km_type[128];
     41     union km_union {
     42 	struct keymap *km_km;
     43 	char *km_str;
     44     } km_ptr[128];
     45 };
     46 
     47 #define KM_NOTHIN 0
     48 #define KM_STRING 1
     49 #define KM_KEYMAP 2
     50 #define KM_BOGUS 3
     51 
     52 #define KM_TMASK 3
     53 #define KM_GSHIFT 4
     54 #define KM_GMASK 7
     55 
     56 typedef struct keymap KEYMAP;
     57 
     58 KEYMAP *topmap INIT(NULL);
     59 
     60 void mac_init(char *);
     61 static KEYMAP *newkeymap(void);
     62 void pushstring(char *);
     63 #endif
     64 
     65 /* terminal initialization */
     66 
     67 void
     68 term_init(void)
     69 {
     70     savetty();				/* remember current tty state */
     71 
     72 #ifdef TERMIO
     73     ospeed = _tty.c_cflag & CBAUD;	/* for tputs() */
     74     ERASECH = _tty.c_cc[VERASE];	/* for finish_command() */
     75     KILLCH = _tty.c_cc[VKILL];		/* for finish_command() */
     76 #else
     77     ospeed = _tty.sg_ospeed;		/* for tputs() */
     78     ERASECH = _tty.sg_erase;		/* for finish_command() */
     79     KILLCH = _tty.sg_kill;		/* for finish_command() */
     80 #endif
     81 
     82     /* The following could be a table but I can't be sure that there isn't */
     83     /* some degree of sparsity out there in the world. */
     84 
     85     switch (ospeed) {			/* 1 second of padding */
     86 #ifdef BEXTA
     87         case BEXTA:  just_a_sec = 1920; break;
     88 #else
     89 #ifdef B19200
     90         case B19200: just_a_sec = 1920; break;
     91 #endif
     92 #endif
     93         case B9600:  just_a_sec =  960; break;
     94         case B4800:  just_a_sec =  480; break;
     95         case B2400:  just_a_sec =  240; break;
     96         case B1800:  just_a_sec =  180; break;
     97         case B1200:  just_a_sec =  120; break;
     98         case B600:   just_a_sec =   60; break;
     99 	case B300:   just_a_sec =   30; break;
    100 	/* do I really have to type the rest of this??? */
    101         case B200:   just_a_sec =   20; break;
    102         case B150:   just_a_sec =   15; break;
    103         case B134:   just_a_sec =   13; break;
    104         case B110:   just_a_sec =   11; break;
    105         case B75:    just_a_sec =    8; break;
    106         case B50:    just_a_sec =    5; break;
    107         default:     just_a_sec =  960; break;
    108 					/* if we are running detached I */
    109     }					/*  don't want to know about it! */
    110 }
    111 
    112 /* set terminal characteristics */
    113 
    114 void
    115 term_set(char *tcbuf) /* temp area for "uncompiled" termcap entry */
    116 {
    117     char *tmpaddr;			/* must not be register */
    118     char *tstr;
    119     char *s;
    120     int retval;
    121 
    122 #ifdef PENDING
    123 #ifndef FIONREAD
    124 #ifndef RDCHK
    125     /* do no delay reads on something that always gets closed on exit */
    126 
    127     devtty = open("/dev/tty",0);
    128     if (devtty < 0) {
    129 	printf(cantopen,"/dev/tty");
    130 	finalize(1);
    131     }
    132     fcntl(devtty,F_SETFL,O_NDELAY);
    133 #endif
    134 #endif
    135 #endif
    136 
    137     /* get all that good termcap stuff */
    138 
    139     retval = tgetent(tcbuf,getenv("TERM"));	/* get termcap entry */
    140     if (retval < 1) {
    141 #ifdef VERBOSE
    142 	printf("No termcap %s found.\n", retval ? "file" : "entry");
    143 #else
    144 	fputs("Termcap botch\n",stdout);
    145 #endif
    146 	finalize(1);
    147     }
    148     tmpaddr = tcarea;			/* set up strange tgetstr pointer */
    149     s = Tgetstr("pc");			/* get pad character */
    150     PC = *s;				/* get it where tputs wants it */
    151     if (!tgetflag("bs")) {		/* is backspace not used? */
    152 	BC = Tgetstr("bc");		/* find out what is */
    153 	if (BC == nullstr) 		/* terminfo grok's 'bs' but not 'bc' */
    154 	    BC = Tgetstr("le");
    155     } else
    156 	BC = __UNCONST("\b");		/* make a backspace handy */
    157     UP = Tgetstr("up");			/* move up a line */
    158     ND = Tgetstr("nd");			/* non-destructive move cursor right */
    159     DO = Tgetstr("do");			/* move cursor down */
    160     if (!*DO)
    161 	DO = Tgetstr("nl");
    162     CL = Tgetstr("cl");			/* get clear string */
    163     CE = Tgetstr("ce");			/* clear to end of line string */
    164     CM = Tgetstr("cm");			/* cursor motion - PWP */
    165     HO = Tgetstr("ho");			/* home cursor if no CM - PWP */
    166     CD = Tgetstr("cd");			/* clear to end of display - PWP */
    167     SO = Tgetstr("so");			/* begin standout */
    168     SE = Tgetstr("se");			/* end standout */
    169     if ((SG = tgetnum("sg"))<0)
    170 	SG = 0;				/* blanks left by SG, SE */
    171     US = Tgetstr("us");			/* start underline */
    172     UE = Tgetstr("ue");			/* end underline */
    173     if ((UG = tgetnum("ug"))<0)
    174 	UG = 0;				/* blanks left by US, UE */
    175     if (*US)
    176 	UC = nullstr;			/* UC must not be NULL */
    177     else
    178 	UC = Tgetstr("uc");		/* underline a character */
    179     if (!*US && !*UC) {			/* no underline mode? */
    180 	US = SO;			/* substitute standout mode */
    181 	UE = SE;
    182 	UG = SG;
    183     }
    184     LINES = tgetnum("li");		/* lines per page */
    185     COLS = tgetnum("co");		/* columns on page */
    186     AM = tgetflag("am");		/* terminal wraps automatically? */
    187     XN = tgetflag("xn");		/* then eats next newline? */
    188     VB = Tgetstr("vb");
    189     if (!*VB)
    190 	VB = __UNCONST("\007");
    191     CR = Tgetstr("cr");
    192     if (!*CR) {
    193 	if (tgetflag("nc") && *UP) {
    194 	    size_t l = strlen(UP) + 2;
    195 	    CR = safemalloc(l);
    196 	    snprintf(CR, l, "%s\r",UP);
    197 	}
    198 	else
    199 	    CR = __UNCONST("\r");
    200     }
    201     if (LINES <= 0)
    202 	LINES = 24;
    203     if (COLS <= 0)
    204 	COLS = 80;
    205 
    206     BCsize = comp_tc(bsptr,BC,1);
    207     BC = bsptr;
    208 
    209     if (!*ND)				/* not defined? */
    210 	NDsize = 1000;			/* force cursor addressing */
    211     else {
    212 	NDsize = comp_tc(cmbuffer,ND,1);
    213 	myND = malloc((unsigned)NDsize);
    214 	movc3(NDsize,cmbuffer,myND);
    215 	if (debugging) {
    216 	    int scr;
    217 
    218 	    printf("ND");
    219 	    for (scr=0; scr<NDsize; scr++)
    220 		printf(" %d",myND[scr]);
    221 	    printf("\n");
    222 	}
    223     }
    224 
    225     if (!*UP)				/* not defined? */
    226 	UPsize = 1000;			/* force cursor addressing */
    227     else {
    228 	UPsize = comp_tc(cmbuffer,UP,1);
    229 	myUP = malloc((unsigned)UPsize);
    230 	movc3(UPsize,cmbuffer,myUP);
    231 	if (debugging) {
    232 	    int scr;
    233 
    234 	    printf("UP");
    235 	    for (scr=0; scr<UPsize; scr++)
    236 		printf(" %d",myUP[scr]);
    237 	    printf("\n");
    238 	}
    239     }
    240 
    241     if (!*DO) {				/* not defined? */
    242 	myDO = DO = __UNCONST("\n");		/* assume a newline */
    243 	DOsize = 1;
    244     }
    245     else {
    246 	DOsize = comp_tc(cmbuffer,DO,1);
    247 	myDO = malloc((unsigned)DOsize);
    248 	movc3(DOsize,cmbuffer,myDO);
    249 	if (debugging) {
    250 	    int scr;
    251 
    252 	    printf("DO");
    253 	    for (scr=0; scr<DOsize; scr++)
    254 		printf(" %d",myDO[scr]);
    255 	    printf("\n");
    256 	}
    257     }
    258     if (debugging)
    259 	fgets(cmbuffer,(sizeof cmbuffer),stdin);
    260 
    261     CMsize = comp_tc(cmbuffer,tgoto(CM,20,20),0);
    262     if (PC != '\0') {
    263 	char *p;
    264 
    265 	for (p=filler+(sizeof filler)-1;!*p;--p)
    266 	    *p = PC;
    267     }
    268     charsperhalfsec = ospeed >= B9600 ? 480 :
    269 		      ospeed == B4800 ? 240 :
    270 		      ospeed == B2400 ? 120 :
    271 		      ospeed == B1200 ? 60 :
    272 		      ospeed == B600 ? 30 :
    273 	      /* speed is 300 (?) */   15;
    274 
    275     gfillen = ospeed >= B9600 ? (int /*XXX: speed_t*/)(sizeof filler) :
    276 	      ospeed == B4800 ? 13 :
    277 	      ospeed == B2400 ? 7 :
    278 	      ospeed == B1200 ? 4 :
    279 				(1+BCsize);
    280     if (ospeed < B2400)
    281 	lowspeed = true;
    282 
    283     strcpy(term,ttyname(2));
    284 
    285     if (!*CM || !BCsize)
    286 	no_can_do("dumb");
    287     if (!scorespec && (LINES < 24 || COLS < 80))
    288 	no_can_do("puny");
    289 
    290     crmode();
    291     raw();
    292     noecho();				/* turn off echo */
    293     nonl();
    294 
    295 #ifdef PUSHBACK
    296     mac_init(tcbuf);
    297 #endif
    298 }
    299 
    300 #ifdef PUSHBACK
    301 void
    302 mac_init(char *tcbuf)
    303 {
    304     char tmpbuf[1024];
    305 
    306     tmpfp = fopen(filexp(getval("WARPMACRO",WARPMACRO)),"r");
    307     if (tmpfp != NULL) {
    308 	while (fgets(tcbuf,1024,tmpfp) != NULL) {
    309 	    mac_line(tcbuf,tmpbuf,(sizeof tmpbuf));
    310 	}
    311 	fclose(tmpfp);
    312     }
    313 }
    314 
    315 void
    316 mac_line(char *line, char *tmpbuf, size_t tbsize)
    317 {
    318     char *s;
    319     char *m;
    320     KEYMAP *curmap;
    321     int ch;
    322     int garbage = 0;
    323     static const char override[] = "\r\nkeymap overrides string\r\n";
    324 
    325     if (topmap == NULL)
    326 	topmap = newkeymap();
    327     if (*line == '#' || *line == '\n')
    328 	return;
    329     if (line[ch = strlen(line)-1] == '\n')
    330 	line[ch] = '\0';
    331     m = dointerp(tmpbuf,tbsize,line," \t");
    332     if (!*m)
    333 	return;
    334     while (*m == ' ' || *m == '\t') m++;
    335     for (s=tmpbuf,curmap=topmap; *s; s++) {
    336 	ch = *s & 0177;
    337 	if (s[1] == '+' && isdigit((unsigned char)s[2])) {
    338 	    s += 2;
    339 	    garbage = (*s & KM_GMASK) << KM_GSHIFT;
    340 	}
    341 	else
    342 	    garbage = 0;
    343 	if (s[1]) {
    344 	    if ((curmap->km_type[ch] & KM_TMASK) == KM_STRING) {
    345 		puts(override);
    346 		free(curmap->km_ptr[ch].km_str);
    347 		curmap->km_ptr[ch].km_str = NULL;
    348 	    }
    349 	    curmap->km_type[ch] = KM_KEYMAP + garbage;
    350 	    if (curmap->km_ptr[ch].km_km == NULL)
    351 		curmap->km_ptr[ch].km_km = newkeymap();
    352 	    curmap = curmap->km_ptr[ch].km_km;
    353 	}
    354 	else {
    355 	    if ((curmap->km_type[ch] & KM_TMASK) == KM_KEYMAP)
    356 		puts(override);
    357 	    else {
    358 		curmap->km_type[ch] = KM_STRING + garbage;
    359 		curmap->km_ptr[ch].km_str = savestr(m);
    360 	    }
    361 	}
    362     }
    363 }
    364 
    365 static KEYMAP*
    366 newkeymap(void)
    367 {
    368     int i;
    369     KEYMAP *map;
    370 
    371 #ifndef lint
    372     map = (KEYMAP*)safemalloc(sizeof(KEYMAP));
    373 #else
    374     map = Null(KEYMAP*);
    375 #endif /* lint */
    376     for (i=127; i>=0; --i) {
    377 	map->km_ptr[i].km_km = NULL;
    378 	map->km_type[i] = KM_NOTHIN;
    379     }
    380     return map;
    381 }
    382 
    383 #endif
    384 
    385 /* print out a file, stopping at form feeds */
    386 
    387 void
    388 page(const char *filename, size_t num)
    389 {
    390     int linenum = 1;
    391 
    392     tmpfp = fopen(filename,"r");
    393     if (tmpfp != NULL) {
    394 	while (fgets(spbuf,(sizeof spbuf),tmpfp) != NULL) {
    395 	    if (*spbuf == '\f') {
    396 		printf("[Type anything to continue] ");
    397 		fflush(stdout);
    398 		getcmd(spbuf);
    399 		printf("\r\n");
    400 		if (*spbuf == INTRCH)
    401 		    finalize(0);
    402 		if (*spbuf == 'q' || *spbuf == 'Q')
    403 		    break;
    404 	    }
    405 	    else {
    406 		if (num)
    407 		    printf("%3d   %s\r",linenum++,spbuf);
    408 		else
    409 		    printf("%s\r",spbuf);
    410 	    }
    411 	}
    412 	fclose(tmpfp);
    413     }
    414 }
    415 
    416 void
    417 move(int y, int x, int chadd)
    418 {
    419     int ydist;
    420     int xdist;
    421     int i;
    422     char *s;
    423 
    424     ydist = y - real_y;
    425     xdist = x - real_x;
    426     i = ydist * (ydist < 0 ? -UPsize : DOsize) +
    427         xdist * (xdist < 0 ? -BCsize : NDsize);
    428     beg_qwrite();
    429     if (i <= CMsize) {
    430 	if (ydist < 0)
    431 	    for (; ydist; ydist++)
    432 		for (i=UPsize,s=myUP; i; i--)
    433 		    qaddch(*s++);
    434 	else
    435 	    for (; ydist; ydist--)
    436 		for (i=DOsize,s=myDO; i; i--)
    437 		    qaddch(*s++);
    438 	if (xdist < 0)
    439 	    for (; xdist; xdist++)
    440 		for (i=BCsize,s=BC; i; i--)
    441 		    qaddch(*s++);
    442 	else
    443 	    for (; xdist; xdist--)
    444 		for (i=NDsize,s=myND; i; i--)
    445 		    qaddch(*s++);
    446     }
    447     else {
    448 	tputs(tgoto(CM,x,y),0,cmstore);
    449     }
    450     real_y = y;
    451     real_x = x;
    452     if (chadd) {
    453 	qaddch(chadd);
    454     }
    455     if (maxcmstring != cmbuffer)
    456 	end_qwrite();
    457 }
    458 
    459 void
    460 do_tc(const char *s, int l)
    461 {
    462     beg_qwrite();
    463     tputs(s,l,cmstore);
    464     end_qwrite();
    465 }
    466 
    467 int
    468 comp_tc(char *dest, const char *s, int l)
    469 {
    470     maxcmstring = dest;
    471     tputs(s,l,cmstore);
    472     return(maxcmstring-dest);
    473 }
    474 
    475 void
    476 helper(void)
    477 {
    478     clear();
    479     mvaddstr(0,4,"h or 4          left");
    480     mvaddstr(1,4,"j or 2          down                Use with SHIFT to fire torpedoes.");
    481     mvaddstr(2,4,"k or 8          up                  Use with CTRL or FUNCT to fire");
    482     mvaddstr(3,4,"l or 6          right                   phasers or turbolasers.");
    483     mvaddstr(4,4,"b or 1          down and left       Use preceded by 'a' or 'r' for");
    484     mvaddstr(5,4,"n or 3          down and right          attractors or repulsors.");
    485     mvaddstr(6,4,"y or 7          up and left         Use normally for E or B motion.");
    486     mvaddstr(7,4,"u or 9          up and right");
    487     mvaddstr(8,4,"");
    488     mvaddstr(9,4,"del or %        fire photon torpedoes in every (reasonable) direction.");
    489     mvaddstr(10,4,"s               stop all torpedoes.");
    490     mvaddstr(11,4,"S or 0          stop the Enterprise when in warp mode.");
    491     mvaddstr(12,4,"d/D             destruct all torpedoes/current vessel.");
    492     mvaddstr(13,4,"i/w             switch to Enterprise & put into impulse/warp mode.");
    493     mvaddstr(14,4,"c/v             switch to Enterprise & make cloaked/visible.");
    494     mvaddstr(15,4,"p               switch to Base.");
    495     mvaddstr(16,4,"o               toggle to other vessel (from E to B, or vice versa.)");
    496     mvaddstr(17,4,"z               zap (suppress) blasts near Enterprise next cycle");
    497     mvaddstr(18,4,"");
    498     mvaddstr(19,4,"^R      refresh the screen.              ^Z      suspend the game.");
    499     mvaddstr(20,4,"q       exit this round (if you haven't typed q within 10 cycles).");
    500     mvaddstr(21,4,"Q       exit this game.");
    501     mvaddstr(22,4,"");
    502     mvaddstr(23,4,"                   [Hit space to continue]");
    503     fflush(stdout);
    504     do {
    505 	getcmd(spbuf);
    506     } while (*spbuf != ' ');
    507     rewrite();
    508 
    509 }
    510 
    511 void
    512 rewrite(void)
    513 {
    514     int x;
    515     int y;
    516     OBJECT *obj;
    517 
    518     clear();
    519     for (y=0; y<YSIZE; y++) {
    520 	for (x=0; x<XSIZE; x++) {
    521 	    if (numamoebas && amb[y][x] != ' ')
    522 		mvaddc(y+1,x*2,amb[y][x]);
    523 	    if ((obj = occupant[y][x]) != NULL) {
    524 		if (obj->image != ' ')
    525 		    mvaddc(y+1,x*2,obj->image);
    526 	    }
    527 	}
    528     }
    529     snprintf(spbuf, sizeof(spbuf),
    530      "%-4s E: %4d %2d B: %5d %3d Enemies: %-3d Stars: %-3d Stardate%5d.%1d %9ld",
    531 	"   ", 0, 0, 0, 0, 0, 0, timer/10+smarts*100, timer%10, 0L);
    532     mvaddstr(0,0,spbuf);
    533     oldeenergy = oldbenergy = oldcurscore =
    534     oldstatus = oldetorp = oldbtorp = oldstrs = oldenemies = -1;
    535 					/* force everything to fill in */
    536     if (damage)
    537 	olddamage = 0;
    538     if (!ent)
    539 	etorp = 0;
    540     if (!base)
    541 	btorp = 0;
    542     display_status();
    543 }
    544 
    545 int
    546 cmstore(int ch)
    547 {
    548     *maxcmstring++ = ch;
    549     return 0;
    550 }
    551 
    552 /* discard any characters typed ahead */
    553 
    554 void
    555 eat_typeahead(void)
    556 {
    557 #ifdef PUSHBACK
    558     if (!typeahead && nextin==nextout)	/* cancel only keyboard stuff */
    559 #else
    560     if (!typeahead)
    561 #endif
    562     {
    563 #ifdef PENDING
    564 	while (input_pending())
    565 	    read_tty(buf,sizeof(buf));
    566 #else /* this is probably v7, with no rdchk() */
    567 	ioctl(_tty_ch,TIOCSETP,&_tty);
    568 #endif
    569     }
    570 }
    571 
    572 void
    573 settle_down(void)
    574 {
    575     dingaling();
    576     fflush(stdout);
    577     sleep(1);
    578 #ifdef PUSHBACK
    579     nextout = nextin;			/* empty circlebuf */
    580 #endif
    581     eat_typeahead();
    582 }
    583 
    584 #ifdef PUSHBACK
    585 /* read a character from the terminal, with multi-character pushback */
    586 
    587 int
    588 read_tty(char *addr, ssize_t size)	/* ignored for now */
    589 {
    590     if (nextout != nextin) {
    591 	*addr = circlebuf[nextout++];
    592 	nextout %= PUSHSIZE;
    593 	return 1;
    594     }
    595     else {
    596 	size = read(0,addr,1);
    597 	if (size < 0)
    598 	    sig_catcher(SIGHUP);
    599 	if (metakey) {
    600 	    if (*addr & 0200) {
    601 		pushchar(*addr & 0177);
    602 		*addr = '\001';
    603 	    }
    604 	}
    605 	else
    606 	    *addr &= 0177;
    607 	return 1;
    608     }
    609 }
    610 
    611 #ifdef PENDING
    612 #ifndef FIONREAD
    613 #ifndef RDCHK
    614 int
    615 circfill()
    616 {
    617     int howmany;
    618     int i;
    619 
    620     assert (nextin == nextout);
    621     howmany = read(devtty,circlebuf+nextin,metakey?1:PUSHSIZE-nextin);
    622     if (howmany > 0) {
    623 	if (metakey) {
    624 	    if (circlebuf[nextin] & 0200) {
    625 		circlebuf[nextin] &= 0177;
    626 		pushchar('\001');
    627 	    }
    628 	}
    629 	else
    630 	    for (i = howmany+nextin-1; i >= nextin; i--)
    631 		circlebuf[i] &= 0177;
    632 	nextin += howmany;
    633 	nextin %= PUSHSIZE;	/* may end up 1 if metakey */
    634     }
    635     return howmany;
    636 }
    637 #endif /* RDCHK */
    638 #endif /* FIONREAD */
    639 #endif /* PENDING */
    640 
    641 void
    642 pushchar(int ch)
    643 {
    644     nextout--;
    645     if (nextout < 0)
    646 	nextout = PUSHSIZE - 1;
    647     if (nextout == nextin) {
    648 	fputs("\r\npushback buffer overflow\r\n",stdout);
    649 	sig_catcher(0);
    650     }
    651     circlebuf[nextout] = ch;
    652 }
    653 
    654 #else /* PUSHBACK */
    655 #ifndef read_tty
    656 /* read a character from the terminal, with hacks for O_NDELAY reads */
    657 
    658 int
    659 read_tty(addr,size)
    660 char *addr;
    661 int size;
    662 {
    663     if (is_input) {
    664 	*addr = pending_ch;
    665 	is_input = false;
    666 	return 1;
    667     }
    668     else {
    669 	size = read(0,addr,size);
    670 	if (size < 0)
    671 	    sig_catcher(SIGHUP);
    672 	if (metakey) {
    673 	    if (*addr & 0200) {
    674 		pending_ch = *addr & 0177;
    675 		is_input = true;
    676 		*addr = '\001';
    677 	    }
    678 	}
    679 	else
    680 	    *addr &= 0177;
    681 	return size;
    682     }
    683 }
    684 #endif /* read_tty */
    685 #endif /* PUSHBACK */
    686 
    687 int
    688 read_nd(char *buff, size_t siz)
    689 {
    690     if (!input_pending())
    691 	return 0;
    692 
    693     getcmd(buff);
    694     return 1;
    695 }
    696 
    697 /* get a character into a buffer */
    698 
    699 void
    700 getcmd(char *wbuf)
    701 {
    702 #ifdef PUSHBACK
    703     KEYMAP *curmap;
    704     int i;
    705     bool no_macros;
    706     int times = 0;			/* loop detector */
    707     char scrchar;
    708     unsigned char *whatbuf = (void *)wbuf;
    709 
    710 tryagain:
    711     curmap = topmap;
    712 /*    no_macros = (whatbuf != buf && nextin == nextout);  */
    713     no_macros = false;
    714 #endif
    715     for (;;) {
    716 	errno = 0;
    717 	if (read_tty(wbuf,1) < 0 && !errno)
    718 	    errno = EINTR;
    719 #ifdef read_tty
    720 	if (metakey) {
    721 	    if (*whatbuf & 0200) {
    722 		*what_buf &= 037;	/* punt and hope they don't notice */
    723 	    }
    724 	}
    725 	else
    726 	    *whatbuf &= 0177;
    727 #endif /* read_tty */
    728 	if (errno && errno != EINTR) {
    729 	    perror(readerr);
    730 	    sig_catcher(0);
    731 	}
    732 #ifdef PUSHBACK
    733 	if (*whatbuf & 0200 || no_macros) {
    734 	    *whatbuf &= 0177;
    735 	    goto got_canonical;
    736 	}
    737 	if (curmap == NULL)
    738 	    goto got_canonical;
    739 	for (i = (curmap->km_type[*whatbuf] >> KM_GSHIFT) & KM_GMASK; i; --i){
    740 	    read_tty(&scrchar,1);
    741 	}
    742 	switch (curmap->km_type[*whatbuf] & KM_TMASK) {
    743 	case KM_NOTHIN:			/* no entry? */
    744 	    if (curmap == topmap)	/* unmapped canonical */
    745 		goto got_canonical;
    746 	    settle_down();
    747 	    goto tryagain;
    748 	case KM_KEYMAP:			/* another keymap? */
    749 	    curmap = curmap->km_ptr[*whatbuf].km_km;
    750 	    assert(curmap != NULL);
    751 	    break;
    752 	case KM_STRING:			/* a string? */
    753 	    pushstring(curmap->km_ptr[*whatbuf].km_str);
    754 	    if (++times > 20) {		/* loop? */
    755 		fputs("\r\nmacro loop?\r\n",stdout);
    756 		settle_down();
    757 	    }
    758 	    no_macros = false;
    759 	    goto tryagain;
    760 	}
    761 #else
    762 	*whatbuf &= 0177;
    763 	break;
    764 #endif
    765     }
    766 
    767 got_canonical:
    768 #ifndef TERMIO
    769     if (*whatbuf == '\r')
    770 	*whatbuf = '\n';
    771 #endif
    772     if (wbuf == buf)
    773 	whatbuf[1] = FINISHCMD;		/* tell finish_command to work */
    774 }
    775 
    776 #ifdef PUSHBACK
    777 void
    778 pushstring(char *str)
    779 {
    780     int i;
    781     char tmpbuf[PUSHSIZE];
    782     char *s = tmpbuf;
    783 
    784     assert(str != NULL);
    785     interp(s,PUSHSIZE,str);
    786     for (i = strlen(s)-1; i >= 0; --i) {
    787 	s[i] ^= 0200;
    788 	pushchar(s[i]);
    789     }
    790 }
    791 #endif
    792