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_LIB
15 #define INC_MISC
16 #define INC_HELP_DEFS
17 #include "system.h"
18 #include "shell.h"
19 #include "table.h"
20 
21 char *com, *args;       /* available as globals to the command procedures */
22 char *uncrunched_args;
23 int com_num, num_args;
24 char *((*prompt)());
25 char *default_prompt();
26 bool (*quit_save_hook)();
27 bool more_mode;
28 
29 /***** defs for internal use only *****/
30 #define UNHELPFUL	(-1)        /* as a help_entry */
31 #define HELPLESS	((long) -1) /* as a help_key */
32 #define MAX_SUCCESSIVE_FAILURES 15
33 
34 #define MAX_NUM_MENUS    20
35 #define MAX_MENU_ENTRIES 20
36 #define MENU_ENTRY_LEN   20
37 
38 COMMAND **cmd;		/* [command#] => ptr to a COMMAND struct */
39 MENU **menu;            /* [menu#] => ptr to a menu struct */
40 int num_menus;
41 int command_num; 	/* num commands in cmd */
42 int help_entries;	/* number of entries in help_file */
43 char **topic_name;   	/* [topic_num] set by mktopic() */
44 int *topic_code;   	/* [topic_num] set by mktopic() */
45 long *topic_help_key;   /* [topic_num] set by mktopic() */
46 char **tokens;		/* [token_num] used as a temp by the parser */
47 char **remaining;  	/* ditto */
48 bool *matched; 		/* [command_num] parser temp */
49 int num_matched;
50 bool wizard_mode;
51 char *save_args_ptr;    /* the arg list, saved so the ptr can be bashed */
52 char *save_uncrunched_args;
53 TABLE *cmd_history;
54 int autosave;           /* saves state on exit if TRUE */
55 FILE *help_file;        /* file that contains all the help summaries */
56 bool user_is_amused;    /* two state vars for keep_user_amused */
57 
58 bool shell_uses_wimp_cmds, help_uses_wimp_help;
59 bool inhibit_menus;
60 /* If shell_uses_wimp_cmds==TRUE, a command entered to the prompt will
61 invoke its WIMP version, if one is available, otherwise its normal
62 text version will be run. help_uses_wimp_help is similar. The WIMP
63 code should only try to run a command when inhibit_menus==FALSE. */
64 
65 int cmd_history_num;
66 bool photo_update_top_hook;
67 void (*photo_banner_hook)();
68 char *the_program, *the_version, *the_copyright;
69 
70 int alias_match();
71 int abbrev_match();
72 int com_matches();
73 int abbrev_matches();
74 int try_to_match();
75 
76 void expand_history_references();
77 char *centering();
78 
79 #define NO_HELP_FILE "\
80 Can't find help file - detailed help information is not available.\n\
81 See installation instructions for details.\n"
82 
83 #define NO_HELP_KEY "No additional information available.\n"
84 
85 #define SURROGATE_ABOUT "\
86 This program is freely redistributable under certain conditions and is\n\
87 licensed free of charge for non-commercial applications. No warranty of any\n\
88 type is provided. See the License Agreement for details.\n"
89 
90 #define TYPE_HELP_PLEASE "\
91 Type 'help' for help.\n\
92 Type 'about' for license, non-warranty, and support information.\n"
93 
banner()94 void banner()
95 {
96     char line[81];
97 
98 print(
99  "************************************************************************\n");
100 print(
101  "* Welcome to:                                                          *\n");
102 
103 print(centering("",FALSE));
104 print(centering(the_program,FALSE));
105 if (!nullstr(the_version))
106   { sf(line,"(version %s)",the_version); print(centering(line,FALSE)); }
107 print(centering("",FALSE));
108 sf(line,"Copyright %s, Whitehead Institute for Biomedical Research",
109    the_copyright); print(centering(line,FALSE));
110 if (gnu_copyright(line)) print(centering(line,TRUE));
111 
112 print(
113  "************************************************************************\n");
114 
115 nl();
116 print(TYPE_HELP_PLEASE);
117 if (help_file==NULL) print(NO_HELP_FILE);
118 }
119 
120 
get_version(version_filename)121 char *get_version(version_filename)
122 char *version_filename;
123 {
124     FILE *fp;
125     char str[TOKLEN+1], *p, *version, name[PATH_LENGTH+1];
126 
127     if (nullstr(version_filename)) return(NULL);
128     fp=NULL; version=NULL;
129 
130     run {
131 	strcpy(name,version_filename);
132 	if (!make_filename_in_dir(name,FORCE_EXTENSION,WRS(".v"),
133 				  FORCE_DIR,CODE_DIR)) send(CANTOPEN);
134 	fp=open_file(name,READ);
135 	finput(fp,ln_,MAXLINE); p=ln_; /* don't hack &ln_ */
136 	close_file(fp);
137 	if (stoken(&p,sREQUIRED,str)) version=mkstrcpy(str);
138     } when_aborting {
139 	close_file(fp); /* Don't bother relaying messages */
140     }
141     return(version);
142 }
143 
144 
photo_banner()145 void photo_banner()
146 {
147     char ver[81];
148 /*                                         Thu Apr 13 05:31:11 EDT 1989
149  123456789012345678901234567890123456789012345678901234567890123456789012 */
150 lib_puts(photo,
151  "************************************************************************\n");
152 sf(ps,"* Output from:                                %24s *\n",time_string());
153 lib_puts(photo,ps);
154 
155 lib_puts(photo,centering("",FALSE));
156 lib_puts(photo,centering(the_program,FALSE));
157 sf(ver,"(version %s)",the_version); lib_puts(photo,centering(ver,FALSE));
158 lib_puts(photo,centering("",FALSE));
159 lib_puts(photo,
160  "************************************************************************\n");
161 lib_puts(photo,"\n");
162 if (photo_banner_hook!=NULL) (*photo_banner_hook)(photo);
163 }
164 
165 
166 int prev_spaces=2;
167 
centering(str,lineup)168 char *centering(str,lineup)  /* into global ps */
169 char *str;
170 bool lineup;
171 {
172     int spaces, i;
173     if (!lineup) spaces= 36-(len(str)/2); else spaces=prev_spaces;
174     prev_spaces= spaces;
175     strcpy(ps,
176 "*                                                                      *\n");
177     for (i=0; str[i]!='\0'; i++) ps[spaces+i]=str[i];
178     return(ps);
179 }
180 
181 
shell_init(program,version,copyright,help_filename)182 void shell_init(program,version,copyright,help_filename)
183 char *program, *version, *copyright, *help_filename;
184 /* help_filename must be side-effectable */
185 {
186     int i;
187     char *full_name[PATH_LENGTH+1];
188 
189     array(cmd, MAX_COMMANDS, COMMAND*);
190     array(matched, MAX_COMMANDS, bool);
191     array(topic_name, MAX_COM_TOPICS+1, char*);
192     array(topic_code, MAX_COM_TOPICS+1, int);
193     array(topic_help_key, MAX_COM_TOPICS+1, long);
194     matrix(tokens, MAX_COM_TOKENS, TOKLEN+1, char);
195     array(remaining, MAX_COM_TOKENS+1, char*);
196     array(args, ARGS_LEN+1, char);
197     array(uncrunched_args, ARGS_LEN+1, char);
198     save_args_ptr= args; save_uncrunched_args= uncrunched_args;
199 
200     the_program= mkstrcpy(program);
201     the_version= mkstrcpy(version);
202     the_copyright= mkstrcpy(copyright);
203 
204     for (i=1; i<MAX_COM_TOPICS+1; i++)
205       { topic_name[i]=NULL; topic_code[i]=0; topic_help_key[i]=HELPLESS; }
206     topic_name[0]= mkstrcpy("TOPIC ZERO");
207     topic_code[0]=0; topic_help_key[0]=HELPLESS;
208 
209     cmd_history= allocate_table(21,250,CANT_EXPAND,INDEX_BY_NUMBER);
210     cmd_history_num= next_entry_number(cmd_history);
211 
212     command_num=0;
213     help_entries=0;
214     wizard_mode=FALSE;
215     photo_update_top_hook=TRUE;
216     photo_banner_hook=NULL;
217     prompt=default_prompt;
218     inhibit_menus=TRUE;
219     quit_save_hook=NULL;
220     user_is_amused=FALSE;
221 
222     parray(menu, MAX_NUM_MENUS, MENU);
223     for (i=0; i<MAX_NUM_MENUS; i++)
224       array(menu[i]->title,MENU_ENTRY_LEN+1,char);
225     shell_uses_wimp_cmds= FALSE;
226     help_uses_wimp_help=  FALSE;
227 
228     help_file=NULL;
229     if (make_filename_in_dir(help_filename,FORCE_EXTENSION,WRS(HELP_EXT),
230 			     DEFAULT_DIR,CODE_DIR)) {
231 	run help_file=open_file(help_filename,READ);
232 	  except_when(CANTOPEN) {}
233     }
234 }
235 
236 
default_prompt(s)237 char *default_prompt(s)
238 char *s;
239 { sf(s,"\n%d> ",cmd_history_num+1); return(s); }
240 
241 
mktopic(num,nam,code,description_index)242 void mktopic(num,nam,code,description_index)
243 int num;
244 char *nam;
245 int code;
246 long description_index;
247 {
248     if (num<=0 || num>MAX_COM_TOPICS || len(nam)>MAX_TOPIC_LEN) send(CRASH);
249     topic_name[num]=mkstrcpy(nam); topic_code[num]=code;
250     topic_help_key[num]= description_index;
251 }
252 
253 
mkcommand(name,abbrev,func,code)254 int mkcommand(name,abbrev,func,code)
255 char *name, *abbrev;
256 void (*func)();
257 int code;
258 {
259     char *str, **toks, c;
260     int i, n_tokens;
261 
262     single(cmd[command_num], COMMAND);
263     str= mkstrcpy(name); crunch(str); truncstr(str,MAX_COM_NAME_LEN);
264     cmd[command_num]->name= mkstrcpy(str); /* str itself => toks, below */
265 
266     array(toks,MAX_COM_TOKENS,char*);
267     for (i=0; i<MAX_COM_TOKENS; i++) toks[i]=NULL;
268     toks[0]= str;
269 
270     for (i=0, n_tokens=1; str[i]!='\0'; i++)
271       if ((c=str[i])==' ' || c=='-' || c=='_') {
272 	  str[i]= '\0';
273 	  if (n_tokens<MAX_COM_TOKENS) toks[n_tokens++]= &str[i+1];
274 	  else { cmd[command_num]->name[i]= '\0'; break; }
275       }
276 
277     cmd[command_num]->tokens= toks;
278     cmd[command_num]->num_tokens= n_tokens;
279     cmd[command_num]->procedure= func;
280     cmd[command_num]->code= code;
281 
282     nstrcpy(cmd[command_num]->abbreviation,abbrev,MAX_ABBREV_LEN);
283     crunch(cmd[command_num]->abbreviation);
284 
285     cmd[command_num]->topic= 0;
286     cmd[command_num]->help_key= HELPLESS;
287     cmd[command_num]->help_entry= UNHELPFUL;
288     cmd[command_num]->cmd_help= NULL;
289     cmd[command_num]->args_help=NULL;
290     cmd[command_num]->num_args= -1;
291     cmd[command_num]->num_args_prefix= 0;
292 
293     cmd[command_num]->wimp_procedure= NULL;
294     cmd[command_num]->status_function= NULL;
295     cmd[command_num]->wimp_help= NULL;
296     cmd[command_num]->wimp_menu_num= -1;
297     cmd[command_num]->menu_entry= NULL;
298     cmd[command_num]->wimp_shortcut= '\0';
299 
300     return(command_num++);
301 }
302 
303 
mkhelp(cmd_name,abbrev,description_index,num_args_prefix,num_args,code,topic,description,arguments,defaults)304 void mkhelp(cmd_name,abbrev,description_index,num_args_prefix,num_args,
305 	    code,topic,description,arguments,defaults)
306 char *cmd_name, *abbrev;
307 long description_index;
308 int num_args_prefix, num_args;
309 int topic, code;
310 char *description, *arguments, *defaults;
311 {
312     int i, old_wiz;
313     char *rest;
314 
315     old_wiz=wizard_mode; wizard_mode=TRUE;
316     i=parser(cmd_name,&rest,TRUE);
317     wizard_mode= old_wiz;
318 
319     if (i<0 || !nullstr(rest)) {
320 	if (code!=HLP) {
321 	    sf(ps,"warning: attempt to make help for non-command '%s'\n",
322 	       cmd_name); pr();
323 	    return;
324 	} else i=mkcommand(cmd_name,abbrev,NULL,HLP);
325     }
326 
327     if (!streq(cmd[i]->name,cmd_name)) {
328 	sf(ps,"warning: names disagree for command '%s'\n",cmd_name);
329 	pr();
330     }
331     if (code!=cmd[i]->code) {
332 	sf(ps,"warning: type codes disagree for command '%s'\n",cmd_name);
333 	pr();
334     }
335     if (!streq(cmd[i]->abbreviation,abbrev)) {
336 	sf(ps,"warning: abbreviations disagree for command '%s'\n",cmd_name);
337 	pr();
338     }
339 
340     array(cmd[i]->cmd_help, MAX_COM_HELP_LEN+1,char);
341     array(cmd[i]->def_help, MAX_ARG_HELP_LEN+1,char);
342     array(cmd[i]->args_help,MAX_ARG_HELP_LEN+1,char);
343 
344     nstrcpy(cmd[i]->cmd_help,description,MAX_COM_HELP_LEN);
345     nstrcpy(cmd[i]->args_help,arguments,MAX_ARG_HELP_LEN);
346     nstrcpy(cmd[i]->def_help,defaults,MAX_ARG_HELP_LEN);
347 
348     cmd[i]->code=  code;
349     cmd[i]->topic= topic;
350     cmd[i]->help_key= description_index;
351     cmd[i]->help_entry= help_entries;
352     cmd[i]->num_args= num_args;
353     cmd[i]->num_args_prefix= num_args_prefix;
354 
355     ++help_entries;
356 }
357 
358 
valid_name(str)359 bool valid_name(str) /* checks the syntax of names */
360 char *str;
361 {
362   int i;  char *token;
363 
364   if (!is_a_token(str)) return(FALSE);
365   if (strin(NAME_TAG_CHARS,str[0])) str++;
366   if (!strin(NAME_FIRST_CHARS,str[0])) return(FALSE);
367   for(i=1; str[i]!='\0'; i++) if (!strin(NAME_CHARS,str[i])) return(FALSE);
368   return(TRUE);
369 }
370 
371 
null_command()372 void null_command() { send(CRASH); }
373 
374 
375 
376 /**************************** The Command Parser ****************************/
377 
parser(line,rest,help_ok)378 int parser(line,rest,help_ok)
379 char *line;
380 char **rest; /* side-effected */
381 bool help_ok;
382 {
383     int i, j, n_tokens, last_match;
384     char *foo;
385     extern char **tokens, **remaining; /* temps */
386     extern int *matched, num_matched;
387 
388     for (i=0; i<command_num; i++) matched[i]= FALSE;
389     num_matched= 0;
390     if (rest==(char**)NULL) rest= &foo;
391 
392     remaining[0]= line;
393     for (n_tokens=0; n_tokens<MAX_COM_TOKENS; n_tokens++)
394       if (stoken(&line,sREQUIRED,tokens[n_tokens])) {
395 	  remaining[n_tokens+1]= line;
396 	  if (remaining[n_tokens+1][0]==' ')
397 	    remaining[n_tokens+1]+=1;
398       } else break;
399 
400     for (i=0; i<command_num; i++) /* try to exactly match an abbreviation */
401       if (allowed_cmd(i,help_ok) && xstreq(tokens[0],cmd[i]->abbreviation))
402 	{ matched[i]=TRUE; num_matched=1; *rest=remaining[1]; return(i); }
403 
404     try_to_match(tokens,n_tokens,3,3,matched,&num_matched,&last_match,help_ok);
405     if (num_matched==1) { *rest=remaining[3]; return(last_match); }
406 
407     try_to_match(tokens,n_tokens,2,2,matched,&num_matched,&last_match,help_ok);
408     if (num_matched==1) { *rest=remaining[2]; return(last_match); }
409     try_to_match(tokens,n_tokens,2,3,matched,&num_matched,&last_match,help_ok);
410     if (num_matched==1) { *rest=remaining[2]; return(last_match); }
411 
412     try_to_match(tokens,n_tokens,1,1,matched,&num_matched,&last_match,help_ok);
413     if (num_matched==1) { *rest=remaining[1]; return(last_match); }
414     try_to_match(tokens,n_tokens,1,2,matched,&num_matched,&last_match,help_ok);
415     if (num_matched==1) { *rest=remaining[1]; return(last_match); }
416     try_to_match(tokens,n_tokens,1,3,matched,&num_matched,&last_match,help_ok);
417     if (num_matched==1) { *rest=remaining[1]; return(last_match); }
418 
419     *rest= remaining[0];
420     return(-1);
421 }
422 
423 
print_parser_results(rest,help_ok)424 void print_parser_results(rest,help_ok) /* uses the global state */
425 char *rest;
426 bool help_ok;
427 {
428     int j;
429 
430     if (num_matched==1) {
431 	for (j=0; j<command_num; j++)
432 	  if (matched[j]) sf(ps,"matched command '%s'\n",cmd[j]->name);
433 	pr();
434 
435     } else if (num_matched>1) {
436 	sf(ps,"ambiguous command%s: could be any of the following:\n",
437 	   (help_ok ? " or help topic":"")); pr();
438 	for (j=0; j<command_num; j++)
439 	  if (matched[j]) { print("\t"); print(cmd[j]->name); nl();
440 	}
441 
442     } else { /* num_matched==0 */
443 	sf(ps,"unrecognized command%s: '",(help_ok ? " or help topic":""));
444 	pr();
445 	if (len(rest)>LINE-25) strcpy(&rest[LINE-28],"...");
446 	print(rest); print("'\n");
447     }
448 }
449 
450 
451 /* Try to match n_to_try the input tokens against all n_word_commands, and
452    set com_match, n_matched and last_match accordingly. Return TRUE only if a
453    unique match was found. */
454 
try_to_match(token,n_tokens,n_to_try,n_word_command,com_match,n_matched,last_match,allow_help_only_stuff)455 int try_to_match(token,n_tokens,n_to_try,n_word_command,
456 	com_match,n_matched,last_match,allow_help_only_stuff)
457 char **token;
458 int n_tokens, n_to_try, n_word_command;
459 int *com_match, *n_matched, *last_match, allow_help_only_stuff;
460 {
461 	int exact, i;
462 
463 	if (n_tokens<n_to_try) return(FALSE);
464 	for (i=0; i<command_num; i++)
465 	    if (allowed_cmd(i,allow_help_only_stuff) &&
466 		cmd[i]->num_tokens==n_word_command &&
467 		com_matches(token,cmd[i]->tokens,n_to_try,&exact)) {
468 		        *last_match= i;
469 			if (!com_match[i])
470 				{ com_match[i]= TRUE; ++*n_matched; }
471 			if (exact && n_to_try==n_word_command) return(TRUE);
472 		}
473 	if (*n_matched==1) return(TRUE); else return(FALSE);
474 }
475 
476 
com_matches(in_tokens,com_tokens,num_to_match,exact)477 int com_matches(in_tokens,com_tokens,num_to_match,exact)
478 char **in_tokens, **com_tokens;
479 int num_to_match, *exact;
480 {
481     int i,j;
482 
483     for (i=0, *exact=TRUE; i<num_to_match; i++)
484       if (!matches(in_tokens[i],com_tokens[i])) return(FALSE);
485       else if (istrlen(in_tokens[i])!=istrlen(com_tokens[i])) *exact=FALSE;
486 
487     return(TRUE);
488 }
489 
490 
491 /********************************* The Shell *********************************/
492 
493 
494 #define HIST_RANGE "command history number is out of range"
495 
expand_history_references(line)496 void expand_history_references(line) /* sends an error if need be */
497 char *line; /* line IS side-effected */
498 {
499     char *str, *save;
500     int num, first, foo, i;
501 
502     /* The cmd_history num will NOT have been incremented between the call
503        to prompt and here! Also remember that 1+ that number is printed. */
504 
505     save=line;
506     if (!itoken(&line,iREQUIRED,&num)) return;
507     for (i=0; save+i!=line; i++) save[i]=' ';
508 
509     if (get_numbered_entry(num-1,&str,cmd_history)) {
510 	/* maxstrins(line," ",MAXLINE); */
511 	maxstrins(line,str,MAXLINE);
512 	despace(line);
513 	print("="); if (cmd_history_num>9) print("=");
514 	if (cmd_history_num>99) print("="); print("> ");
515 	print(line); nl();
516 	expand_history_references(line);
517 
518     } else {
519 	/* THIS IS A KLUDGE, and results in a 'statement not reached' error. */
520 	for(Te=cmd_history->list; Te!=NULL; Te=Te->next)
521 	  { first=Te->id.num; break; } /*NOTREACHED*/
522 	if (cmd_history_num>1)
523 	  sf(ps,"%s\nUse a number from %d to %d.",HIST_RANGE,
524 	     first+1,cmd_history_num);
525 	else if (cmd_history_num==1)
526 	  sf(ps,"%s\nThe only valid command number yet is 1.",HIST_RANGE);
527 	else if (cmd_history_num==0)
528 	  sf(ps,"%s\nNo commands have yet been entered.",HIST_RANGE);
529 	error(ps); return; /* never returns from here */
530     }
531 }
532 
533 
534 #define nopunt  failures++; break;
535 #define punt    done=TRUE;  break;
536 #define toleft  if (cursor!=0) nl();
537 
command_loop()538 void command_loop()
539 {
540     char *rest;
541     int done, failures, foo, prev_lvl= -1;
542     void (*func)();
543 
544     done=FALSE; failures=0;
545     do {
546 	/* run { */
547 	if ((msg=setjmp(stk[lvl_plus_plus()]))==0) {
548 	    do {
549 		if (prev_lvl<0) prev_lvl=lvl;
550 		  else if (prev_lvl!=lvl) {
551 		      fprintf(stderr,"Internal Error: stack level changed\n");
552 		      prev_lvl= lvl;
553 		  }
554 		if (log_open) fflush(photo);
555 
556 		inhibit_menus= FALSE;
557 		cmd_history_num= next_entry_number(cmd_history);
558 		uncrunched_args= save_uncrunched_args;
559 		input((*prompt)(ps),uncrunched_args,ARGS_LEN-1);
560 		despace(uncrunched_args); /* should already be filtered */
561 
562 		if (!nullstr(uncrunched_args)) {
563 		    expand_history_references(uncrunched_args);
564 		    put_numbered_entry(uncrunched_args,cmd_history,&foo);
565 
566 		    if ((com_num=parser(uncrunched_args,&rest,FALSE))>=0) {
567 			uncrunched_args=rest; args=save_args_ptr;
568 			strcpy(args,uncrunched_args); lowercase(args);
569 			if (wimp && shell_uses_wimp_cmds &&
570 			    cmd[com_num]->wimp_procedure!=NULL)
571 		          func=cmd[com_num]->wimp_procedure;
572 			else func=cmd[com_num]->procedure;
573 			if (func==NULL) send(CRASH);
574 			com=cmd[com_num]->name;
575 			num_args=0; inhibit_menus=TRUE;
576 			(*func)(); /* Run the command */
577 			failures=0;
578 		    } else print_parser_results(rest,FALSE); /* error msg */
579 		}
580 	    } while(--lvl>10000);
581 
582 	    /* } except { */
583 	} else switch(msg) {
584 	    when INTERRUPT:
585 	    	print("\n*** break ***\n");
586 	    	if (redirecting_input) {
587 		    print("\n\t...run cancelled...\n");
588 		    redirect_input(NULL,FALSE);
589 		}
590 	    	nopunt;
591 
592 	    when NOMEMORY:   print("\n*** out of memory ***\n"); nopunt;
593 	    when MATHERROR:  print("\n*** floating point error ***"); nopunt;
594 	    when ENDOINPUT:  toleft; print("\n\t...end of input...\n\n"); punt;
595 	    when QUIT:       toleft; print("\n\t...goodbye...\n\n"); punt;
596 	    when SOFTABORT:  nopunt;
597 
598 	    when IOERROR:
599 	    when CRASH:
600 	    when SYSERROR:
601 	    when CANTOPEN:
602 	    when ENDOFILE:
603 	    default:	      verbose_untrapped_msg(); nopunt;
604 	}
605 
606 	if (failures>MAX_SUCCESSIVE_FAILURES) {
607 	    fprintf(stderr,"\n*** too many successive errors: aborting ***\n");
608 	    done= TRUE;
609 	}
610     } while(!done);
611 }
612 
613 
614 
615 /************************ Useful things for commands ************************/
616 
617 
abort_command()618 void abort_command() { send(SOFTABORT); }
619 
620 
error(errmsg)621 void error(errmsg) /* guaranteed not to use ps */
622 char *errmsg;
623 { print("error: "); print(errmsg); nl(); abort_command(); }
624 
625 
maybe_set_bool(var)626 void maybe_set_bool(var)
627 bool *var;
628 {
629     char temp[TOKLEN+1];
630 
631     if (stoken(&args,sREQUIRED,temp)) {
632         if (streq(temp,"on")) *var= TRUE;
633 	else if(streq(temp,"off")) *var=FALSE;
634 	else set_usage_error("either 'on' or 'off'");
635     }
636     sf(ps,"'%s' is %s.\n",com, *var ? "on" : "off"); pr();
637 }
638 
639 
maybe_set_real(var,lbound,hbound,fmt)640 void maybe_set_real(var,lbound,hbound,fmt)
641 real *var, lbound, hbound;
642 real fmt;
643 {
644     real temp;
645 
646     if (!nullstr(args)) {
647 	if (!rtoken(&args,rREQUIRED,&temp) || !rrange(&temp,lbound,hbound)) {
648 	    sf(ps,"a real number from %s to %s",rs(fmt,lbound),rs(fmt,hbound));
649 	    set_usage_error(ps);
650 	} else *var= temp;
651     }
652     sf(ps,"'%s' is %s\n",com,rs(fmt,*var)); pr();
653 }
654 
655 
maybe_set_long(var,lbound,hbound)656 void maybe_set_long(var,lbound,hbound)
657 long *var, lbound, hbound;
658 {
659 	long temp;
660 
661 	if (!nullstr(args)) {
662 	    if (!ltoken(&args,lREQUIRED,&temp) ||
663 		!lrange(&temp,lbound,hbound)) {
664 		   sf(ps,"an integer number from %ld to %ld",lbound,hbound);
665 		   set_usage_error(ps);
666 	    } else *var= temp;
667 	}
668 	sf(ps,"'%s' is %ld\n",com,*var); pr();
669 }
670 
671 
maybe_set_int(var,lbound,hbound)672 void maybe_set_int(var,lbound,hbound)
673 int *var, lbound, hbound;
674 {
675 	int temp;
676 
677 	if (!nullstr(args)) {
678 	    if (!itoken(&args,iREQUIRED,&temp) ||
679 		!irange(&temp,lbound,hbound)) {
680 		   sf(ps,"an integer number from %d to %d",lbound,hbound);
681 		   set_usage_error(ps);
682 	    } else *var= temp;
683 	}
684 	sf(ps,"'%s' is %d\n",com,*var); pr();
685 }
686 
687 
set_usage_error(com_args)688 void set_usage_error(com_args) /* guaranteed not to use ps */
689 char *com_args;
690 { print("error: illegal value for '"); print(com); print("'\n");
691   if (cmd[com_num]->args_help!=NULL)
692     { print("correct value: "); print(cmd[com_num]->args_help); print("\n"); }
693   print("type '"); print(com);
694   print("' alone to display current value\n");
695   print("type 'help "); print(com); print("' for details\n");
696   abort_command();
697 }
698 
699 
usage_error(num)700 void usage_error(num)
701 int num; /* num args given, maybe <0 */
702 {
703     if (cmd[com_num]->num_args==0)
704       sf(ps,"error: The '%s' command takes no arguments.\n",com);
705     else
706       sf(ps,"error: Missing%s argument(s) for the '%s' command.\n",
707 	 (num!=0 ? " or invalid":""),com);
708     pr();
709 
710     if (cmd[com_num]->num_args!=0) {
711 	if (!nullstr(cmd[com_num]->args_help))
712 	  { sf(ps,"expected:  %s\n",cmd[com_num]->args_help); pr(); }
713 	if (!nullstr(cmd[com_num]->def_help))
714 	  { sf(ps,"default%s %s\n",(cmd[com_num]->num_args==1 ? ": ":"s:"),
715 	       cmd[com_num]->def_help); pr(); }
716     }
717 
718     if (cmd[com_num]->help_key!=HELPLESS)
719       { sf(ps,"type 'help %s' for details.\n",com); pr(); }
720 
721     abort_command();
722 }
723 
724 
725 #define TOOMANY \
726 "error: Too many arguments for the '%s' command\n(%s%d argument%s expected).\n"
727 
nomore_args(n)728 void nomore_args(n)
729 int n; /* for now, a dummy arg */
730 {
731     int mode, num;
732     num= cmd[com_num]->num_args;
733     mode=cmd[com_num]->num_args_prefix;
734     if (nullstr(args)) return; /* all is OK, otherwise... */
735 
736     if (num==0)
737       sf(ps,"error: The '%s' command takes no arguments.\n",com);
738     else if (num>0)
739       sf(ps,TOOMANY,com,(mode==EXACTLY ? "" : "up to "),num,maybe_s(num));
740     else /* num<0 */ sf(ps,"error: Too many arguments.\n");
741     pr();
742 
743     if (!nullstr(cmd[com_num]->args_help))
744       { print("expected: "); print(cmd[com_num]->args_help); nl(); }
745 
746     if (!nullstr(cmd[com_num]->def_help))
747       { sf(ps,"default%s: %s\n",maybe_s(num),cmd[com_num]->args_help); pr(); }
748 
749     if (cmd[com_num]->help_key!=HELPLESS)
750       { sf(ps,"Type 'help %s' for details.\n",com); pr(); }
751 
752     abort_command();
753 }
754 
755 
more_args(num)756 void more_args(num)
757 int num; /* num args given, maybe <0 */
758 {
759     int mode, want;
760     want= cmd[com_num]->num_args;
761     mode=cmd[com_num]->num_args_prefix;
762 
763     if (!nullstr(args)) usage_error(-1); /* instead */
764 
765     sf(ps,"error: Missing argument%s.\n",maybe_s(want-num)); pr();
766     sf(ps,"The '%s' command requires %s%d argument%s.\n",com,
767        mode==EXACTLY ? "" : "at least ",want,maybe_s(want)); pr();
768 
769     if (!nullstr(cmd[com_num]->args_help))
770       { print("expected: "); print(cmd[com_num]->args_help); nl(); }
771 
772     if (!nullstr(cmd[com_num]->def_help))
773       { sf(ps,"default%s: %s\n",maybe_s(num),cmd[com_num]->args_help); pr(); }
774 
775     if (cmd[com_num]->help_key!=HELPLESS)
776       { sf(ps,"Type 'help %s' for details.\n",com); pr(); }
777 
778     abort_command();
779 }
780 
781 
input_error(val,def)782 void input_error(val,def)
783 char *val, *def;
784 {
785     print("error: you have given an invalid response\n");
786     if (!nullstr(val))
787       { print("expected: "); print(val); nl(); }
788     if (!nullstr(def))
789       { print("default:  "); print(def); nl(); }
790 
791   if (cmd[com_num]->help_key!=HELPLESS)
792     { sf(ps,"try typing 'help %s' for details\n",com); pr(); }
793 
794   abort_command();
795 }
796 
797 
798 #define EXTRA_INPUT   "too many values given in input line"
799 
expect_nomore_input(str,mode,num)800 void expect_nomore_input(str,mode,num)
801 char *str;
802 int mode, num;
803 { if (nullstr(str)) return;
804   else if (num>0)
805     sf(ps,"error: %s\n %s %d value%s were expected\n",EXTRA_INPUT,
806        (mode==EXACTLY ? "only" : "up to"),
807        num,maybe_s(num));
808   else sf(ps,"%s\n",EXTRA_INPUT);
809   pr(); print("try 'help "); print(com); print("' for details\n");
810   abort_command();
811 }
812 
813 
expect_more_input(str,mode,num)814 void expect_more_input(str,mode,num)
815 char *str;
816 int mode, num;
817 { if (!nullstr(str)) return;
818   else if (num>0)
819     sf(ps,"error: missing input\n%s %d value%s expected\n",
820        (mode==EXACTLY ? "":"at least"),num,(num>1 ? "s were":" was"));
821   else sf(ps,"error: missing input\n");
822   pr(); print("try 'help "); print(com); print("' for details\n");
823   abort_command();
824 }
825 
826 
split_arglist(rest,divider)827 bool split_arglist(rest,divider)
828 char **rest;
829 char divider; /* character that arglist should be split on */
830 {
831     int i, j;
832 
833     *rest=NULL; i=0;
834     for (i=0; args[i]!='\0'; i++)
835       if (args[i]==divider) {
836 	  j=i+1; while (white(args[j])) j++; *rest=args+j;
837 	  i--;   while (white(args[i]) && i>0) i--;  args[i+1]='\0';
838 	  return(TRUE);
839       }
840     return(FALSE);
841 }
842 
843 
split_uncrunched_args(rest,divider)844 bool split_uncrunched_args(rest,divider)
845 char **rest;
846 char divider; /* character that arglist should be split on */
847 {
848     int i, j;
849 
850     *rest=NULL; i=0;
851     for (i=0; uncrunched_args[i]!='\0'; i++)
852       if (uncrunched_args[i]==divider) {
853 	  j=i+1; while (white(uncrunched_args[j])) j++;
854 	  *rest=uncrunched_args+j;
855 	  i--;   while (white(uncrunched_args[i]) && i>0) i--;
856 	  uncrunched_args[i+1]='\0';
857 	  return(TRUE);
858       }
859     return(FALSE);
860 }
861 
862 
maybe_ok(str)863 void maybe_ok(str)
864 char *str;
865 {
866     if (update_top()) {
867 	if (photo!=NULL) lib_puts(photo,str);
868 	lib_puts(out,"ok");
869     } else print(str);
870     nl();
871 }
872 
873 
keep_user_amused(thing,iter,max_iter)874 void keep_user_amused(thing,iter,max_iter)
875 char *thing; /* nullstr(str) means we are done, len<<TOKLEN, like one word */
876 int iter, max_iter;
877 {
878     char simple[TOKLEN+1], fancy[TOKLEN+1];
879 
880     if (iter==0) {
881 	usertime(TRUE);
882 	/* only need to do the simples the first time */
883 	if (max_iter==0) sf(simple,"computing %s%s",thing,"s");
884 	  else sf(simple,"computing %d %s%s",max_iter,thing,maybe_s(max_iter));
885     } else strcpy(simple,"foo");
886 
887     if (max_iter==0) sf(fancy,"%s %d",thing,iter);
888       else sf(fancy,"%s %d of %d",thing,iter,max_iter);
889 
890     if (iter==0 || usertime(FALSE)>1.0)
891       { temp_print(simple,fancy); usertime(TRUE); }
892 }
893 
894 
895 /**************************** Some Basic Commands ****************************/
896 
show_cmd_history()897 command show_cmd_history()
898 {
899     int i, num_to_print, first_to_print, printed_any;
900     char *cmd_str;
901 
902     get_one_arg(itoken,1000,&num_to_print);
903     if (num_to_print<1) usage_error(1);
904 
905     printed_any=FALSE;
906     first_to_print= imaxf(cmd_history_num-num_to_print,0);
907     for(Te=cmd_history->list; Te!=NULL; Te=Te->next) {
908 	i=Te->id.num;  cmd_str=Te->string;
909 	if (i>=first_to_print && i<cmd_history_num) {
910 	    if(!printed_any){print("Previous commands:\n"); printed_any=TRUE;}
911 	    sf(ps,"%3d  ",i+1); pr(); print(cmd_str); nl();
912 	}
913     }
914     if (!printed_any) print("No commands have yet been entered.\n");
915 }
916 
917 
quit()918 command quit()
919 {
920     char token[TOKLEN+1];
921 
922     if (interactive && !redirecting_input) {
923 	/* test auto_save && data_loaded */
924 	if (quit_save_hook==NULL || !(*quit_save_hook)(FALSE)) send(QUIT);
925 	run getln("save data before quitting? [yes] ");
926 	except {
927 	    when ENDOINPUT: ln[0]='y'; break;
928 	    when INTERRUPT: abort_command();
929 	    default: 	    relay;
930 	}
931 	if (!stoken(&ln,sREQUIRED,token) || !matches(token,"no")) {
932 	    if (!((*quit_save_hook)(TRUE))) return; /* if saving fails */
933 	      else send(QUIT);
934 	} else send(QUIT);
935     } else {
936 	if (quit_save_hook!=NULL && !((*quit_save_hook)(TRUE))) return;
937 	send(QUIT);
938     }
939 }
940 
941 
really_quit()942 command really_quit()
943 { send(QUIT); }
944 
945 
run_from_file()946 command run_from_file()
947 {
948     char file_name[PATH_LENGTH+1];
949 
950     use_uncrunched_args();
951     get_one_arg(stoken,sREQUIRED,file_name);
952 
953     if (!make_filename(file_name,DEFAULT_EXTENSION,WRS("in")))
954       error("bad input file name");
955     else redirect_input(file_name,TRUE); /* verbose -> messages */
956 }
957 
958 
do_photo()959 command do_photo()
960 {
961     char file_name[TOKLEN+1];
962 
963     use_uncrunched_args();
964     get_one_arg(stoken,"",file_name);
965 
966     if (streq(file_name,"off")) {
967 	photo_to_file("","");
968 	sf(ps,"'%s' is off.\n",com); pr();
969 	return;
970 
971     } else if (streq(file_name,"on")) {
972 	usage_error(1);
973 
974     } else if (!nullstr(file_name)) {
975 	if (!make_filename(file_name,DEFAULT_EXTENSION,WRS("out")))
976 	  sf(ps,"error: Bad photo file name");
977 	if (!photo_to_file(file_name,"a"))
978 	  sf(ps,"error: Unable to open photo file '%s'\n",file_name);
979 	else {
980 	    photo_banner();
981 	    if (photo_update_top_hook && update_top()) sf(ps,"ok\n");
982 	    else sf(ps,"'%s' is on: file is '%s'\n",com,photo_file);
983 	}
984 	pr(); return;
985 
986     } else { /* no args */
987 	if (log_open) {
988 	    fflush(photo);
989 	    sf(ps,"'%s' is on: file is '%s'\n",com,photo_file);
990 	} else sf(ps,"'%s' is off\n",com);
991 	pr(); return;
992     }
993 }
994 
995 
set_more()996 command set_more()   { maybe_set_bool(&more); }
set_wizard()997 command set_wizard() { maybe_set_bool(&wizard_mode); }
set_verbose_mem()998 command set_verbose_mem() { maybe_set_bool(&verbose_mem); }
999 
1000 
1001 #define s_or_space_colon (cmd[i]->num_args==1 ? ": ":"s:")
1002 #define NOT_A_TOPIC "There is no help topic number %d.\n"
1003 #define MOREHELP0 \
1004 "Type 'help' for a list of commands and topics.\n"
1005 #define MOREHELP1 \
1006 "Type 'help <topic-number>' for more information about a particular topic.\n"
1007 #define MOREHELP2 \
1008 "Type 'help <command-name>' for more help with a particular command.\n"
1009 #define HELP_LEFT       26
1010 #define HELP_FORMAT     "%-26s.%-52s\n"
1011 #define HELP_DIVIDER \
1012 "=============================================================================\
1013 \n"
1014 
help()1015 command help()
1016 {
1017     int j, i, k, n;
1018     char *name=get_temp_string(), *rest=NULL, *str=get_temp_string();
1019     bool got_any, got_it;
1020 
1021     crunch(args);
1022     maybe_clear_screen();
1023 
1024     hold (more_mode) {
1025 
1026 	if (nullstr(args)) { /**** Print Help Table ****/
1027 	    print(HELP_DIVIDER);
1028 	    sf(ps,"%s Commands and Options:\n",the_program); pr();
1029 	    for (j=1; j<MAX_COM_TOPICS; j++) { /* NOT j<=MAX_COM_TOPICS! */
1030 		if (nullstr(topic_name[j]) || wizard_only(topic_code[j]))
1031 		  continue;
1032 		strcpy(name,topic_name[j]); uppercase(name);
1033 		sf(ps,"\n(%d) %s:\n",j,name); pr();
1034 		for (n=0; n<help_entries; n++) {
1035 		    for (i=0, got_it=FALSE; i<command_num; i++) {
1036 			if (cmd[i]->help_entry!=n) continue;
1037 			if (cmd[i]->topic!=j) break;
1038 			if (!allowed_cmd(i,TRUE)) break;
1039 			got_it=TRUE; break;
1040 		    }
1041 		    if (!got_it) continue; /* for n */
1042 
1043 		    if (!nullstr(cmd[i]->abbreviation))
1044 		      sf(str,"%s (%s)",cmd[i]->name,cmd[i]->abbreviation);
1045 		    else sf(str,"%s",cmd[i]->name,"");
1046 		    if (!nullstr(cmd[i]->cmd_help)) {
1047 			for (k=len(str); k<HELP_LEFT; k++) str[k]='.';
1048 			str[HELP_LEFT]='\0';
1049 			sf(ps,HELP_FORMAT,str,cmd[i]->cmd_help); pr();
1050 		    } else { /* no cmd_help */
1051 			print(str); nl();
1052 		    }
1053 		}
1054 	    }
1055 	    for (i=0, got_any=FALSE; i<command_num; i++) {
1056 		if (!allowed_cmd(i,TRUE)) continue;
1057 		if (cmd[i]->help_entry==UNHELPFUL) {
1058 		    if (!got_any)
1059 		      { print("\nOTHER COMMANDS:\n"); got_any=TRUE; }
1060 		    if (!nullstr(cmd[i]->abbreviation))
1061 		      sf(ps,"%s (%s)\n",cmd[i]->name,cmd[i]->abbreviation);
1062 		    else sf(ps,"%s\n",cmd[i]->name,"");
1063 		    pr();
1064 		}
1065 	    }
1066 	    nl();
1067 	    print(MOREHELP1); print(MOREHELP2);
1068 	    if (help_file==NULL) { print("\nNote: "); print(NO_HELP_FILE); }
1069 	    print(HELP_DIVIDER);
1070 
1071 	} else if (itoken(&args,iREQUIRED,&i)) { /**** a topic num ****/
1072 	    if (i<=0 || i>MAX_COM_TOPICS || nullstr(topic_name[i])) {
1073 		unhold(); sf(ps,NOT_A_TOPIC,i); pr();
1074 		print(MOREHELP0); print(MOREHELP1); print(MOREHELP2);
1075 	    } else {
1076 		print(HELP_DIVIDER);
1077 		strcpy(name,topic_name[i]); uppercase(name);
1078 		sf(ps,"(%d) %s\n\n",i,name); pr();
1079 		if (help_file==NULL) print(NO_HELP_FILE);
1080 		else if (topic_help_key[i]==HELPLESS) print(NO_HELP_KEY);
1081 		else {
1082 		    fgoto_line(help_file,topic_help_key[i]);
1083 		    fgetln_(help_file);
1084 		    got_any=FALSE;
1085 		    while (ln[0]!='@') {
1086 			got_any=TRUE;
1087 			print(ln); nl();
1088 			fgetln_(help_file);
1089 		    }
1090 		    if (!got_any) print(NO_HELP_KEY);
1091 		}
1092 		print(HELP_DIVIDER);
1093 	    }
1094 
1095 	} else { /**** a command name? ****/
1096 	    if ((i=parser(args,&rest,TRUE))<0) {
1097 		unhold();
1098 		print_parser_results(rest,TRUE); /* not a command */
1099 		print(MOREHELP0); print(MOREHELP1); print(MOREHELP2);
1100 	    } else {
1101 		/* print out short help */
1102 		print(HELP_DIVIDER);
1103 		nstrcpy(name,cmd[i]->name,MAX_COM_NAME_LEN); uppercase(name);
1104 		if (isa_option(cmd[i]->code))         strcpy(str,"Option");
1105 		else if (isa_parameter(cmd[i]->code)) strcpy(str,"Parameter");
1106 		else if (!help_only(cmd[i]->code))    strcpy(str,"Command");
1107 		else                                 strcpy(str,"Information");
1108 		if (!nullstr(cmd[i]->abbreviation))
1109 		  sf(ps,"%s %s (abbreviation: '%s')\n",name,str,
1110 		     cmd[i]->abbreviation);
1111 		  else sf(ps,"%s %s\n",name,str);
1112 		pr(); nl();
1113 
1114 		if (!nullstr(cmd[i]->cmd_help))
1115 		  { sf(ps,"Description: %s\n",cmd[i]->cmd_help); pr(); }
1116 		if (cmd[i]->topic!=0)
1117 		  { sf(ps,"Help Topic:  (%d) %s\n",cmd[i]->topic,
1118 		       topic_name[cmd[i]->topic]); pr(); }
1119 		if (cmd[i]->num_args==0) print("No Arguments\n");
1120 		else if (!nullstr(cmd[i]->args_help)) {
1121 		    sf(ps,"Argument%s   %s\n",s_or_space_colon,
1122 		       cmd[i]->args_help); pr();
1123 		    if (!nullstr(cmd[i]->def_help)) {
1124 			sf(ps,"Default%s    %s\n",s_or_space_colon,
1125 			   cmd[i]->def_help); pr();
1126 		    }
1127 		}
1128 
1129 		nl();
1130 		if (help_file==NULL) print(NO_HELP_FILE);
1131 		else if (cmd[i]->help_key==HELPLESS) print(NO_HELP_KEY);
1132 		else {
1133 		    fgoto_line(help_file,cmd[i]->help_key);
1134 		    fgetln_(help_file);
1135 		    got_any=FALSE;
1136 		    while (ln[0]!='@') {
1137 			got_any=TRUE;
1138 			print(ln); nl();
1139 			fgetln_(help_file);
1140 		    }
1141 		    if (!got_any) print(NO_HELP_KEY);
1142 		}
1143 		print(HELP_DIVIDER);
1144 	    }
1145 	}
1146     } /* hold */
1147 }
1148 
1149 
about()1150 command about()
1151 {
1152     int n;
1153 
1154     print(HELP_DIVIDER);
1155     sf(ps,"%s version %s, Copyright %s Whitehead Institute\n",
1156        the_program,the_version,the_copyright); pr();
1157     if (gnu_copyright(ps)) { pr(); nl(); }
1158     print("All rights reserved.\n"); nl();
1159 
1160     n=cmd[com_num]->help_key;
1161     if (help_file==NULL)
1162       { print(NO_HELP_FILE); nl(); print(SURROGATE_ABOUT); }
1163     else if (n==HELPLESS)
1164       { print(NO_HELP_KEY); nl(); print(SURROGATE_ABOUT); }
1165     else {
1166 	fgoto_line(help_file,n);
1167 	fgetln_(help_file);
1168 	while (ln[0]!='@') {
1169 	    print(ln); nl();
1170 	    fgetln_(help_file);
1171 	}
1172     }
1173     nl(); print(MOREHELP0); print(MOREHELP1); print(MOREHELP2);
1174     print(HELP_DIVIDER);
1175 }
1176 
1177 
review_output()1178 command review_output() { review_memory(); print("ok\n"); }
1179 
show_time()1180 command show_time()
1181 {
1182     char *str;
1183 
1184     if (!nullstr(str=time_string())) {
1185 	print(str); nl();
1186     } else print("Time to go home...\n");
1187 }
1188 
1189 
cd_command()1190 command cd_command()
1191 {
1192     char dir_name[PATH_LENGTH+1];
1193 
1194     use_uncrunched_args();
1195     get_one_arg(stoken,"",dir_name);
1196 
1197     if (nullstr(dir_name)) {
1198 	if (get_directory(dir_name))
1199 	  { sf(ps,"The current directory is '%s'\n",dir_name); pr(); }
1200 	else error("Can't get current directory name");
1201 
1202     } else { /* !nullstr(dir_name) */
1203 	if (change_directory(dir_name)) {
1204 	    if (get_directory(dir_name)) {
1205 		sf(ps,"The current directory is now '%s'\n",dir_name); pr();
1206 	    } else print("ok\n");
1207 	} else {
1208 	    sf(ps,"Can't change to directory '%s'",dir_name);
1209 	    error(ps);
1210 	}
1211     }
1212 }
1213 
1214 
system_command()1215 command system_command()
1216 {
1217     if (!nullstr(args)) { /* run shell command */
1218 	if (shell_command(args)) {
1219 	    if (curses)
1220 	      print("\n        ...System Output Omitted...\n");
1221 	    else if (logging)
1222 	      lib_puts(photo,"\n        ...System Output Omitted...\n");
1223 	    print("\nOK\n");
1224 	} else {
1225 	    sf(ps,"Unable to run command '%s'",truncstr(args,40));
1226 	    error(ps);
1227 	}
1228 
1229     } else { /* run a subshell */
1230 	print(SHELL_MESSAGE); nl(); flush(); /* for curses */
1231 	if (subshell()) {
1232 	    if (curses)
1233 	      print("        ...System Interaction Omitted...\n");
1234 	    else if (logging)
1235 	      lib_puts(photo,"        ...System Interaction Omitted...\n");
1236 	    lib_puts(out,"\n");  /* C shell does not print one on ctrl-D */
1237 	    print("\nBack in "); print(the_program); print("...\n");
1238 	} else error("Unable to run subshell\n");
1239     }
1240 }
1241 
1242 
1243 #define GIMME_A_COMMENT \
1244 "Enter your comment. End it with a period ('.') on a line by itself.\n"
1245 
comment()1246 command comment()
1247 {
1248     if (nullstr(args)) {
1249 	print(GIMME_A_COMMENT);
1250 	do { getln(""); despace(ln); } while(!streq(ln,"."));
1251     } else print("ok\n");
1252 }
1253 
1254 
1255 
1256 
1257 /********** WIMP (Windows, Icons, Mouse, and Pointers) Support **********/
1258 
1259 #ifdef NOT_PUBLIC
wimp_start()1260 void wimp_start() /* Call from main() */
1261 {
1262 #ifdef HAVE_WIMP
1263     if (wimp) do_wimp_start(menus,num_menus);
1264 #endif
1265 }
1266 
1267 
mkwimp(name,menu_entry,menu_num,wimp_function,status_function,wimp_help_function,shortcut)1268 void mkwimp(name,menu_entry,menu_num,wimp_function,status_function,
1269 	  wimp_help_function,shortcut)
1270 char *name, *menu_entry;
1271 int menu_num;
1272 void (*wimp_function)(), (*status_function)(), (*wimp_help_function)();
1273 char shortcut;
1274 {
1275     int i;
1276 
1277     if ((i=parser(name,(char**)NULL,TRUE))<0) send(CRASH);
1278 
1279     cmd[i]->menu_entry= mkstrcpy(menu_entry);
1280     cmd[i]->wimp_menu_num= menu_num;
1281     cmd[i]->wimp_procedure= wimp_function;
1282     cmd[i]->status_function= status_function;
1283     cmd[i]->wimp_help= wimp_help_function;
1284     cmd[i]->wimp_shortcut= shortcut;
1285 
1286     if (menu_num>=MAX_NUM_MENUS || nullstr(menu[menu_num]->title) ||
1287 	menu[menu_num]->num_entries>=MAX_MENU_ENTRIES) send(CRASH);
1288     menu[menu_num]->entry[(menu[menu_num]->num_entries)++]= cmd[i];
1289 }
1290 
1291 
mkmenu(num,title)1292 void mkmenu(num,title)
1293 int num;
1294 char *title;
1295 {
1296     if (num>=MAX_NUM_MENUS) send(CRASH);
1297     menu[num]->title=mkstrcpy(title);
1298 }
1299 
1300 
1301 COMMAND the_divider;
1302 
mkdivider(menu_num)1303 void mkdivider(menu_num)
1304 int menu_num;
1305 {
1306     if (menu_num>=MAX_NUM_MENUS || nullstr(menu[menu_num]->title) ||
1307 	menu[menu_num]->num_entries>=MAX_MENU_ENTRIES) send(CRASH);
1308     menu[menu_num]->entry[(menu[menu_num]->num_entries)++]= &the_divider;
1309 }
1310 #endif
1311