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