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