1 /******************************************************************************
2 
3     #     ####   #          #    #####            ####
4     #    #    #  #          #    #    #          #    #
5     #    #    #  #          #    #####           #
6     #    #    #  #          #    #    #   ###    #
7     #    #    #  #          #    #    #   ###    #    #
8     #     ####   ######     #    #####    ###     ####
9 
10 ******************************************************************************/
11 /* This file is part of MAPMAKER 3.0b, Copyright 1987-1992, Whitehead Institute
12    for Biomedical Research. All rights reserved. See READ.ME for license. */
13 
14 #define INC_IO
15 #define INC_MSG
16 #define INC_MEM
17 #define INC_STR
18 #define INC_MATH
19 #define INC_HELP_DEFS
20 #include "system.h"
21 #include "shell.h" /* just to get the decl of error()? */
22 
23 /***** globals- see descriptions in iolib.h *****/
24 char *ps, *ps_, *ln, *ln_;
25 bool logging, more_mode, more, ignore_eof, scrollback;
26 int tty_lines, term;
27 bool screen, curses, split, interactive;
28 
29 /* Local declarations - externed in iolib.h only for syscode.c  */
30 void ioerror();
31 int lookup_fp();
32 char *out_file, out_modechar, *in_file, in_modechar;
33 char *photo_file, photo_modechar;
34 FILE *in, *out, *photo;
35 
36 char *linebuf;  /* stdout buffer: one screen line max */
37 int cursor, buf, printed_lines;
38 bool supress_more, more_break_pending, memorize;
39 int lines_written;
40 char *gotln, *lnptr;
41 
42 int  temp_print_spaces;
43 #define temp_print_up (temp_print_spaces>0)
44 
45 void flush_linebuf();
46 void flush_and_force_nl();
47 void mem_puts();
48 bool dump_held_lines();
49 void dump_memory_lines();
50 bool dump_to_screen();
51 void kill_temp_print();
52 bool lib_clear_screen();
53 bool really_do_more();
54 
55 char **memory, **held;
56 int memory_end, memory_wrapped, holding, hold_count;
57 
58 FILE **in_fp; /* the "stack" of input files for redirecting input */
59 int redirs;
60 
61 /* a list associating fp's with names, etc */
62 FILE_INFO **files;
63 
64 #define ALLOW_SUPRESS_MORE FALSE
65 
66 
67 /***** ERROR HANDLING *****/
68 
69 #define try_filenum(i) if (files[i]->fp==fp) {	\
70     if (name!=NULL) *name=files[i]->name; 	\
71     if (modechar!=NULL) *modechar=files[i]->modechar; \
72     return(i); }
73 
lookup_fp(fp,name,modechar)74 int lookup_fp(fp,name,modechar)
75 FILE *fp;
76 char **name, *modechar; /* side-effected if non-null */
77 {
78     int i;
79 
80     if (fp==NULL) {
81 	if (name!=NULL) *name= null_string;
82 	if (modechar!=NULL) *modechar='?';
83 	return(-1);
84     }
85 
86     try_filenum(3); /* MUST HAVE 0...MAXFILES-1 OF THESE! */
87     try_filenum(4);
88     try_filenum(5);
89     try_filenum(6);
90     try_filenum(7);
91     try_filenum(8);
92     try_filenum(9);
93     try_filenum(0); /* These are in, out, and photo */
94     try_filenum(1);
95     try_filenum(2);
96 
97     if (name!=NULL) *name=null_string;
98     if (modechar!=NULL) *modechar='?';
99     return(-1);
100 }
101 
102 
ioerror(errmsg,fp,ioline)103 void ioerror(errmsg,fp,ioline)
104 char *errmsg;
105 FILE *fp;
106 char *ioline;
107 {
108          IOERROR_errmsg= errmsg;
109 	 if (ioline!=NULL) nstrcpy(IOERROR_linecopy,ioline,64);
110 	     else ioline[0]='\0';
111 	 lookup_fp(fp,&IOERROR_filename,&IOERROR_modechar);
112 	 send(IOERROR);
113 }
114 
115 
116 /********** FILE HANDLING STUFF ***********/
117 
make_filename_in_dir(str,force_ext,ext,add_dir_mode,dir)118 bool make_filename_in_dir(str,force_ext,ext,add_dir_mode,dir)
119 char *str; /* str is side-effected: it should be PATH_LENGTH+1 chars long */
120 bool force_ext;
121 char *ext; /* may have the preceeding '.' or not */
122 int add_dir_mode; /* values defined in iolib.h */
123 char *dir; /* assume no trailing divider, except VMS dirs, which have [..] */
124 {
125     int end, dir_chars, has_dir, i, j;
126     char cwd[PATH_LENGTH+1];
127 
128     if (!make_filename(str,force_ext,ext)) return(FALSE);
129 
130     end=len(str)-1;
131 
132     for (i=end; i>=0; i--)
133       if (strin(PATH_DIR_SEPARATORS,str[i])) break;
134 
135     /* has_dir= (i<0); --> this is opposite of what it should be MJD 1-21-93 */
136 
137     has_dir = (i>=0);
138     dir_chars=i+1;
139 
140     if (has_dir && add_dir_mode==DEFAULT_DIR) return(TRUE);
141     if (dir==NULL) { /* eg dir==CURRENT_DIR */
142 	dir=cwd;
143 	if (!get_directory(dir)) {
144 	    dir[0]='\0';
145 	    if (!has_dir) return(TRUE); /* Nothing we can do, but we're OK */
146 	}
147     } else if (streq(dir,HOME_DIR)) {
148 	dir=cwd;
149 	if (!get_home_directory(dir)) {
150 	    dir[0]='\0';
151 	    if (!has_dir) return(TRUE);
152 	}
153     } else if (streq(dir,CODE_DIR)) {
154     	dir=cwd;
155 	if (!get_code_directory(dir)) {
156 	    dir[0]='\0';
157 	    if (!has_dir) return(TRUE);
158 	}
159     }
160 
161     /* DO WE REALLY WANT count_tokens() HERE? */
162     despace(dir); if (count_tokens(dir)!=1) return(FALSE);
163     if (PATH_UPPERCASE) uppercase(dir);
164     for (i=0, j=0; i<=len(dir); i++)
165       if (strin(PATH_DIR_CHARS,dir[i])) dir[j++]=dir[i];
166 
167     if (has_dir) strdel(str,dir_chars);
168     maxstrcat(dir,PATH_DIR_FILE_INSERT,PATH_LENGTH);
169     maxstrins(str,dir,PATH_LENGTH);
170     return(TRUE);
171 }
172 
173 #define NO_DOT (-1)
174 
make_filename(str,force_ext,ext)175 bool make_filename(str,force_ext,ext)
176 char *str; /* str is side-effected: it should be PATH_LENGTH+1 chars long */
177 bool force_ext;
178 char *ext; /* may have the preceeding '.' or not */
179 {
180     int i, j, first_dot, next_dot, last_dot, root_start, end;
181     bool has_ext, two_ext;
182 
183     if (nullstr(str) || ext==NULL) send(CRASH);
184 
185     /* Elimiate extra whitespace, despace, truncate, filter, etc. */
186     despace(str); truncstr(str,PATH_LENGTH);
187     if (PATH_UPPERCASE) uppercase(str);
188     if (count_tokens(str)!=1) return(FALSE);
189     for (i=0, j=0; i<=len(str); i++)
190       if (strin(PATH_OK_CHARS,str[i])) str[j++]=str[i];
191 
192     /* Also for the extension... */
193     despace(ext); if (ext[0]=='.') ext++;
194     if (PATH_UPPERCASE) uppercase(ext);
195     if (count_tokens(ext)!=1) return(FALSE);
196     for (i=0, j=0; i<=len(ext); i++)
197       if (strin(PATH_OK_CHARS,ext[i])) ext[j++]=ext[i];
198 
199     /* Find the dot for the extension. If PATH_SINGLE_EXTENSION, find
200        the leftmost dot to the right of a PATH_DIR_SEPARATOR, otherwise
201        find the rightmost dot of a PATH_DIR_SEPARATOR */
202 
203     end=len(str)-1;
204 
205     for (i=end; i>=0; i--)
206       if (strin(PATH_DIR_SEPARATORS,str[i])) break;
207     root_start=i+1;
208     if (root_start==end+1) return(FALSE);
209 
210     for (i=root_start, first_dot=NO_DOT; i<=end; i++)
211       if (str[i]=='.') { first_dot=i; break; }
212 
213     for (i=first_dot+1, next_dot=NO_DOT; i<=end; i++)
214       if (str[i]=='.') { next_dot=i; break; }
215 
216     for (i=end, last_dot=NO_DOT; i>=root_start; i--)
217       if (str[i]=='.') { last_dot=i; break; }
218 
219     has_ext= first_dot!=NO_DOT;
220     two_ext= has_ext && next_dot!=NO_DOT;
221 
222     if (PATH_SINGLE_EXTENSION && two_ext)  /* truncate */
223       { str[next_dot]='\0'; last_dot=first_dot; next_dot=NO_DOT; }
224 
225     if (!has_ext && !nullstr(ext)) {
226 	truncstr(str,PATH_LENGTH-2); /* then add a dot */
227 	strcat(str,".");
228 	maxstrcat(str+len(str),ext,PATH_LENGTH);
229     }
230 
231     if (force_ext && has_ext) {
232 	str[last_dot]='\0';
233 	truncstr(str,PATH_LENGTH-2);
234 	if (!nullstr(ext)) strcat(str,".");
235 	maxstrcat(str+len(str),ext,PATH_LENGTH);
236     }
237 
238     return(TRUE);
239 }
240 
241 
open_file(name,mode)242 FILE *open_file(name,mode)
243 char *name;  /* It's best to use make_filename() on name first */
244 char *mode;  /* use a #define in iolib.h for mode */
245 {
246         FILE *fp; int i, n;
247 
248 	for(i=3, n= -1; i<MAXFILES; i++) if (files[i]->fp==NULL) n=i;
249 	if (n== -1) ioerror("can't open: too many files",(FILE*)NULL,name);
250 
251 	if ((fp= fopen(name,mode))==NULL) { /* Can't open */
252 	   nstrcpy(CANTOPEN_path,name,PATH_LENGTH);
253 	   CANTOPEN_modechar= mode[0];
254 	   send(CANTOPEN);
255 	}
256 
257 	files[n]->fp= fp;
258 	nstrcpy(files[n]->name,name,PATH_LENGTH);
259 	files[n]->modechar= mode[0];
260 	return(fp);
261 }
262 
263 
close_file(fp)264 void close_file(fp)   /* ignore NULL fp */
265 FILE *fp;
266 {
267     int n, i;
268 
269     if (fp==NULL) return;
270 
271     for(i=3, n= -1; i<MAXFILES; i++) if (files[i]->fp==fp) n=i;
272     if (n==-1) send(CANTCLOSE);
273     if (dos_output) lib_puts(fp,"\015"); /* ctrl-Z at end of DOS files */
274     if (!xclose(fp)) send(CANTCLOSE);
275     files[n]->fp = NULL;
276     strcpy(files[n]->name,"");
277 }
278 
279 
fwrite(fp,str)280 void fwrite(fp,str)
281 FILE *fp;
282 char *str;
283 { _filter(str); lib_puts(fp,str); } /* DO NOTHING FANCY */
284 
fprint(fp,str)285 void fprint(fp,str)
286 FILE *fp;
287 char *str;
288 { _filter(str); lib_puts(fp,str); } /* JUST FOR NOW - GET FANCIER LATER */
289 
290 
finput(fp,str,length)291 void finput(fp,str,length)
292 FILE *fp;
293 char *str;
294 int length;
295 {
296     if (fp==stdin) { input("? ",str,length); return; }
297     if (!file_gets(fp,str,length)) send(ENDOFILE);
298     _filter(str);
299 }
300 
301 
fgetln_(fp)302 void fgetln_(fp) /* ln is side-effected. */
303 FILE *fp;
304 {
305     if (fp==stdin) { getln("? "); return; }
306     ln=lnptr;
307     if (!file_gets(fp,ln,MAXLINE)) send(ENDOFILE);
308     _filter(ln);
309 }
310 
311 
fgetdataln(fp,count)312 void fgetdataln(fp,count) /* ln is side-effected */
313 FILE *fp;
314 int *count;
315 {
316     int i;
317     char *p;
318 
319     if (fp==stdin) { getln("? "); if (count!=NULL) ++*count; return; }
320     ln=lnptr;
321     do {
322         if (!file_gets(fp,ln,MAXLINE)) send(ENDOFILE);
323 	if (count!=NULL) ++*count;
324 	for (p=ln; white(*p)||trash(*p); ++p) {}
325     } while (*p=='\0' || *p=='#');
326     _filter(ln);
327 }
328 
329 
end_of_file(fp)330 bool end_of_file(fp)
331 FILE *fp;
332 {
333     int c;
334 
335     while ((c=getc(fp))!=EOF)
336       if (!trash(c)) { ungetc(c,fp); return(FALSE); }
337     return(TRUE);
338 }
339 
end_of_text(fp)340 bool end_of_text(fp)
341 FILE *fp;
342 {
343     int c;
344 
345     while ((c=getc(fp))!=EOF)
346       if (!white(c) && !trash(c)) { ungetc(c,fp); return(FALSE); }
347     return(TRUE);
348 }
349 
350 
351 /*********************** OUTPUT ROUTINES... local ***********************/
352 
flush_linebuf()353 void flush_linebuf()
354 {
355     bool endoline;
356     if (buf==0) return;
357     endoline=(linebuf[buf-1]=='\n');
358     linebuf[buf]='\0'; buf=0;
359 
360     if (holding) maxstrcat(held[hold_count],linebuf,LINE+1);
361     else { kill_temp_print(); dump_to_screen(linebuf); }
362 
363     if (logging) lib_puts(photo,linebuf);
364     mem_puts(linebuf);
365 
366     if (endoline) {
367 	cursor=0;
368 	if (holding) {
369 	    hold_count++; held[hold_count][0]='\0';
370 	    if (hold_count==MAX_HOLD_LINES || hold_count==tty_lines-1) {
371 		if(!dump_held_lines()) {
372 		    held[0][0]='\0'; hold_count=0;
373 		    holding=0; more=FALSE;
374 		    more_break_pending=FALSE;
375 		    send(INTERRUPT);
376 		}
377 	    }
378 	}
379     }
380 }
381 
382 
dump_held_lines()383 bool dump_held_lines()
384 /* When one does hold {}, we flush_and_force_nl(TRUE). THUS, held lines will
385    always start dumping into the left column. Thus clear_screen works right. */
386 {
387     int i;
388     bool did_early_more;
389 
390     if (held[hold_count][0]!='\0')
391       { hold_count++; held[hold_count][len(held[hold_count])-1]='\n'; }
392     if (hold_count==0) return(TRUE);
393 
394     kill_temp_print();
395     if (more_break_pending) {
396 	if (!really_do_more()) { hold_count=0; return(FALSE); }
397     } else if (more) {
398 	if (printed_lines>tty_lines/2 &&
399 	    printed_lines+hold_count>=tty_lines-2) {
400 	    if (!really_do_more()) {
401 		hold_count=0; return(FALSE);
402 	    }
403 	}
404     }
405 
406     if ((!scrollback || curses || wimp) &&
407 	(hold_count>=tty_lines-4 || hold_count>(3*tty_lines)/4))
408       lib_clear_screen();
409 
410     /* NEEDS WIMP HOOK */
411 #ifdef HAVE_CURSES
412     else if (curses) curses_scrollup(hold_count+2);
413 #endif
414 
415     /* dump_to_screen may add new 'more' breaks if needed */
416     for (i=0; i<=hold_count; i++) {
417 	if (!dump_to_screen(held[i])) return(FALSE);
418     }
419     hold_count=0; held[0][0]='\0';
420     return(TRUE);
421 }
422 
423 
dump_to_screen(str)424 bool dump_to_screen(str)
425 char *str;
426 {
427     if (more_break_pending) {
428 	if (!really_do_more()) {
429 	    return(FALSE);
430 	}
431     }
432     lib_puts(out,str);
433 
434     if (str[len(str)-1]=='\n') {
435 	if (++lines_written>1000000) send(CRASH);
436 	if (more) {
437 	    if (printed_lines>=tty_lines-3) more_break_pending=TRUE;
438 	    else printed_lines++;
439 	} else printed_lines=0;
440     }
441     return(TRUE);
442 }
443 
444 
really_do_more()445 bool really_do_more()
446 {
447     bool continue_flag= TRUE;
448     int i;
449     if (!interactive || redirecting_input) return(TRUE);
450 
451     run {
452  	do_highlight(TRUE);
453 	check_tty_lines();
454 	lib_puts(out,"Hit RETURN for more or Q to quit...");
455 	if (curses) lib_puts(out,"\n");
456 	fflush(out);
457 	do_highlight(FALSE);
458 	if (!tty_gets(ln_,MAXLINE-10) && !ignore_eof) {nl(); send(ENDOINPUT);}
459 	do_delete_previous_line();
460 	for (i=0; ln_[i]!='\0'; i++)
461 	  if (ln_[i]=='q' || ln_[i]=='Q') send(INTERRUPT);
462 	printed_lines=0;
463 	more_break_pending=FALSE;
464     } except_when(INTERRUPT) {
465 	do_delete_previous_line();
466 	continue_flag= FALSE;
467     }
468     return(continue_flag);
469 }
470 
471 
mem_puts(str)472 void mem_puts(str)
473 char *str;
474 {
475     int last;
476     if (str[0]=='\0' || !memorize) return;
477 
478     last= len(str)-1;
479     maxstrcat(memory[memory_end],str,LINE+1);
480     if (str[last]=='\n') {
481 	if (memory_end==MEMORY_LINES-1) { memory_end=0; memory_wrapped=TRUE; }
482 	    else memory_end++;
483 	memory[memory_end][0]='\0';
484     }
485 }
486 
487 
flush_and_force_nl(nl_on_screen_also)488 void flush_and_force_nl(nl_on_screen_also)
489 bool nl_on_screen_also;
490 {
491     kill_temp_print();
492     flush();
493     if (cursor!=0) {
494 	if (nl_on_screen_also) { nl(); flush(); }
495 	else {
496 	    if (logging) lib_puts(photo,"\n");
497 	    mem_puts("\n");
498 	    cursor=0;
499 	    if (more) {
500 		if (printed_lines>=tty_lines-3) more_break_pending=TRUE;
501 		else printed_lines++;
502 	    } else printed_lines=0;
503 	}
504     }
505 }
506 
507 
kill_temp_print()508 void kill_temp_print()
509 {
510     if (temp_print_up) {
511 	if (term==TERM_UNKNOWN) lib_puts(out,"\n");
512 	  else do_cursor_left(FAR_LEFT,"");
513 	temp_print_spaces=0;
514     }
515 }
516 
517 
lib_clear_screen()518 bool lib_clear_screen()
519 {
520     check_tty_lines();
521     if (more_break_pending) if (!really_do_more()) { send(INTERRUPT); }
522     if (do_clear_screen()) { printed_lines= -1; cursor=0; return(TRUE); }
523     else return(FALSE);
524 }
525 
526 
527 
528 /**************** application level output routines ****************/
529 
photo_to_file(new_log_name,new_log_mode)530 int photo_to_file(new_log_name,new_log_mode)
531 char *new_log_name; /* best to run through make_filename() first */
532 char *new_log_mode; /* use a #define as for open_file() */
533 /* Returns FALSE if it fails, in which case nothing is changed. If
534    nullstr(new_log_name) then photoing is stopped. */
535 {
536 	FILE *fp;
537 	flush_and_force_nl(TRUE);
538 
539 	if (nullstr(new_log_name)) {
540 		if (log_open) fclose(photo);
541 		logging= FALSE;
542 		photo_file[0]= '\0';
543 		return(TRUE);
544 
545 	} else {
546 	    if ((fp=fopen(new_log_name,new_log_mode))==NULL) {
547 		return(FALSE);
548 	    } else {
549 		if (log_open) fclose(photo);
550 		logging= TRUE;
551 		photo= fp;
552 		strcpy(photo_file,new_log_name);
553 		return(TRUE);
554 	    }
555 	}
556 }
557 
558 
print(str)559 void print(str)
560 char *str;		/* Sends IOERROR if an error occurs */
561 /* Linebuf should never have an embedded '\n' (only one at the end, maybe)
562    and should only have as many chars as can fit on the rest of the line.
563    Print interprets tabs and newlines, and punts any other control chars. */
564 {
565     int i, j, k, found;
566     char c, save[21];
567 
568     kill_temp_print();
569 
570     if (str==NULL) send(CRASH);
571     for(i=0; str[i] != '\0'; i++) {
572 	c= str[i];
573 
574 	if (c=='\n' || (c==' ' && cursor>LINE) ||
575 	    (c=='\t' && cursor>LASTTAB)) {
576 	    linebuf[buf++]='\n';
577 	    flush_linebuf();
578 
579 	} else if (cursor>LINE) {
580 	    linebuf[buf]='\0';
581 	    /* Line would wrap, so we find a nice place to break it */
582 	    for (found=FALSE, j=buf-1, k=(buf>20 ? buf-20:0); j>=k; j--)
583 	      if (white(linebuf[j])) { found=TRUE; break; }
584 	    if (found) {
585 		strcpy(save,&linebuf[j+1]);
586 		linebuf[j++]='\n'; buf=j;
587 		flush_linebuf(); /* now buf==0, cursor==0 */
588 		for (k=0; save[k]!='\0'; k++)
589 		  { linebuf[buf++]=save[k]; cursor++; }
590 	    } else { /* didn't find a whitespace */
591 		linebuf[buf++]='\n';
592 		flush_linebuf();
593 	    }
594 	    if (!trash(c)) { linebuf[buf++]= c; cursor++; }
595 
596 
597 	} else if (c=='\t' && TRANSLATE_TABS) {
598 	    do { linebuf[buf++]=' '; cursor++; } while (cursor%8 != 0);
599 
600 	} else if (!trash(c)) { /* KLUDGE: WHAT CTRL CHARS TO HANDLE?*/
601 	    linebuf[buf++]= c; cursor++;
602 	}
603     }
604 }
605 
606 
607 #ifdef NEEDS_WORK
fprint(fp,str)608 void fprint(fp,str)
609 FILE *fp;
610 char *str;
611 {
612     int i, j, k, found, fbuf;
613     char c, save[21], fline[LINE+10];
614 
615     if (str==NULL) send(CRASH);
616     fbuf=0;
617 
618     for(i=0; str[i] != '\0'; i++) {
619 	c= str[i];
620 
621 	if (c=='\n' || (c==' ' && cursor>LINE) ||
622 	    (c=='\t' && cursor>LASTTAB)) {
623 	    fline[fbuf++]='\n';
624 	    flush_linebuf();
625 
626 	} else if (cursor>LINE) {
627 	    linebuf[buf]='\0';
628 	    /* Line would wrap, so we find a nice place to break it */
629 	    for (found=FALSE, j=buf-1, k=(buf>20 ? buf-20:0); j>=k; j--)
630 	      if (white(linebuf[j])) { found=TRUE; break; }
631 	    if (found) {
632 		strcpy(save,&linebuf[j+1]);
633 		linebuf[j++]='\n'; buf=j;
634 		flush_linebuf(); /* now buf==0, cursor==0 */
635 		for (k=0; save[k]!='\0'; k++) linebuf[buf++]=save[k];
636 	    } else { /* didn't find a whitespace */
637 		linebuf[buf++]='\n';
638 		flush_linebuf();
639 	    }
640 	    if (!trash(c)) { linebuf[buf++]= c; cursor++; }
641 
642 
643 	} else if (c=='\t' && TRANSLATE_TABS) {
644 	    do { linebuf[buf++]=' '; cursor++; } while (cursor%8 != 0);
645 
646 	} else if (!trash(c)) { /* KLUDGE: WHAT CTRL CHARS TO HANDLE?*/
647 	    linebuf[buf++]= c; cursor++;
648 	}
649     }
650 }
651 #endif
652 
653 
temp_print(simple_str,fancy_str)654 bool temp_print(simple_str,fancy_str)
655 char *simple_str, *fancy_str;
656 {
657     int i, chrs, pos;
658     char *str;
659 
660     /* If the last print command was not temp_print(), we must first get the
661        screen up to date... */
662     if (!temp_print_up) flush();
663 
664     if (term==TERM_UNKNOWN) str=simple_str; else str=fancy_str;
665 
666     /* NOTE: we don't need to fflush in here for the do_ functions, as they
667        in syscode fflush(out), and the curses functions update()... */
668 
669     if (nullstr(str)) { /* kill old temp print thing */
670 	if (temp_print_up) {
671 	    if (term==TERM_UNKNOWN) { lib_puts(out,"\n"); fflush(out); }
672 	    else do_cursor_left(FAR_LEFT,"");
673 	    temp_print_spaces=0;
674 	} /* else do nothing */
675 	return(TRUE);
676 
677     } else if (!temp_print_up) { /* new temp print thing */
678 	/* do_highlight does nothing if TERM_UNKNOWN */
679 	if (term==TERM_UNKNOWN) { lib_puts(out,str); fflush(out); }
680 	  else { do_highlight(TRUE); lib_puts(out," "); lib_puts(out,str);
681 		 do_highlight(FALSE); }
682 	temp_print_spaces=len(str);
683 	return(TRUE);
684 
685     } else { /* there is an existing temp print str, which we must deal with */
686 	if (term==TERM_UNKNOWN) {
687 	    if (temp_print_spaces==79)
688 	      { lib_puts(out,"\n"); temp_print_spaces=0; }
689 	    lib_puts(out,"."); fflush(out); temp_print_spaces++;
690 	} else {
691 	    do_highlight(TRUE); do_cursor_left(FAR_LEFT,"");
692 	    lib_puts(out," "); lib_puts(out,str); do_highlight(FALSE);
693 	}
694 	return(TRUE);
695     }
696 }
697 
698 
699 #ifdef JUST_TOO_HAIRY
700 	/* Optimization in here to allow reprinting only the changed part */
701 	/* just print a dot if TERM_UNKNOWN */
702 	_filter(str);
703 	for (i=0; str[i]!='\0'; i++) /* get rid of tabs and newlines */
704 	  if (str[i]=='\t'||str[i]=='\n') str[i]=' ';
705 	chrs= imaxf(len(str),LINE-cursor);
706 	str[chrs]='\0'; /* truncate str if it would wrap */
707 
708 	for (pos= -1, i=0; temp_print_str[i]!='\0' && str[i]!='\0'; i++)
709 	  if (temp_print_str[i]!=str[i]) { pos=i; break; } /* 1st difference */
710 	if (pos<0) return(TRUE); /* new and old things are identical */
711 	if (term==TERM_UNKNOWN) {
712 	    if (temp_print_spaces==79)
713 	      { lib_puts(out,"\n"); temp_print_spaces=0; }
714 	    lib_puts(out,"."); temp_print_spaces++; /* ignore temp_print_str */
715 	} else
716 	    if (temp_print_on_left && pos<chrs/2) do_cursor_left(FAR_LEFT,str);
717 #endif
718 
719 
do_hold(start,more_on)720 bool do_hold(start,more_on)
721 bool start, more_on;
722 {
723     if (start==TRUE && !holding) {
724 	flush_and_force_nl(TRUE);
725 	check_tty_lines();
726 	hold_count=0; holding++;
727 	more=more_on;
728 	printed_lines=0;
729 	more_break_pending=FALSE;
730     } else if (start==FALSE) {
731 	check_tty_lines();
732 	dump_held_lines();
733 	holding=0; more=FALSE;
734     } /* else start==MAYBE */
735     return(FALSE);
736 }
737 
738 
flush()739 void flush()
740 {
741     dump_held_lines();
742     flush_linebuf();
743 #ifdef HAVE_CURSES
744     if (curses) curses_flush();
745 #endif
746 }
747 
748 
space(n)749 void space(n)
750 int n;
751 { while(n-->0) print(" "); }
752 
753 
to_column(num)754 bool to_column(num)
755 int num;
756 {
757     if (num > LINE-1) { nl(); return(TRUE); }
758     if (num < cursor) return(FALSE);
759     while (cursor < num) { linebuf[buf++]=' '; cursor++; }
760     return(TRUE);
761 }
762 
763 
at_column()764 int at_column() { return(cursor); }
765 
766 
maybe_clear_screen()767 bool maybe_clear_screen()
768 {  flush_and_force_nl(FALSE);
769    if ((!scrollback || curses) && lib_clear_screen()) return(TRUE);
770    else return(FALSE);
771 }
772 
clear_screen()773 bool clear_screen() { flush_and_force_nl(FALSE);  return(lib_clear_screen()); }
774 
highlight(x)775 bool highlight(x) bool x; { flush(); return(do_highlight(x)); }
776 
do_more()777 void do_more() { flush_and_force_nl(TRUE); more_break_pending=TRUE; }
778 
779 
review_memory()780 void review_memory()
781 {
782     int i, old_photo, old_more, start, num, old_memory;
783 
784     if (memory_end==0 && !memory_wrapped) return;
785     if (more_break_pending) if (!really_do_more()) send(INTERRUPT);
786     flush_and_force_nl(TRUE);
787 
788     temp_logging(FALSE,&old_photo);
789     do_hold(TRUE,TRUE);
790     old_memory= memorize; memorize=FALSE;
791 
792     start= i= (memory_wrapped ? memory_end+1:0); /* start */
793     num=0;
794     while (TRUE) { /* find next group of lines */
795 	if (i==memory_end) break; /* end - was m_e-1 */
796 	if (i==MEMORY_LINES-1) i=0; else i++;
797 	num++;
798 	if (num==tty_lines-1)
799 	  { dump_memory_lines(start,num); start=i; num=0; }
800     }
801     dump_memory_lines(start,num);
802 
803     do_hold(FALSE,FALSE);
804     prev_logging(old_photo);
805     memorize= old_memory;
806 }
807 
808 
dump_memory_lines(start,num)809 void dump_memory_lines(start,num) /* internal */
810 int start, num;
811 {
812     int j;
813 
814     for (j=start; num>0; num--) {
815 	print(memory[j]);
816 	if (j==MEMORY_LINES-1) j=0; else j++;
817     }
818 }
819 
820 
821 /*** INPUT ROUTINES ***/
822 
redirect_input(fname,verbose)823 int redirect_input(fname,verbose)	/* fname=NULL to interrupt */
824 char *fname;
825 bool verbose;
826 {
827     bool retoin;
828     FILE *fp;
829 
830     if (fname==NULL) {
831 	if (redirs!=0 && verbose)
832 	  print("\n\n\t...Input file interrupted...\n");
833 	while (redirs>0) close_file(in_fp[redirs--]);
834 	return(TRUE);
835     }
836     if (redirs>=MAXREDIRECTS) {
837 	if (verbose) print("error: Too many open input files\n");
838 	return(FALSE);
839     }
840     run {
841 	fp=open_file(fname,READ);
842 	in_fp[++redirs]= fp;
843 	retoin=TRUE;
844     } except_when(CANTOPEN) retoin=FALSE;
845 
846     if (verbose) {
847 	if (!retoin) sf(ps,"error: Unable to open input file '%s'\n",fname);
848 	else sf(ps,"\n\t...Running commands from input file '%s'...\n",fname);
849 	pr();
850     }
851     return(retoin);
852 }
853 
854 
855 /* Input(): If the prompt wraps (over, say, the 75th column), everything
856    may become confused!  Str must be at least length+2 long to hold
857    "\n\0", however, the trailing '\n' is stripped from the returned
858    string, which is also despace()ed and filter()ed. */
859 
input(prompt,str,length)860 void input(prompt,str,length)
861 char *prompt, *str;
862 int length;
863 {
864     int l; bool eof;
865     more_break_pending= FALSE; eof= FALSE;
866 
867     if (redirs==0) { /* it's not redirected via redirect_input() */
868 	if (use_gnu_readline) {
869 	    flush();
870 	    eof=!do_gnu_readline(prompt,str,length);  /* see syscode.c */
871 	} else {
872 	    print(prompt); flush();
873 	    eof=!tty_gets(str, length);
874 	}
875 	if (eof) {
876 	    str[0]='\0';
877 	    if (interactive && ignore_eof) send(INTERRUPT);
878 	      else send(ENDOINPUT);
879 	} else {
880 	    _filter(str); despace(str);
881 	    if (use_gnu_readline) {
882 		mem_puts(prompt);
883 		if (logging) lib_puts(photo,prompt);
884 	    }
885 	    mem_puts(str); mem_puts("\n");
886 	    if (logging) { lib_puts(photo,str); lib_puts(photo,"\n"); }
887 	}
888 
889     } else { /* we have redirected input */
890 	print(prompt);
891 	if (!file_gets(in_fp[redirs], str, length)) {
892 	    /* if (cursor!=0) nl(); */
893 	    print("\n\t...end of input file...\n\n");  /* move to shell? */
894 	    close_file(in_fp[redirs--]);
895 	    input(prompt,str,length); /* try again */
896 	}
897 	_filter(str); despace(str);
898 	print(str); nl();
899     }
900 }
901 
902 
903 /* edit_line(): essentially the same rules as input() */
904 
edit_line(prompt,str,length,initial)905 bool edit_line(prompt,str,length,initial)
906 char *prompt, *str;
907 int length;
908 char *initial;
909 {
910     int l; bool eof;
911     more_break_pending= FALSE; eof= FALSE;
912 
913     if (interactive && redirs==0 && use_gnu_readline) {
914 	flush();
915 	eof= !do_gnu_edit(prompt,str,length,initial);
916 	if (eof) {
917 	    str[0]='\0';
918 	    if (ignore_eof) send(INTERRUPT); else send(ENDOINPUT);
919 	} else {
920 	    _filter(str); despace(str);
921 	    mem_puts(prompt); mem_puts(str); mem_puts("\n");
922 	    if (logging) {
923 		lib_puts(photo,prompt); lib_puts(photo,str);
924 		lib_puts(photo,"\n");
925 	    }
926 	}
927 
928     } else { /* we have redirected input */
929 	error("Unable to edit sequence from file input");
930     }
931 }
932 
933 
getln(prompt)934 void getln(prompt) /* ln is side-effected, is filtered, despaced, lowercased */
935 char *prompt;      /* may signal IOERROR or ENDOINPUT */
936 { ln=lnptr; input(prompt,ln,MAXLINE-2); lowercase(ln); }
937 
938 
939 /*** TEMPORARILY CHANGE TERMINAL I/O MODES ***/
940 
temp_logging(new,old)941 bool temp_logging(new,old)
942 bool new, *old;
943 { if (new && !log_open) return(FALSE);
944   flush(); if (cursor!=0) nl();
945   *old=logging; logging=new; return(TRUE); }
946 
prev_logging(old)947 void prev_logging(old)
948 int old;
949 { if (old && !log_open) send(CRASH);
950   flush(); if (cursor!=0) nl(); logging=old; }
951 
temp_more_mode(new,old)952 bool temp_more_mode(new,old)
953 int new, *old;
954 { flush(); if (cursor!=0) nl(); *old=more; more=new; return(TRUE); }
955 
prev_more_mode(old)956 void prev_more_mode(old)
957 int old;
958 { flush(); if (cursor!=0) nl(); more=old; }
959 
960 #ifdef OBSOLETE
961 
962 /* We did away with the redirecting_input flag, so what to do about this? */
temp_redirect_input(new,old)963 bool temp_redirect_input(new,old)
964 bool new, *old;
965 { *old= redirecting_input;
966   if (new && redirs==0) return(FALSE);
967   redirecting_input=new; return(TRUE); }
968 
prev_redirect_input(old)969 void prev_redirect_input(old)
970 int old;
971 { if (old && redirs==0) send(CRASH); else redirecting_input=old; }
972 
973 bool temp_redirect_input(); /* args redirect, *save_state; turn ALL input
974    redirection on/off. To turn on, redirection must already be enabled at
975    least once with redirect_input() (remember, redirects may happen "inside"
976    each other). Behavior is like temp_logging(). */
977 void prev_redirect_input();     /* args state; */
978 
979 #endif
980 
981 
io_init()982 void io_init()  /* this is run from lib_init() */
983 {
984 	int i;
985 
986 	array(ps,MAXLINE+3,char);
987 	array(ps_,MAXLINE+3,char);
988 	array(linebuf,LINE+3,char);
989 	array(ln,MAXLINE+3,char); lnptr= ln;
990 	array(ln_,MAXLINE+3,char);
991 
992 	parray(files,MAXFILES,FILE_INFO);
993 	for (i=0; i<MAXFILES; i++) {
994 	    array(files[i]->name,PATH_LENGTH+1,char);
995 	    files[i]->fp=NULL;
996 	    files[i]->modechar='?';
997 	}
998 
999 	in_file=files[0]->name; strcpy(in_file,"stdin");
1000 	in_modechar= files[0]->modechar= 'r';
1001 	array(in_fp,MAXREDIRECTS+1,FILE*); redirs= 0;
1002 	for (i=1; i<=MAXREDIRECTS; i++) { in_fp[i]=NULL; }
1003 	in_fp[0]= in= files[0]->fp= stdin;
1004 
1005 	out_file=files[1]->name; strcpy(out_file,"stdout");
1006 	out_modechar= files[1]->modechar= 'w';
1007 	out= files[1]->fp= stdout;
1008 
1009 	photo_file=files[2]->name; photo_modechar= files[2]->modechar= '?';
1010 
1011 	matrix(held,MAX_HOLD_LINES+1,LINE+3,char);
1012 	holding=FALSE; hold_count=0;
1013 	for (i=0; i<MAX_HOLD_LINES+1; i++) held[i][0]='\0';
1014 
1015 	matrix(memory,MEMORY_LINES,LINE+3,char);
1016 	memory_end=0; memory_wrapped=FALSE;
1017 	for (i=0; i<MEMORY_LINES; i++) memory[i][0]='\0';
1018 
1019 	supress_more= FALSE;
1020 	more_break_pending= FALSE;
1021 	memorize= TRUE;
1022 	cursor= 0;
1023 	printed_lines= 0;
1024 	lines_written= 0;
1025 	buf=0; linebuf[0]='\0';
1026 	holding= 0;
1027 	temp_print_spaces= 0;
1028 
1029 	/* These may all be changed by tty_init() or wimp_io_init() */
1030 	more= FALSE;
1031 	more_mode= DEFAULT_MORE_MODE;
1032 	logging= FALSE;
1033 	ignore_eof= TRUE;
1034 	interactive=TRUE;
1035 	use_gnu_readline= FALSE;
1036 	scrollback= FALSE;
1037 	curses= FALSE;
1038 	screen= TRUE;
1039 	have_drawn_top=FALSE;
1040 	tried_curses= FALSE;
1041 	tried_wimp= FALSE;
1042 	tried_split= FALSE;
1043 	tty_errors=0;
1044 	file_errors=0;
1045 	puts_errors=0;
1046 	tty_lines= 24;
1047 	term= TERM_UNKNOWN;
1048 	wimp= FALSE;
1049 }
1050