Isolate the grotty save/resume code. Most of it will soon go away.
[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
7 #include "advent.h"
8 #include "database.h"
9 #include "linenoise/linenoise.h"
10
11 #define PERCENT 63      /* partly hide the packed encoding */
12
13 const char advent_to_ascii[] = {0, 32, 33, 34, 39, 40, 41, 42, 43, 44, 45, 46, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 37, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 35, 36, 38, 47, 58, 59, 60, 61, 62, 63, 64, 91, 92, 93, 94, 95, 96, 123, 124, 125, 126, 0};
14 /* Rendered from the now-gone MPINIT() function */
15 const char ascii_to_advent[] = {0, 74, 75, 76, 77, 78, 79, 80, 81, 82, 0, 0, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 0, 1, 2, 106, 107, 63, 108, 3, 4, 5, 6, 7, 8, 9, 10, 109, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 110, 111, 112, 113, 114, 115, 116, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 117, 118, 119, 120, 121, 122, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 123, 124, 125, 126, 83};
16
17 /*  I/O routines (SPEAK, PSPEAK, RSPEAK, SETPRM, GETIN, YES) */
18
19 void SPEAK(vocab_t msg)
20 /*  Print the message which starts at LINES[N].  Precede it with a blank line
21  *  unless game.blklin is false. */
22 {
23     long blank, casemake, i, nxt, neg, nparms, param, prmtyp, state;
24
25     if (msg == 0)
26         return;
27     blank=game.blklin;
28     nparms=1;
29     do {
30         nxt=labs(LINES[msg])-1;
31         msg=msg+1;
32         LNLENG=0;
33         LNPOSN=1;
34         state=0;
35         for (i = msg; i <= nxt; i++) {
36             PUTTXT(LINES[i],&state,2);
37         }
38         LNPOSN=0;
39         ++LNPOSN;
40
41         while (LNPOSN <= LNLENG) { 
42             if (INLINE[LNPOSN] != PERCENT) {
43                 ++LNPOSN;
44                 continue;
45             }
46             prmtyp = INLINE[LNPOSN+1];
47             /*  A "%"; the next character determine the type of
48              *  parameter: 1 (!) = suppress message completely, 29 (S) = NULL
49              *  If PARAM=1, else 'S' (optional plural ending), 33 (W) = word
50              *  (two 30-bit values) with trailing spaces suppressed, 22 (L) or
51              *  31 (U) = word but map to lower/upper case, 13 (C) = word in
52              *  lower case with first letter capitalised, 30 (T) = text ending
53              *  with a word of -1, 65-73 (1-9) = number using that many
54              *  characters, 12 (B) = variable number of blanks. */
55             if (prmtyp == 1)
56                 return;
57             if (prmtyp == 29) {
58                 SHFTXT(LNPOSN+2,-1);
59                 INLINE[LNPOSN] = 55;
60                 if (PARMS[nparms] == 1)
61                     SHFTXT(LNPOSN+1,-1);
62                 ++nparms;
63                 continue;
64             }
65             if (prmtyp == 30) {
66                 SHFTXT(LNPOSN+2,-2);
67                 state=0;
68                 casemake=2;
69
70                 while (PARMS[nparms] > 0) {
71                     if (PARMS[nparms+1] < 0)
72                         casemake=0;
73                     PUTTXT(PARMS[nparms],&state,casemake);
74                     nparms=nparms+1;
75                 }
76                 ++nparms;
77                 continue;
78             }
79             if (prmtyp == 12) {
80                 prmtyp=PARMS[nparms];
81                 SHFTXT(LNPOSN+2,prmtyp-2);
82                 if (prmtyp != 0) {
83                     for (i=1; i<=prmtyp; i++) {
84                         INLINE[LNPOSN]=0;
85                         ++LNPOSN;
86                     }
87                 }
88                 ++nparms;
89                 continue;
90             }
91             if (prmtyp == 33 || prmtyp == 22 || prmtyp == 31 || prmtyp == 13) {
92                 SHFTXT(LNPOSN+2,-2);
93                 state = 0;
94                 casemake = -1;
95                 if (prmtyp == 31)
96                     casemake=1;
97                 if (prmtyp == 33)
98                     casemake=0;
99                 i = LNPOSN;
100                 PUTTXT(PARMS[nparms],&state,casemake);
101                 PUTTXT(PARMS[nparms+1],&state,casemake);
102                 if (prmtyp == 13 && INLINE[i] >= 37 && INLINE[i] <= 62)
103                     INLINE[i] -= 26;
104                 nparms += 2;
105                 continue;
106             }
107
108             prmtyp=prmtyp-64;
109             if (prmtyp < 1 || prmtyp > 9) {
110                 ++LNPOSN;
111                 continue;
112             }
113             SHFTXT(LNPOSN+2,prmtyp-2);
114             LNPOSN += prmtyp;
115             param=labs(PARMS[nparms]);
116             neg=0;
117             if (PARMS[nparms] < 0)
118                 neg=9;
119             for (i=1; i <= prmtyp; i++) {
120                 --LNPOSN;
121                 INLINE[LNPOSN]=MOD(param,10)+64;
122                 if (i != 1 && param == 0) {
123                     INLINE[LNPOSN]=neg;
124                     neg=0;
125                 }
126                 param=param/10;
127             }
128             LNPOSN += prmtyp;
129             ++nparms;
130             continue;
131         }
132
133         if (blank)
134             TYPE0();
135         blank=false;
136         TYPE();
137         msg = nxt + 1;
138     } while
139         (LINES[msg] >= 0);
140 }
141
142 void PSPEAK(vocab_t msg,int skip)
143 /*  Find the skip+1st message from msg and print it.  msg should be
144  *  the index of the inventory message for object.  (INVEN+N+1 message
145  *  is game.prop=N message). */
146 {
147     long i, m;
148
149     m=PTEXT[msg];
150     if (skip >= 0) {
151         for (i=0; i <=skip; i++) {
152             do {
153                 m=labs(LINES[m]);
154             } while
155                 (LINES[m] >= 0);
156         }
157     }
158     SPEAK(m);
159 }
160
161 void RSPEAK(vocab_t i)
162 /* Print the i-th "random" message (section 6 of database). */
163 {
164     if (i != 0)
165         SPEAK(RTEXT[i]);
166 }
167
168 void SETPRM(long first, long p1, long p2)
169 /*  Stores parameters into the PRMCOM parms array for use by speak.  P1 and P2
170  *  are stored into PARMS(first) and PARMS(first+1). */
171 {
172     if (first >= MAXPARMS)
173         BUG(29);
174     else {
175         PARMS[first] = p1;
176         PARMS[first+1] = p2;
177     }
178 }
179
180 bool GETIN(FILE *input,
181             long *pword1, long *pword1x, 
182             long *pword2, long *pword2x) 
183 /*  Get a command from the adventurer.  Snarf out the first word, pad it with
184  *  blanks, and return it in WORD1.  Chars 6 thru 10 are returned in WORD1X, in
185  *  case we need to print out the whole word in an error message.  Any number of
186  *  blanks may follow the word.  If a second word appears, it is returned in
187  *  WORD2 (chars 6 thru 10 in WORD2X), else WORD2 is -1. */
188 {
189     long junk;
190
191     for (;;) {
192         if (game.blklin)
193             TYPE0();
194         if (!MAPLIN(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 PUTTXT(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 /*  Data structure  routines */
383
384 long VOCAB(long id, long init) 
385 /*  Look up ID in the vocabulary (ATAB) and return its "definition" (KTAB), or
386  *  -1 if not found.  If INIT is positive, this is an initialisation call setting
387  *  up a keyword variable, and not finding it constitutes a bug.  It also means
388  *  that only KTAB values which taken over 1000 equal INIT may be considered.
389  *  (Thus "STEPS", which is a motion verb as well as an object, may be located
390  *  as an object.)  And it also means the KTAB value is taken modulo 1000. */
391 {
392     long i, lexeme;
393
394     for (i=1; i<=TABSIZ; i++) {
395         if (KTAB[i] == -1) {
396             lexeme= -1;
397             if (init < 0)
398                 return(lexeme);
399             BUG(5);
400         }
401         if (init >= 0 && KTAB[i]/1000 != init) 
402             continue;
403         if (ATAB[i] == id) {
404             lexeme=KTAB[i];
405             if (init >= 0)
406                 lexeme=MOD(lexeme,1000);
407             return(lexeme);
408         }
409     }
410     BUG(21);
411 }
412
413 void DSTROY(long object)
414 /*  Permanently eliminate "object" by moving to a non-existent location. */
415 {
416     MOVE(object,0);
417 }
418
419 void JUGGLE(long object)
420 /*  Juggle an object by picking it up and putting it down again, the purpose
421  *  being to get the object to the front of the chain of things at its loc. */
422 {
423     long i, j;
424
425     i=game.place[object];
426     j=game.fixed[object];
427     MOVE(object,i);
428     MOVE(object+NOBJECTS,j);
429 }
430
431 void MOVE(long object, long where)
432 /*  Place any object anywhere by picking it up and dropping it.  May
433  *  already be toting, in which case the carry is a no-op.  Mustn't
434  *  pick up objects which are not at any loc, since carry wants to
435  *  remove objects from game.atloc chains. */
436 {
437     long from;
438
439     if (object > NOBJECTS) 
440         from=game.fixed[object-NOBJECTS];
441     else
442         from=game.place[object];
443     if (from > 0 && from <= 300)
444         CARRY(object,from);
445     DROP(object,where);
446 }
447
448 long PUT(long object, long where, long pval)
449 /*  PUT is the same as MOVE, except it returns a value used to set up the
450  *  negated game.prop values for the repository objects. */
451 {
452     MOVE(object,where);
453     return (-1)-pval;;
454 }
455
456 void CARRY(long object, long where) 
457 /*  Start toting an object, removing it from the list of things at its former
458  *  location.  Incr holdng unless it was already being toted.  If object>NOBJECTS
459  *  (moving "fixed" second loc), don't change game.place or game.holdng. */
460 {
461     long temp;
462
463     if (object <= NOBJECTS) {
464         if (game.place[object] == -1)
465             return;
466         game.place[object]= -1;
467         game.holdng=game.holdng+1;
468     }
469     if (game.atloc[where] == object) {
470         game.atloc[where]=game.link[object];
471         return;
472     }
473     temp=game.atloc[where];
474     while (game.link[temp] != object) {
475         temp=game.link[temp];
476     }
477     game.link[temp]=game.link[object];
478 }
479
480 void DROP(long object, long where)
481 /*  Place an object at a given loc, prefixing it onto the game.atloc list.  Decr
482  *  game.holdng if the object was being toted. */
483 {
484     if (object > NOBJECTS)
485         game.fixed[object-NOBJECTS] = where;
486     else
487     {
488         if (game.place[object] == -1)
489             --game.holdng;
490         game.place[object] = where;
491     }
492     if (where <= 0)
493         return;
494     game.link[object] = game.atloc[where];
495     game.atloc[where] = object;
496 }
497
498 long ATDWRF(long where)
499 /*  Return the index of first dwarf at the given location, zero if no dwarf is
500  *  there (or if dwarves not active yet), -1 if all dwarves are dead.  Ignore
501  *  the pirate (6th dwarf). */
502 {
503     long at, i;
504
505     at =0;
506     if (game.dflag < 2)
507         return(at);
508     at = -1;
509     for (i=1; i<=NDWARVES-1; i++) {
510         if (game.dloc[i] == where)
511             return i;
512         if (game.dloc[i] != 0)
513             at=0;
514     }
515     return(at);
516 }
517
518 /*  Utility routines (SETBIT, TSTBIT, set_seed, get_next_lcg_value,
519  *  randrange, RNDVOC, BUG) */
520
521 long SETBIT(long bit)
522 /*  Returns 2**bit for use in constructing bit-masks. */
523 {
524     return(1 << bit);
525 }
526
527 bool TSTBIT(long mask, int bit)
528 /*  Returns true if the specified bit is set in the mask. */
529 {
530     return (mask & (1 << bit)) != 0;
531 }
532
533 void set_seed(long seedval)
534 /* Set the LCG seed */
535 {
536     lcgstate.x = (unsigned long) seedval % lcgstate.m;
537 }
538
539 unsigned long get_next_lcg_value(void)
540 /* Return the LCG's current value, and then iterate it. */
541 {
542     unsigned long old_x = lcgstate.x;
543     lcgstate.x = (lcgstate.a * lcgstate.x + lcgstate.c) % lcgstate.m;
544     return old_x;
545 }
546
547 long randrange(long range)
548 /* Return a random integer from [0, range). */
549 {
550     return range * get_next_lcg_value() / lcgstate.m;
551 }
552
553 long RNDVOC(long second, long force)
554 /*  Searches the vocabulary ATAB for a word whose second character is
555  *  char, and changes that word such that each of the other four
556  *  characters is a random letter.  If force is non-zero, it is used
557  *  as the new word.  Returns the new word. */
558 {
559     long rnd = force;
560
561     if (rnd == 0) {
562         for (int i = 1; i <= 5; i++) {
563             long j = 11 + randrange(26);
564             if (i == 2)
565                 j = second;
566             rnd = rnd * 64 + j;
567         }
568     }
569
570     long div = 64L * 64L * 64L;
571     for (int i = 1; i <= TABSIZ; i++) {
572         if (MOD(ATAB[i]/div, 64L) == second)
573         {
574             ATAB[i] = rnd;
575             break;
576         }
577     }
578
579     return rnd;
580 }
581
582 void BUG(long num)
583 /*  The following conditions are currently considered fatal bugs.  Numbers < 20
584  *  are detected while reading the database; the others occur at "run time".
585  *      0       Message line > 70 characters
586  *      1       Null line in message
587  *      2       Too many words of messages
588  *      3       Too many travel options
589  *      4       Too many vocabulary words
590  *      5       Required vocabulary word not found
591  *      6       Too many RTEXT messages
592  *      7       Too many hints
593  *      8       Location has cond bit being set twice
594  *      9       Invalid section number in database
595  *      10      Too many locations
596  *      11      Too many class or turn messages
597  *      20      Special travel (500>L>300) exceeds goto list
598  *      21      Ran off end of vocabulary table
599  *      22      Vocabulary type (N/1000) not between 0 and 3
600  *      23      Intransitive action verb exceeds goto list
601  *      24      Transitive action verb exceeds goto list
602  *      25      Conditional travel entry with no alternative
603  *      26      Location has no travel entries
604  *      27      Hint number exceeds goto list
605  *      28      Invalid month returned by date function
606  *      29      Too many parameters given to SETPRM */
607 {
608
609     printf("Fatal error %ld.  See source code for interpretation.\n", num);
610     exit(0);
611 }
612
613 /*  Machine dependent routines (MAPLIN, TYPE, SAVEIO) */
614
615 bool MAPLIN(FILE *fp)
616 {
617     long i, val;
618     bool eof;
619
620     /*  Read a line of input, from the specified input source,
621      *  translate the chars to integers in the range 0-126 and store
622      *  them in the common array "INLINE".  Integer values are as follows:
623      *     0   = space [ASCII CODE 40 octal, 32 decimal]
624      *    1-2  = !" [ASCII 41-42 octal, 33-34 decimal]
625      *    3-10 = '()*+,-. [ASCII 47-56 octal, 39-46 decimal]
626      *   11-36 = upper-case letters
627      *   37-62 = lower-case letters
628      *    63   = percent (%) [ASCII 45 octal, 37 decimal]
629      *   64-73 = digits, 0 through 9
630      *  Remaining characters can be translated any way that is convenient;
631      *  The "TYPE" routine below is used to map them back to characters when
632      *  necessary.  The above mappings are required so that certain special
633      *  characters are known to fit in 6 bits and/or can be easily spotted.
634      *  Array elements beyond the end of the line should be filled with 0,
635      *  and LNLENG should be set to the index of the last character.
636      *
637      *  If the data file uses a character other than space (e.g., tab) to
638      *  separate numbers, that character should also translate to 0.
639      *
640      *  This procedure may use the map1,map2 arrays to maintain static data for
641      *  the mapping.  MAP2(1) is set to 0 when the program starts
642      *  and is not changed thereafter unless the routines on this page choose
643      *  to do so. */
644
645     if (!oldstyle && !isatty(1))
646         fputs("> ", stdout);
647     do {
648         if (oldstyle) {
649             IGNORE(fgets(rawbuf,sizeof(rawbuf)-1,fp));
650             eof = (feof(fp));
651         } else {
652             char *cp = linenoise("> ");
653             eof = (cp == NULL);
654             if (!eof) {
655                 strncpy(rawbuf, cp, sizeof(rawbuf)-1);
656                 linenoiseHistoryAdd(rawbuf);
657                 strncat(rawbuf, "\n", sizeof(rawbuf)-1);
658                 linenoiseFree(cp);
659             }
660         }
661     } while
662             (!eof && rawbuf[0] == '#');
663     if (eof) {
664         if (logfp && fp == stdin)
665             fclose(logfp);
666         return false;
667     } else {
668         if (logfp && fp == stdin)
669             IGNORE(fputs(rawbuf, logfp));
670         else if (!isatty(0))
671             IGNORE(fputs(rawbuf, stdout));
672         strcpy(INLINE+1, rawbuf);
673         LNLENG=0;
674         for (i=1; i<=(long)sizeof(INLINE) && INLINE[i]!=0; i++) {
675             val=INLINE[i]+1;
676             INLINE[i]=ascii_to_advent[val];
677             if (INLINE[i] != 0)
678                 LNLENG=i;
679         }
680         LNPOSN=1;
681         return true;
682     }
683 }
684
685 void TYPE(void)
686 /*  Type the first "LNLENG" characters stored in inline, mapping them
687  *  from integers to text per the rules described above.  INLINE
688  *  may be changed by this routine. */
689 {
690     long i;
691
692     if (LNLENG == 0) {
693         printf("\n");
694         return;
695     }
696
697     for (i=1; i<=LNLENG; i++) {
698         INLINE[i]=advent_to_ascii[INLINE[i]+1];
699     }
700     INLINE[LNLENG+1]=0;
701     printf("%s\n", INLINE+1);
702     return;
703 }
704
705 void DATIME(long* d, long* t)
706 {
707     struct timeval tv;
708     gettimeofday(&tv, NULL);
709     *d = (long) tv.tv_sec;
710     *t = (long) tv.tv_usec;
711 }
712
713 long MOD(long n, long m) 
714 {
715     return(n%m);
716 }