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