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