misc.c is goto-free.
[open-adventure.git] / misc.c
1 #include <unistd.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <sys/time.h>
6 #include "advent.h"
7 #include "funcs.h"
8 #include "database.h"
9
10 /* hack to ignore GCC Unused Result */
11 #define IGNORE(r) do{if (r){}}while(0)
12
13 #define PERCENT 63      /* partly hide the packed encoding */
14
15 /*  I/O routines (SPEAK, PSPEAK, RSPEAK, SETPRM, GETIN, YES) */
16
17 void SPEAK(vocab_t msg)
18 /*  Print the message which starts at LINES[N].  Precede it with a blank line
19  *  unless game.blklin is false. */
20 {
21     long blank, casemake, i, nxt, neg, nparms, param, prmtyp, state;
22
23     if (msg == 0)
24         return;
25     blank=game.blklin;
26     nparms=1;
27     do {
28         nxt=labs(LINES[msg])-1;
29         msg=msg+1;
30         LNLENG=0;
31         LNPOSN=1;
32         state=0;
33         for (i = msg; i <= nxt; i++) {
34             PUTTXT(LINES[i],state,2);
35         }
36         LNPOSN=0;
37         ++LNPOSN;
38
39         while (LNPOSN <= LNLENG) { 
40             if (INLINE[LNPOSN] != PERCENT) {
41                 ++LNPOSN;
42                 continue;
43             }
44             prmtyp = INLINE[LNPOSN+1];
45             /*  A "%"; the next character determine the type of
46              *  parameter: 1 (!) = suppress message completely, 29 (S) = NULL
47              *  If PARAM=1, else 'S' (optional plural ending), 33 (W) = word
48              *  (two 30-bit values) with trailing spaces suppressed, 22 (L) or
49              *  31 (U) = word but map to lower/upper case, 13 (C) = word in
50              *  lower case with first letter capitalised, 30 (T) = text ending
51              *  with a word of -1, 65-73 (1-9) = number using that many
52              *  characters, 12 (B) = variable number of blanks. */
53             if (prmtyp == 1)
54                 return;
55             if (prmtyp == 29) {
56                 SHFTXT(LNPOSN+2,-1);
57                 INLINE[LNPOSN] = 55;
58                 if (PARMS[nparms] == 1)
59                     SHFTXT(LNPOSN+1,-1);
60                 ++nparms;
61                 continue;
62             }
63             if (prmtyp == 30) {
64                 SHFTXT(LNPOSN+2,-2);
65                 state=0;
66                 casemake=2;
67
68                 while (PARMS[nparms] > 0) {
69                     if (PARMS[nparms+1] < 0)
70                         casemake=0;
71                     PUTTXT(PARMS[nparms],state,casemake);
72                     nparms=nparms+1;
73                 }
74                 ++nparms;
75                 continue;
76             }
77             if (prmtyp == 12) {
78                 prmtyp=PARMS[nparms];
79                 SHFTXT(LNPOSN+2,prmtyp-2);
80                 if (prmtyp != 0) {
81                     for (i=1; i<=prmtyp; i++) {
82                         INLINE[LNPOSN]=0;
83                         ++LNPOSN;
84                     }
85                 }
86                 ++nparms;
87                 continue;
88             }
89             if (prmtyp == 33 || prmtyp == 22 || prmtyp == 31 || prmtyp == 13) {
90                 SHFTXT(LNPOSN+2,-2);
91                 state = 0;
92                 casemake = -1;
93                 if (prmtyp == 31)
94                     casemake=1;
95                 if (prmtyp == 33)
96                     casemake=0;
97                 i = LNPOSN;
98                 PUTTXT(PARMS[nparms],state,casemake);
99                 PUTTXT(PARMS[nparms+1],state,casemake);
100                 if (prmtyp == 13 && INLINE[i] >= 37 && INLINE[i] <= 62)
101                     INLINE[i] -= 26;
102                 nparms += 2;
103                 continue;
104             }
105
106             prmtyp=prmtyp-64;
107             if (prmtyp < 1 || prmtyp > 9) {
108                 ++LNPOSN;
109                 continue;
110             }
111             SHFTXT(LNPOSN+2,prmtyp-2);
112             LNPOSN += prmtyp;
113             param=labs(PARMS[nparms]);
114             neg=0;
115             if (PARMS[nparms] < 0)
116                 neg=9;
117             for (i=1; i <= prmtyp; i++) {
118                 --LNPOSN;
119                 INLINE[LNPOSN]=MOD(param,10)+64;
120                 if (i != 1 && param == 0) {
121                     INLINE[LNPOSN]=neg;
122                     neg=0;
123                 }
124                 param=param/10;
125             }
126             LNPOSN += prmtyp;
127             ++nparms;
128             continue;
129         }
130
131         if (blank)
132             TYPE0();
133         blank=false;
134         TYPE();
135         msg = nxt + 1;
136     } while
137         (LINES[msg] >= 0);
138 }
139
140 void PSPEAK(vocab_t msg,int skip)
141 /*  Find the skip+1st message from msg and print it.  msg should be
142  *  the index of the inventory message for object.  (INVEN+N+1 message
143  *  is game.prop=N message). */
144 {
145     long i, m;
146
147     m=PTEXT[msg];
148     if (skip >= 0) {
149         for (i=0; i <=skip; i++) {
150             do {
151                 m=labs(LINES[m]);
152             } while
153                 (LINES[m] >= 0);
154         }
155     }
156     SPEAK(m);
157 }
158
159 void RSPEAK(vocab_t i)
160 /* Print the i-th "random" message (section 6 of database). */
161 {
162     if (i != 0)
163         SPEAK(RTEXT[i]);
164 }
165
166 void SETPRM(long first, long p1, long p2)
167 /*  Stores parameters into the PRMCOM parms array for use by speak.  P1 and P2
168  *  are stored into PARMS(first) and PARMS(first+1). */
169 {
170     if (first >= MAXPARMS)
171         BUG(29);
172     else {
173         PARMS[first] = p1;
174         PARMS[first+1] = p2;
175     }
176 }
177
178 bool fGETIN(FILE *input, 
179             long *pword1, long *pword1x, 
180             long *pword2, long *pword2x) 
181 /*  Get a command from the adventurer.  Snarf out the first word, pad it with
182  *  blanks, and return it in WORD1.  Chars 6 thru 10 are returned in WORD1X, in
183  *  case we need to print out the whole word in an error message.  Any number of
184  *  blanks may follow the word.  If a second word appears, it is returned in
185  *  WORD2 (chars 6 thru 10 in WORD2X), else WORD2 is -1. */
186 {
187     long junk;
188
189     for (;;) {
190         if (game.blklin)
191             TYPE0();
192         MAPLIN(input);
193         if (feof(input))
194             return false;
195         *pword1=GETTXT(true,true,true);
196         if (game.blklin && *pword1 < 0)
197             continue;
198         *pword1x=GETTXT(false,true,true);
199         do {    
200             junk=GETTXT(false,true,true);
201         } while 
202             (junk > 0);
203         *pword2=GETTXT(true,true,true);
204         *pword2x=GETTXT(false,true,true);
205         do {
206             junk=GETTXT(false,true,true);
207         } while 
208             (junk > 0);
209         if (GETTXT(true,true,true) <= 0)
210             return true;
211         RSPEAK(53);
212     }
213 }
214
215 long YES(FILE *input, vocab_t x, vocab_t y, vocab_t z)
216 /*  Print message X, wait for yes/no answer.  If yes, print Y and return true;
217  *  if no, print Z and return false. */
218 {
219     token_t reply, junk1, junk2, junk3;
220
221     for (;;) {
222         RSPEAK(x);
223         GETIN(input, reply,junk1,junk2,junk3);
224         if (reply == MAKEWD(250519) || reply == MAKEWD(25)) {
225             RSPEAK(y);
226             return true;
227         }
228         if (reply == MAKEWD(1415) || reply == MAKEWD(14)) {
229             RSPEAK(z);
230             return false;
231         }
232         RSPEAK(185);
233     }
234 }
235
236 /*  Line-parsing routines (GETTXT, MAKEWD, PUTTXT, SHFTXT, TYPE0) */
237
238 long GETTXT(bool skip, bool onewrd, bool upper)
239 /*  Take characters from an input line and pack them into 30-bit words.
240  *  Skip says to skip leading blanks.  ONEWRD says stop if we come to a
241  *  blank.  UPPER says to map all letters to uppercase.  If we reach the
242  *  end of the line, the word is filled up with blanks (which encode as 0's).
243  *  If we're already at end of line when TEXT is called, we return -1. */
244 {
245     long text;
246     static long splitting = -1;
247
248     if (LNPOSN != splitting)
249         splitting = -1;
250     text= -1;
251     while (true) {
252         if (LNPOSN > LNLENG)
253             return(text);
254         if ((!skip) || INLINE[LNPOSN] != 0)
255             break;
256         LNPOSN=LNPOSN+1;
257     }
258
259     text=0;
260     for (int I=1; I<=TOKLEN; I++) {
261         text=text*64;
262         if (LNPOSN > LNLENG || (onewrd && INLINE[LNPOSN] == 0))
263             continue;
264         char current=INLINE[LNPOSN];
265         if (current < PERCENT) {
266             splitting = -1;
267             if (upper && current >= 37)
268                 current=current-26;
269             text=text+current;
270             LNPOSN=LNPOSN+1;
271             continue;
272         }
273         if (splitting != LNPOSN) {
274             text=text+PERCENT;
275             splitting = LNPOSN;
276             continue;
277         }
278
279         text=text+current-PERCENT;
280         splitting = -1;
281         LNPOSN=LNPOSN+1;
282     }
283
284     return text;
285 }
286
287 token_t MAKEWD(long letters)
288 /*  Combine TOKLEN (currently 5) uppercase letters (represented by
289  *  pairs of decimal digits in lettrs) to form a 30-bit value matching
290  *  the one that GETTXT would return given those characters plus
291  *  trailing blanks.  Caution: lettrs will overflow 31 bits if
292  *  5-letter word starts with V-Z.  As a kludgey workaround, you can
293  *  increment a letter by 5 by adding 50 to the next pair of
294  *  digits. */
295 {
296     long i = 1, word = 0;
297
298     for (long k=letters; k != 0; k=k/100) {
299         word=word+i*(MOD(k,50)+10);
300         i=i*64;
301         if (MOD(k,100) > 50)word=word+i*5;
302     }
303     i=64L*64L*64L*64L*64L/i;
304     word=word*i;
305     return word;
306 }
307
308 void fPUTTXT(token_t word, long *state, long casemake)
309 /*  Unpack the 30-bit value in word to obtain up to TOKLEN (currently
310  *  5) integer-encoded chars, and store them in inline starting at
311  *  LNPOSN.  If LNLENG>=LNPOSN, shift existing characters to the right
312  *  to make room.  STATE will be zero when puttxt is called with the
313  *  first of a sequence of words, but is thereafter unchanged by the
314  *  caller, so PUTTXT can use it to maintain state across calls.
315  *  LNPOSN and LNLENG are incremented by the number of chars stored.
316  *  If CASEMAKE=1, all letters are made uppercase; if -1, lowercase; if 0,
317  *  as is.  any other value for case is the same as 0 but also causes
318  *  trailing blanks to be included (in anticipation of subsequent
319  *  additional text). */
320 {
321     long alph1, alph2, byte, div, i, w;
322
323     alph1=13*casemake+24;
324     alph2=26*labs(casemake)+alph1;
325     if (labs(casemake) > 1)
326         alph1=alph2;
327     /*  alph1&2 define range of wrong-case chars, 11-36 or 37-62 or empty. */
328     div=64L*64L*64L*64L;
329     w=word;
330     for (i=1; i<=TOKLEN; i++) 
331     {
332         if (w <= 0 && *state == 0 && labs(casemake) <= 1)
333             return;
334         byte=w/div;
335         w=(w-byte*div)*64;
336         if (!(*state != 0 || byte != PERCENT)) {
337             *state=PERCENT;
338             continue;
339         }
340         SHFTXT(LNPOSN,1);
341         *state=*state+byte;
342         if (*state < alph2 && *state >= alph1)*state=*state-26*casemake;
343         INLINE[LNPOSN]=*state;
344         LNPOSN=LNPOSN+1;
345         *state=0;
346     }
347 }
348 #define PUTTXT(WORD,STATE,CASE) fPUTTXT(WORD,&STATE,CASE)
349
350 void SHFTXT(long from, long delta) 
351 /*  Move INLINE(N) to INLINE(N+DELTA) for N=FROM,LNLENG.  Delta can be
352  *  negative.  LNLENG is updated; LNPOSN is not changed. */
353 {
354     long I, k, j;
355
356     if (!(LNLENG < from || delta == 0)) {
357         for (I=from; I<=LNLENG; I++) {
358             k=I;
359             if (delta > 0)
360                 k=from+LNLENG-I;
361             j=k+delta;
362             INLINE[j]=INLINE[k];
363         }
364     }
365     LNLENG=LNLENG+delta;
366 }
367
368 void TYPE0(void)
369 /*  Type a blank line.  This procedure is provided as a convenience for callers
370  *  who otherwise have no use for MAPCOM. */
371 {
372     long temp;
373
374     temp=LNLENG;
375     LNLENG=0;
376     TYPE();
377     LNLENG=temp;
378     return;
379 }
380
381 /*  Suspend/resume I/O routines (SAVWDS, SAVARR, SAVWRD) */
382
383 void fSAVWDS(long *W1, long *W2, long *W3, long *W4,
384              long *W5, long *W6, long *W7)
385 /* Write or read 7 variables.  See SAVWRD. */
386 {
387     SAVWRD(0,(*W1));
388     SAVWRD(0,(*W2));
389     SAVWRD(0,(*W3));
390     SAVWRD(0,(*W4));
391     SAVWRD(0,(*W5));
392     SAVWRD(0,(*W6));
393     SAVWRD(0,(*W7));
394 }
395
396 void fSAVARR(long arr[], long n)
397 /* Write or read an array of n words.  See SAVWRD. */
398 {
399     long i;
400
401     for (i=1; i<=n; i++) {
402         SAVWRD(0,arr[i]);
403     }
404     return;
405 }
406
407 void fSAVWRD(long op, long *pword) 
408 /*  If OP<0, start writing a file, using word to initialise encryption; save
409  *  word in the file.  If OP>0, start reading a file; read the file to find
410  *  the value with which to decrypt the rest.  In either case, if a file is
411  *  already open, finish writing/reading it and don't start a new one.  If OP=0,
412  *  read/write a single word.  Words are buffered in case that makes for more
413  *  efficient disk use.  We also compute a simple checksum to catch elementary
414  *  poking within the saved file.  When we finish reading/writing the file,
415  *  we store zero into *PWORD if there's no checksum error, else nonzero. */
416 {
417     static long buf[250], cksum = 0, h1, hash = 0, n = 0, state = 0;
418
419     if (op != 0)
420     {
421         long ifvar = state; 
422         switch (ifvar<0 ? -1 : (ifvar>0 ? 1 : 0)) 
423         { 
424         case -1:
425         case 1:
426             if (n == 250)SAVEIO(1,state > 0,buf);
427             n=MOD(n,250)+1;
428             if (state <= 0) {
429                 n--; buf[n]=cksum; n++;
430                 SAVEIO(1,false,buf);
431             }
432             n--; *pword=buf[n]-cksum; n++;
433             SAVEIO(-1,state > 0,buf);
434             state=0;
435             break;
436         case 0: /* FIXME: Huh? should be impossible */
437             state=op;
438             SAVEIO(0,state > 0,buf);
439             n=1;
440             if (state <= 0) {
441                 hash=MOD(*pword,1048576L);
442                 buf[0]=1234L*5678L-hash;
443             }
444             SAVEIO(1,true,buf);
445             hash=MOD(1234L*5678L-buf[0],1048576L);
446             cksum=buf[0];
447             return;
448         }
449     }
450     if (state == 0)
451         return;
452     if (n == 250)
453         SAVEIO(1,state > 0,buf);
454     n=MOD(n,250)+1;
455     h1=MOD(hash*1093L+221573L,1048576L);
456     hash=MOD(h1*1093L+221573L,1048576L);
457     h1=MOD(h1,1234)*765432+MOD(hash,123);
458     n--;
459     if (state > 0)
460         *pword=buf[n]+h1;
461     buf[n]=*pword-h1;
462     n++;
463     cksum=MOD(cksum*13+*pword,1000000000L);
464 }
465
466 /*  Data structure  routines */
467
468 long VOCAB(long id, long init) 
469 /*  Look up ID in the vocabulary (ATAB) and return its "definition" (KTAB), or
470  *  -1 if not found.  If INIT is positive, this is an initialisation call setting
471  *  up a keyword variable, and not finding it constitutes a bug.  It also means
472  *  that only KTAB values which taken over 1000 equal INIT may be considered.
473  *  (Thus "STEPS", which is a motion verb as well as an object, may be located
474  *  as an object.)  And it also means the KTAB value is taken modulo 1000. */
475 {
476     long i, lexeme;
477
478     for (i=1; i<=TABSIZ; i++) {
479         if (KTAB[i] == -1) {
480             lexeme= -1;
481             if (init < 0)
482                 return(lexeme);
483             BUG(5);
484         }
485         if (init >= 0 && KTAB[i]/1000 != init) 
486             continue;
487         if (ATAB[i] == id) {
488             lexeme=KTAB[i];
489             if (init >= 0)
490                 lexeme=MOD(lexeme,1000);
491             return(lexeme);
492         }
493     }
494     BUG(21);
495 }
496
497 void DSTROY(long object)
498 /*  Permanently eliminate "object" by moving to a non-existent location. */
499 {
500     MOVE(object,0);
501 }
502
503 void JUGGLE(long object)
504 /*  Juggle an object by picking it up and putting it down again, the purpose
505  *  being to get the object to the front of the chain of things at its loc. */
506 {
507     long i, j;
508
509     i=game.place[object];
510     j=game.fixed[object];
511     MOVE(object,i);
512     MOVE(object+NOBJECTS,j);
513 }
514
515 void MOVE(long object, long where)
516 /*  Place any object anywhere by picking it up and dropping it.  May
517  *  already be toting, in which case the carry is a no-op.  Mustn't
518  *  pick up objects which are not at any loc, since carry wants to
519  *  remove objects from game.atloc chains. */
520 {
521     long from;
522
523     if (object > NOBJECTS) 
524         from=game.fixed[object-NOBJECTS];
525     else
526         from=game.place[object];
527     if (from > 0 && from <= 300)
528         CARRY(object,from);
529     DROP(object,where);
530 }
531
532 long PUT(long object, long where, long pval)
533 /*  PUT is the same as MOVE, except it returns a value used to set up the
534  *  negated game.prop values for the repository objects. */
535 {
536     MOVE(object,where);
537     return (-1)-pval;;
538 }
539
540 void CARRY(long object, long where) 
541 /*  Start toting an object, removing it from the list of things at its former
542  *  location.  Incr holdng unless it was already being toted.  If object>NOBJECTS
543  *  (moving "fixed" second loc), don't change game.place or game.holdng. */
544 {
545     long temp;
546
547     if (object <= NOBJECTS) {
548         if (game.place[object] == -1)
549             return;
550         game.place[object]= -1;
551         game.holdng=game.holdng+1;
552     }
553     if (game.atloc[where] == object) {
554         game.atloc[where]=game.link[object];
555         return;
556     }
557     temp=game.atloc[where];
558     while (game.link[temp] != object) {
559         temp=game.link[temp];
560     }
561     game.link[temp]=game.link[object];
562 }
563
564 void DROP(long object, long where)
565 /*  Place an object at a given loc, prefixing it onto the game.atloc list.  Decr
566  *  game.holdng if the object was being toted. */
567 {
568     if (object > NOBJECTS)
569         game.fixed[object-NOBJECTS] = where;
570     else
571     {
572         if (game.place[object] == -1)
573             --game.holdng;
574         game.place[object] = where;
575     }
576     if (where <= 0)
577         return;
578     game.link[object] = game.atloc[where];
579     game.atloc[where] = object;
580 }
581
582 long ATDWRF(long where)
583 /*  Return the index of first dwarf at the given location, zero if no dwarf is
584  *  there (or if dwarves not active yet), -1 if all dwarves are dead.  Ignore
585  *  the pirate (6th dwarf). */
586 {
587     long at, i;
588
589     at =0;
590     if (game.dflag < 2)
591         return(at);
592     at = -1;
593     for (i=1; i<=NDWARVES-1; i++) {
594         if (game.dloc[i] == where)
595             return i;
596         if (game.dloc[i] != 0)
597             at=0;
598     }
599     return(at);
600 }
601
602 /*  Utility routines (SETBIT, TSTBIT, set_seed, get_next_lcg_value,
603  *  randrange, RNDVOC, BUG) */
604
605 long SETBIT(long bit)
606 /*  Returns 2**bit for use in constructing bit-masks. */
607 {
608     return(2 << bit);
609 }
610
611 bool TSTBIT(long mask, int bit)
612 /*  Returns true if the specified bit is set in the mask. */
613 {
614     return (mask & (1 << bit)) != 0;
615 }
616
617 void set_seed(long seedval)
618 /* Set the LCG seed */
619 {
620     lcgstate.x = (unsigned long) seedval % lcgstate.m;
621 }
622
623 unsigned long get_next_lcg_value(void)
624 /* Return the LCG's current value, and then iterate it. */
625 {
626     unsigned long old_x = lcgstate.x;
627     lcgstate.x = (lcgstate.a * lcgstate.x + lcgstate.c) % lcgstate.m;
628     return old_x;
629 }
630
631 long randrange(long range)
632 /* Return a random integer from [0, range). */
633 {
634     return range * get_next_lcg_value() / lcgstate.m;
635 }
636
637 long RNDVOC(long second, long force)
638 /*  Searches the vocabulary ATAB for a word whose second character is
639  *  char, and changes that word such that each of the other four
640  *  characters is a random letter.  If force is non-zero, it is used
641  *  as the new word.  Returns the new word. */
642 {
643     long rnd = force;
644
645     if (rnd == 0) {
646         for (int i = 1; i <= 5; i++) {
647             long j = 11 + randrange(26);
648             if (i == 2)
649                 j = second;
650             rnd = rnd * 64 + j;
651         }
652     }
653
654     long div = 64L * 64L * 64L;
655     for (int i = 1; i <= TABSIZ; i++) {
656         if (MOD(ATAB[i]/div, 64L) == second)
657         {
658             ATAB[i] = rnd;
659             break;
660         }
661     }
662
663     return rnd;
664 }
665
666 void BUG(long num)
667 /*  The following conditions are currently considered fatal bugs.  Numbers < 20
668  *  are detected while reading the database; the others occur at "run time".
669  *      0       Message line > 70 characters
670  *      1       Null line in message
671  *      2       Too many words of messages
672  *      3       Too many travel options
673  *      4       Too many vocabulary words
674  *      5       Required vocabulary word not found
675  *      6       Too many RTEXT messages
676  *      7       Too many hints
677  *      8       Location has cond bit being set twice
678  *      9       Invalid section number in database
679  *      10      Too many locations
680  *      11      Too many class or turn messages
681  *      20      Special travel (500>L>300) exceeds goto list
682  *      21      Ran off end of vocabulary table
683  *      22      Vocabulary type (N/1000) not between 0 and 3
684  *      23      Intransitive action verb exceeds goto list
685  *      24      Transitive action verb exceeds goto list
686  *      25      Conditional travel entry with no alternative
687  *      26      Location has no travel entries
688  *      27      Hint number exceeds goto list
689  *      28      Invalid month returned by date function
690  *      29      Too many parameters given to SETPRM */
691 {
692
693     printf("Fatal error %ld.  See source code for interpretation.\n", num);
694     exit(0);
695 }
696
697 /*  Machine dependent routines (MAPLIN, TYPE, MPINIT, SAVEIO) */
698
699 void MAPLIN(FILE *fp)
700 {
701     long i, val;
702
703     /*  Read a line of input, from the specified input source,
704      *  translate the chars to integers in the range 0-126 and store
705      *  them in the common array "INLINE".  Integer values are as follows:
706      *     0   = space [ASCII CODE 40 octal, 32 decimal]
707      *    1-2  = !" [ASCII 41-42 octal, 33-34 decimal]
708      *    3-10 = '()*+,-. [ASCII 47-56 octal, 39-46 decimal]
709      *   11-36 = upper-case letters
710      *   37-62 = lower-case letters
711      *    63   = percent (%) [ASCII 45 octal, 37 decimal]
712      *   64-73 = digits, 0 through 9
713      *  Remaining characters can be translated any way that is convenient;
714      *  The "TYPE" routine below is used to map them back to characters when
715      *  necessary.  The above mappings are required so that certain special
716      *  characters are known to fit in 6 bits and/or can be easily spotted.
717      *  Array elements beyond the end of the line should be filled with 0,
718      *  and LNLENG should be set to the index of the last character.
719      *
720      *  If the data file uses a character other than space (e.g., tab) to
721      *  separate numbers, that character should also translate to 0.
722      *
723      *  This procedure may use the map1,map2 arrays to maintain static data for
724      *  the mapping.  MAP2(1) is set to 0 when the program starts
725      *  and is not changed thereafter unless the routines on this page choose
726      *  to do so. */
727
728     if (MAP2[1] == 0)
729         MPINIT();
730
731     if (!oldstyle && fp == stdin)
732         fputs("> ", stdout);
733     do {
734         IGNORE(fgets(rawbuf,sizeof(rawbuf)-1,fp));
735     } while
736             (!feof(fp) && rawbuf[0] == '#');
737     if (feof(fp)) {
738         if (logfp && fp == stdin)
739             fclose(logfp);
740     } else {
741         if (logfp && fp == stdin)
742             IGNORE(fputs(rawbuf, logfp));
743         else if (!isatty(0))
744             IGNORE(fputs(rawbuf, stdout));
745         strcpy(INLINE+1, rawbuf);
746         LNLENG=0;
747         for (i=1; i<=(long)sizeof(INLINE) && INLINE[i]!=0; i++) {
748             val=INLINE[i]+1;
749             INLINE[i]=MAP1[val];
750             if (INLINE[i] != 0)
751                 LNLENG=i;
752         }
753         LNPOSN=1;
754     }
755 }
756
757 void TYPE(void)
758 /*  Type the first "LNLENG" characters stored in inline, mapping them
759  *  from integers to text per the rules described above.  INLINE
760  *  may be changed by this routine. */
761 {
762     long i;
763
764     if (LNLENG == 0) {
765         printf("\n");
766         return;
767     }
768
769     if (MAP2[1] == 0)
770         MPINIT();
771     for (i=1; i<=LNLENG; i++) {
772         INLINE[i]=MAP2[INLINE[i]+1];
773     }
774     INLINE[LNLENG+1]=0;
775     printf("%s\n", INLINE+1);
776     return;
777 }
778
779 void MPINIT(void) 
780 {
781     long first, i, j, last, val;
782     static long RUNS[7][2] = { {32,34}, {39,46}, {65,90}, {97,122}, 
783                                {37,37}, {48,57}, {0,126} };
784     for (i=1; i<=128; i++) {
785         MAP1[i]= -1;
786     }
787     val=0;
788     for (i=0; i<7; i++) {
789         first =RUNS[i][0];
790         last = RUNS[i][1];
791         for (j=first; j<=last; j++) {
792             j++; 
793             if (MAP1[j] < 0) {
794                 MAP1[j]=val;
795                 ++val;
796             }       
797             j--;
798         }
799     }
800     MAP1[128]=MAP1[10];
801     /*  For this version, tab (9) maps to space (32), so del (127)
802      *  uses tab's value */
803     MAP1[10]=MAP1[33];
804     MAP1[11]=MAP1[33];
805
806     for (i=0; i<=126; i++) {
807         i++; val=MAP1[i]+1; i--;
808         MAP2[val] = i*('B'-'A');
809         if (i >= 64)
810             MAP2[val]=(i-64)*('B'-'A')+'@';
811     }
812 }
813
814 void fSAVEIO(long op, long in, long arr[]) 
815 /*  If OP=0, ask for a file name and open a file.  (If IN=true, the file is for
816  *  input, else output.)  If OP>0, read/write ARR from/into the previously-opened
817  *  file.  (ARR is a 250-integer array.)  If OP<0, finish reading/writing the
818  *  file.  (Finishing writing can be a no-op if a "stop" statement does it
819  *  automatically.  Finishing reading can be a no-op as long as a subsequent
820  *  SAVEIO(0,false,X) will still work.) */
821 {
822     static FILE *fp = NULL; 
823     char name[50];
824
825     switch (op < 0 ? -1 : (op > 0 ? 1 : 0)) 
826     { 
827     case -1:
828         fclose(fp);
829         break;
830     case 0:
831         while (fp == NULL) {
832             printf("\nFile name: ");
833             IGNORE(fgets(name, sizeof(name), stdin));
834             fp = fopen(name,(in ? READ_MODE : WRITE_MODE));
835             if (fp == NULL)
836                 printf("Can't open file %s, try again.\n", name); 
837         }
838         break;
839     case 1: 
840         if (in)
841             IGNORE(fread(arr,sizeof(long),250,fp));
842         else
843             IGNORE(fwrite(arr,sizeof(long),250,fp));
844         break;
845     }
846 }
847
848 void DATIME(long* d, long* t)
849 {
850     struct timeval tv;
851     gettimeofday(&tv, NULL);
852     *d = (long) tv.tv_sec;
853     *t = (long) tv.tv_usec;
854 }
855
856 long MOD(long n, long m) 
857 {
858     return(n%m);
859 }