1 /*
2 * Copyright (c) 1980, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * %sccs.include.redist.c%
6 */
7
8 #ifndef lint
9 static char sccsid[] = "@(#)move.c 8.1 (Berkeley) 07/19/93";
10 #endif /* not lint */
11
12 /*************************************************************************
13 *
14 * MOVE LIBRARY
15 *
16 * This set of subroutines moves a cursor to a predefined
17 * location, independent of the terminal type. If the
18 * terminal has an addressable cursor, it uses it. If
19 * not, it optimizes for tabs (currently) even if you don't
20 * have them.
21 *
22 * At all times the current address of the cursor must be maintained,
23 * and that is available as structure cursor.
24 *
25 * The following calls are allowed:
26 * move(sp) move to point sp.
27 * up() move up one line.
28 * down() move down one line.
29 * bs() move left one space (except column 0).
30 * nd() move right one space(no write).
31 * clear() clear screen.
32 * home() home.
33 * ll() move to lower left corner of screen.
34 * cr() carriage return (no line feed).
35 * pr() just like standard printf, but keeps track
36 * of cursor position. (Uses pstring).
37 * apr() same as printf, but first argument is &point.
38 * (Uses pstring).
39 * pstring(s) output the string of printing characters.
40 * However, '\r' is interpreted to mean return
41 * to column of origination AND do linefeed.
42 * '\n' causes <cr><lf>.
43 * putpad(str) calls tputs to output character with proper
44 * padding.
45 * outch() the output routine for a character used by
46 * tputs. It just calls putchar.
47 * pch(ch) output character to screen and update
48 * cursor address (must be a standard
49 * printing character). WILL SCROLL.
50 * pchar(ps,ch) prints one character if it is on the
51 * screen at the specified location;
52 * otherwise, dumps it.(no wrap-around).
53 *
54 * getcap() initializes strings for later calls.
55 * cap(string) outputs the string designated in the termcap
56 * data base. (Should not move the cursor.)
57 * done() returns the terminal to intial state and exits.
58 *
59 * point(&p,x,y) return point set to x,y.
60 *
61 * baudrate(x) returns the baudrate of the terminal.
62 * delay(t) causes an approximately constant delay
63 * independent of baudrate.
64 * Duration is ~ t/20 seconds.
65 *
66 ******************************************************************************/
67
68 #if __STDC__
69 #include <stdarg.h>
70 #else
71 #include <varargs.h>
72 #endif
73 #include "snake.h"
74
75 int CMlength;
76 int NDlength;
77 int BSlength;
78 int delaystr[10];
79 short ospeed;
80
81 static char str[80];
82
83 move(sp)
84 struct point *sp;
85 {
86 int distance;
87 int tabcol,ct;
88 struct point z;
89
90 if (sp->line <0 || sp->col <0 || sp->col > COLUMNS){
91 pr("move to [%d,%d]?",sp->line,sp->col);
92 return;
93 }
94 if (sp->line >= LINES){
95 move(point(&z,sp->col,LINES-1));
96 while(sp->line-- >= LINES)putchar('\n');
97 return;
98 }
99
100 if (CM != 0) {
101 char *cmstr = tgoto(CM, sp->col, sp->line);
102
103 CMlength = strlen(cmstr);
104 if(cursor.line == sp->line){
105 distance = sp->col - cursor.col;
106 if(distance == 0)return; /* Already there! */
107 if(distance > 0){ /* Moving to the right */
108 if(distance*NDlength < CMlength){
109 right(sp);
110 return;
111 }
112 if(TA){
113 ct=sp->col&7;
114 tabcol=(cursor.col|7)+1;
115 do{
116 ct++;
117 tabcol=(tabcol|7)+1;
118 }
119 while(tabcol<sp->col);
120 if(ct<CMlength){
121 right(sp);
122 return;
123 }
124 }
125 } else { /* Moving to the left */
126 if (-distance*BSlength < CMlength){
127 gto(sp);
128 return;
129 }
130 }
131 if(sp->col < CMlength){
132 cr();
133 right(sp);
134 return;
135 }
136 /* No more optimizations on same row. */
137 }
138 distance = sp->col - cursor.col;
139 distance = distance > 0 ?
140 distance*NDlength : -distance * BSlength;
141 if (distance < 0)
142 pr("ERROR: distance is negative: %d",distance);
143 distance += abs(sp->line - cursor.line);
144 if(distance >= CMlength){
145 putpad(cmstr);
146 cursor.line = sp->line;
147 cursor.col = sp->col;
148 return;
149 }
150 }
151
152 /*
153 * If we get here we have a terminal that can't cursor
154 * address but has local motions or one which can cursor
155 * address but can get there quicker with local motions.
156 */
157 gto(sp);
158 }
159 gto(sp)
160 struct point *sp;
161 {
162
163 int distance,f,tfield,j;
164
165 if (cursor.line > LINES || cursor.line <0 ||
166 cursor.col <0 || cursor.col > COLUMNS)
167 pr("ERROR: cursor is at %d,%d\n",
168 cursor.line,cursor.col);
169 if (sp->line > LINES || sp->line <0 ||
170 sp->col <0 || sp->col > COLUMNS)
171 pr("ERROR: target is %d,%d\n",sp->line,sp->col);
172 tfield = (sp->col) >> 3;
173 if (sp->line == cursor.line){
174 if (sp->col > cursor.col)right(sp);
175 else{
176 distance = (cursor.col -sp->col)*BSlength;
177 if (((TA) &&
178 (distance > tfield+((sp->col)&7)*NDlength)
179 ) ||
180 (((cursor.col)*NDlength) < distance)
181 ){
182 cr();
183 right(sp);
184 }
185 else{
186 while(cursor.col > sp->col) bs();
187 }
188 }
189 return;
190 }
191 /*must change row */
192 if (cursor.col - sp->col > (cursor.col >> 3)){
193 if (cursor.col == 0)f = 0;
194 else f = -1;
195 }
196 else f = cursor.col >> 3;
197 if (((sp->line << 1) + 1 < cursor.line - f) && (HO != 0)){
198 /*
199 * home quicker than rlf:
200 * (sp->line + f > cursor.line - sp->line)
201 */
202 putpad(HO);
203 cursor.col = cursor.line = 0;
204 gto(sp);
205 return;
206 }
207 if (((sp->line << 1) > cursor.line + LINES+1 + f) && (LL != 0)){
208 /* home,rlf quicker than lf
209 * (LINES+1 - sp->line + f < sp->line - cursor.line)
210 */
211 if (cursor.line > f + 1){
212 /* is home faster than wraparound lf?
213 * (cursor.line + 20 - sp->line > 21 - sp->line + f)
214 */
215 ll();
216 gto(sp);
217 return;
218 }
219 }
220 if ((LL != 0) && (sp->line > cursor.line + (LINES >> 1) - 1))
221 cursor.line += LINES;
222 while(sp->line > cursor.line)down();
223 while(sp->line < cursor.line)up();
224 gto(sp); /*can recurse since cursor.line = sp->line */
225 }
226
227 right(sp)
228 struct point *sp;
229 {
230 int field,tfield;
231 int tabcol,strlength;
232
233 if (sp->col < cursor.col)
234 pr("ERROR:right() can't move left\n");
235 if(TA){ /* If No Tabs: can't send tabs because ttydrive
236 * loses count with control characters.
237 */
238 field = cursor.col >> 3;
239 /*
240 * This code is useful for a terminal which wraps around on backspaces.
241 * (Mine does.) Unfortunately, this is not specified in termcap, and
242 * most terminals don't work that way. (Of course, most terminals
243 * have addressible cursors, too).
244 */
245 if (BW && (CM == 0) &&
246 ((sp->col << 1) - field > (COLUMNS - 8) << 1 )
247 ){
248 if (cursor.line == 0){
249 outch('\n');
250 }
251 outch('\r');
252 cursor.col = COLUMNS + 1;
253 while(cursor.col > sp->col)bs();
254 if (cursor.line != 0) outch('\n');
255 return;
256 }
257
258 tfield = sp->col >> 3;
259
260 while (field < tfield){
261 putpad(TA);
262 cursor.col = ++field << 3;
263 }
264 tabcol = (cursor.col|7) + 1;
265 strlength = (tabcol - sp->col)*BSlength + 1;
266 /* length of sequence to overshoot */
267 if (((sp->col - cursor.col)*NDlength > strlength) &&
268 (tabcol < COLUMNS)
269 ){
270 /*
271 * Tab past and backup
272 */
273 putpad(TA);
274 cursor.col = (cursor.col | 7) + 1;
275 while(cursor.col > sp->col)bs();
276 }
277 }
278 while (sp->col > cursor.col){
279 nd();
280 }
281 }
282
cr()283 cr(){
284 outch('\r');
285 cursor.col = 0;
286 }
287
clear()288 clear(){
289 int i;
290
291 if (CL){
292 putpad(CL);
293 cursor.col=cursor.line=0;
294 } else {
295 for(i=0; i<LINES; i++) {
296 putchar('\n');
297 }
298 cursor.line = LINES - 1;
299 home();
300 }
301 }
302
home()303 home(){
304 struct point z;
305
306 if(HO != 0){
307 putpad(HO);
308 cursor.col = cursor.line = 0;
309 return;
310 }
311 z.col = z.line = 0;
312 move(&z);
313 }
314
ll()315 ll(){
316 int j,l;
317 struct point z;
318
319 l = lcnt + 2;
320 if(LL != NULL && LINES==l){
321 putpad(LL);
322 cursor.line = LINES-1;
323 cursor.col = 0;
324 return;
325 }
326 z.col = 0;
327 z.line = l-1;
328 move(&z);
329 }
330
up()331 up(){
332 putpad(UP);
333 cursor.line--;
334 }
335
down()336 down(){
337 putpad(DO);
338 cursor.line++;
339 if (cursor.line >= LINES)cursor.line=LINES-1;
340 }
bs()341 bs(){
342 if (cursor.col > 0){
343 putpad(BS);
344 cursor.col--;
345 }
346 }
347
nd()348 nd(){
349 putpad(ND);
350 cursor.col++;
351 if (cursor.col == COLUMNS+1){
352 cursor.line++;
353 cursor.col = 0;
354 if (cursor.line >= LINES)cursor.line=LINES-1;
355 }
356 }
357
pch(c)358 pch(c)
359 {
360 outch(c);
361 if(++cursor.col >= COLUMNS && AM) {
362 cursor.col = 0;
363 ++cursor.line;
364 }
365 }
366
367 void
368 #if __STDC__
apr(struct point * ps,const char * fmt,...)369 apr(struct point *ps, const char *fmt, ...)
370 #else
371 apr(ps, fmt, va_alist)
372 struct point *ps;
373 char *fmt;
374 va_dcl
375 #endif
376 {
377 struct point p;
378 va_list ap;
379
380 p.line = ps->line+1; p.col = ps->col+1;
381 move(&p);
382 #if __STDC__
383 va_start(ap, fmt);
384 #else
385 va_start(ap);
386 #endif
387 (void)vsprintf(str, fmt, ap);
388 va_end(ap);
389 pstring(str);
390 }
391
392 void
393 #if __STDC__
pr(const char * fmt,...)394 pr(const char *fmt, ...)
395 #else
396 pr(fmt, va_alist)
397 char *fmt;
398 va_dcl
399 #endif
400 {
401 va_list ap;
402
403 #if __STDC__
404 va_start(ap, fmt);
405 #else
406 va_start(ap);
407 #endif
408 (void)vsprintf(str, fmt, ap);
409 va_end(ap);
410 pstring(str);
411 }
412
pstring(s)413 pstring(s)
414 char *s;{
415 struct point z;
416 int stcol;
417
418 stcol = cursor.col;
419 while (s[0] != '\0'){
420 switch (s[0]){
421 case '\n':
422 move(point(&z,0,cursor.line+1));
423 break;
424 case '\r':
425 move(point(&z,stcol,cursor.line+1));
426 break;
427 case '\t':
428 z.col = (((cursor.col + 8) >> 3) << 3);
429 z.line = cursor.line;
430 move(&z);
431 break;
432 case '\b':
433 bs();
434 break;
435 case CTRL('g'):
436 outch(CTRL('g'));
437 break;
438 default:
439 if (s[0] < ' ')break;
440 pch(s[0]);
441 }
442 s++;
443 }
444 }
445
446 pchar(ps,ch)
447 struct point *ps;
448 char ch;{
449 struct point p;
450 p.col = ps->col + 1; p.line = ps->line + 1;
451 if (
452 (p.col >= 0) &&
453 (p.line >= 0) &&
454 (
455 (
456 (p.line < LINES) &&
457 (p.col < COLUMNS)
458 ) ||
459 (
460 (p.col == COLUMNS) &&
461 (p.line < LINES-1)
462 )
463 )
464 ){
465 move(&p);
466 pch(ch);
467 }
468 }
469
470
outch(c)471 outch(c)
472 {
473 putchar(c);
474 }
475
putpad(str)476 putpad(str)
477 char *str;
478 {
479 if (str)
480 tputs(str, 1, outch);
481 }
baudrate()482 baudrate()
483 {
484
485 switch (orig.sg_ospeed){
486 case B300:
487 return(300);
488 case B1200:
489 return(1200);
490 case B4800:
491 return(4800);
492 case B9600:
493 return(9600);
494 default:
495 return(0);
496 }
497 }
delay(t)498 delay(t)
499 int t;
500 {
501 int k,j;
502
503 k = baudrate() * t / 300;
504 for(j=0;j<k;j++){
505 putchar(PC);
506 }
507 }
508
done()509 done()
510 {
511 cook();
512 exit(0);
513 }
514
cook()515 cook()
516 {
517 delay(1);
518 putpad(TE);
519 putpad(KE);
520 fflush(stdout);
521 stty(0, &orig);
522 #ifdef TIOCSLTC
523 ioctl(0, TIOCSLTC, &olttyc);
524 #endif
525 }
526
raw()527 raw()
528 {
529 stty(0, &new);
530 #ifdef TIOCSLTC
531 ioctl(0, TIOCSLTC, &nlttyc);
532 #endif
533 }
534
point(ps,x,y)535 struct point *point(ps,x,y)
536 struct point *ps;
537 int x,y;
538 {
539 ps->col=x;
540 ps->line=y;
541 return(ps);
542 }
543
544 char *ap;
545
getcap()546 getcap()
547 {
548 char *getenv();
549 char *term;
550 char *xPC;
551 struct point z;
552 void stop();
553 #ifdef TIOCGWINSZ
554 struct winsize win;
555 #endif
556
557 term = getenv("TERM");
558 if (term==0) {
559 fprintf(stderr, "No TERM in environment\n");
560 exit(1);
561 }
562
563 switch (tgetent(tbuf, term)) {
564 case -1:
565 fprintf(stderr, "Cannot open termcap file\n");
566 exit(2);
567 case 0:
568 fprintf(stderr, "%s: unknown terminal", term);
569 exit(3);
570 }
571
572 ap = tcapbuf;
573
574 #ifdef TIOCGWINSZ
575 if (ioctl(0, TIOCGWINSZ, (char *) &win) < 0 ||
576 (LINES = win.ws_row) == 0 || (COLUMNS = win.ws_col) == 0) {
577 #endif
578 LINES = tgetnum("li");
579 COLUMNS = tgetnum("co");
580 #ifdef TIOCGWINSZ
581 }
582 #endif
583 if (!lcnt)
584 lcnt = LINES - 2;
585 if (!ccnt)
586 ccnt = COLUMNS - 3;
587
588 AM = tgetflag("am");
589 BW = tgetflag("bw");
590
591 ND = tgetstr("nd", &ap);
592 UP = tgetstr("up", &ap);
593
594 DO = tgetstr("do", &ap);
595 if (DO == 0)
596 DO = "\n";
597
598 BS = tgetstr("bc", &ap);
599 if (BS == 0 && tgetflag("bs"))
600 BS = "\b";
601 if (BS)
602 xBC = *BS;
603
604 TA = tgetstr("ta", &ap);
605 if (TA == 0 && tgetflag("pt"))
606 TA = "\t";
607
608 HO = tgetstr("ho", &ap);
609 CL = tgetstr("cl", &ap);
610 CM = tgetstr("cm", &ap);
611 LL = tgetstr("ll", &ap);
612
613 KL = tgetstr("kl", &ap);
614 KR = tgetstr("kr", &ap);
615 KU = tgetstr("ku", &ap);
616 KD = tgetstr("kd", &ap);
617 Klength = strlen(KL);
618 /* NOTE: If KL, KR, KU, and KD are not
619 * all the same length, some problems
620 * may arise, since tests are made on
621 * all of them together.
622 */
623
624 TI = tgetstr("ti", &ap);
625 TE = tgetstr("te", &ap);
626 KS = tgetstr("ks", &ap);
627 KE = tgetstr("ke", &ap);
628
629 xPC = tgetstr("pc", &ap);
630 if (xPC)
631 PC = *xPC;
632
633 NDlength = strlen(ND);
634 BSlength = strlen(BS);
635 if ((CM == 0) &&
636 (HO == 0 | UP==0 || BS==0 || ND==0)) {
637 fprintf(stderr, "Terminal must have addressible ");
638 fprintf(stderr, "cursor or home + 4 local motions\n");
639 exit(5);
640 }
641 if (tgetflag("os")) {
642 fprintf(stderr, "Terminal must not overstrike\n");
643 exit(5);
644 }
645 if (LINES <= 0 || COLUMNS <= 0) {
646 fprintf(stderr, "Must know the screen size\n");
647 exit(5);
648 }
649
650 gtty(0, &orig);
651 new=orig;
652 new.sg_flags &= ~(ECHO|CRMOD|ALLDELAY|XTABS);
653 new.sg_flags |= CBREAK;
654 signal(SIGINT,stop);
655 ospeed = orig.sg_ospeed;
656 #ifdef TIOCGLTC
657 ioctl(0, TIOCGLTC, &olttyc);
658 nlttyc = olttyc;
659 nlttyc.t_suspc = '\377';
660 nlttyc.t_dsuspc = '\377';
661 #endif
662 raw();
663
664 if ((orig.sg_flags & XTABS) == XTABS) TA=0;
665 putpad(KS);
666 putpad(TI);
667 point(&cursor,0,LINES-1);
668 }
669