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