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