/* @(#)prepa2.c 17.1.1.1 (ESO-DMD) 01/25/02 17:37:37 */ /*=========================================================================== Copyright (C) 1995 European Southern Observatory (ESO) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning ESO-MIDAS should be addressed as follows: Internet e-mail: midas@eso.org Postal address: European Southern Observatory Data Management Division Karl-Schwarzschild-Strasse 2 D 85748 Garching bei Muenchen GERMANY ===========================================================================*/ /*+++++++++++++++ MIDAS monitor routines PREPA2 +++++++++++++++++++++++++ .LANGUAGE C .IDENTIFICATION Module PREPA2 .AUTHOR K. Banse ESO - Garching .KEYWORDS MIDAS monitor .ENVIRONMENT VMS and UNIX .PURPOSE synchronize fore- and background processes; get the MIDAS command string + take action accordingly .ALGORITHM loop on command input (from terminal or procedure file), test for background activity and synchronize system commands are executed directly, all others are executed as command procedures holds Parse3, TTSET, TTINIT, TTEDIT, TTPRO .VERSION 010627 last modif ------------------------------------------------------------------------*/ #define _POSIX_SOURCE 1 /* indicate that this is a POSIX program */ #define OLDTOKMAX 62 /* size of OLDTOKENs is fixed to 64 */ #include #include #include #ifndef SIGUSR1 #define SIGUSR1 30 #endif #ifndef SIGUSR2 #define SIGUSR2 31 #endif #include #include #include #define TW_import 0 #include #include #include #ifndef EOF #define EOF (-1) /* End of File */ #endif static char wstr[84]; static char blank = {' '}; #ifndef NO_READLINE #define HAVE_STRING_H #include #include #include #include #include /* MIDAS osx definitions */ #include /* Context extructure */ extern int is_a_tty; /* Is this a terminal, (yes=1) set in prepa.c */ static char contxt_name[MAX_CONTXT*8+2]; /* Activated contexts */ static char acknowledge; static char buffer[20]; /* command for xhelp */ static char *line_read; static char history_file[200]; /* FRONT.STARTUP+".history."+FRONT.DAZUNIT */ static sigjmp_buf env; /* Contains the line to push into readline. */ static char *push_to_readline = (char *)NULL; static Function *old_rl_startup_hook = (Function *) NULL; /* Push the contents of push_to_readline into the readline buffer. */ static void push_line () { if (push_to_readline) { rl_insert_text (push_to_readline); free (push_to_readline); push_to_readline = (char *)NULL; rl_startup_hook = old_rl_startup_hook; } } /* Call this to set the initial text for the next line to read from readline. */ int re_edit (line) char *line; { char * savestring(); if (push_to_readline) free (push_to_readline); push_to_readline = savestring (line); old_rl_startup_hook = rl_startup_hook; rl_startup_hook = (Function *)push_line; return (0); } struct COMMAND { char name[12]; /* User printable name of the function */ struct COMMAND *prev; }; struct CMD_LIST { struct COMMAND *curr; struct COMMAND *last; }; extern struct CMD_LIST cmds; static int xhelp_fd = 0; static int xhelp_pid = 0; static char *channame[2] = { (char *)NULL, (char *)NULL }; static int initialize_xhelp() /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Initialize channname, containing socket name and filename crated by XHelp.exe containing its pid. It opens a server connection. Initialize the contxt_name string to check for context changes. .RETURNS 0 OK, -1 otherwise. ------------------------------------------------------------*/ { char *mid_work, *dazunit; int n; mid_work = getenv("MID_WORK"); channame[1] = malloc(strlen(mid_work)+strlen("xhelp")+2+strlen("_pid")+1); dazunit = getenv("DAZUNIT"); (void) strcpy(channame[1],mid_work); (void) strcat(channame[1],"xhelp"); (void) strcat(channame[1],dazunit); (void) strcat(channame[1],"_pid"); n = strlen(channame[1]) - strlen("_pid"); channame[0] = malloc(n+1); (void) strncpy(channame[0],channame[1],n); channame[0][n] = '\0'; if (!xhelp_fd && (xhelp_fd = osxopen(channame,LOCAL|IPC_READ)) == -1) { (void) printf("\n\rCannot create server for GUI XHelp.exe.\n\r"); (void) printf("Error message: %s\n\r",osmsg()); xhelp_fd = 0; free(channame[0]); free(channame[1]); return(-1); } /* Initialize the context_name and buffer strings */ memset(contxt_name,' ',MAX_CONTXT*8); /* clear context names */ memset(buffer,'\0',20); /* Initiate command name */ contxt_name[MAX_CONTXT*8] = '\0'; /* make sure we have an end marker */ return(0); } void close_xhelp() /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Close socket and free allocated memory. This routine is called by prepa.c after the BYE command. .RETURNS void ------------------------------------------------------------*/ { if (xhelp_fd) { (void) osxclose(xhelp_fd); (void) free(channame[0]); (void) free(channame[1]); } } Function *gui_xhelp() /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE send messages to the GUI XHELP. .RETURNS nothing ------------------------------------------------------------*/ { int len; char *start_buffer; FILE *fd; /* retrieve the pid number from XHELP */ if (!xhelp_pid) { if ( (fd = fopen(channame[1],"r")) == (FILE *)NULL) { (void) printf("\n\rTry first: CREATE/GUI HELP \n\r"); rl_refresh_line(); return 0; } fscanf(fd,"%d\n",&xhelp_pid); fclose(fd); if (kill(xhelp_pid,0) == -1) { xhelp_pid = 0; unlink(channame[1]); (void) printf("\n\rTry first: CREATE/GUI HELP \n\r"); rl_refresh_line(); return 0; } } /* Prepare the message to be sent to XHELP not bigger than 12 chars */ /* Strip leading blanks */ start_buffer = rl_line_buffer; while ( *start_buffer == ' ') start_buffer++; len = strlen(start_buffer); len = (len > 19)? 19 : len; strncpy(buffer,start_buffer,len); buffer[len] = '\0'; if (buffer[len-1] == '/') buffer[len-1] = '\0'; /* Check first if CONTEXT list has changed, if so then send USR2 */ if (strcmp(contxt_name,CONTXT.NAME)) { (void) strcpy(contxt_name,CONTXT.NAME); /* (void) printf("sending SIGUSR2\n"); */ if (osssend(xhelp_pid,SIGUSR2) == -1) { xhelp_pid = 0; return 0; } while (osxinfo(xhelp_fd,0,0) == NOCONN) ; /* (void) printf("sending context=%s\n",contxt_name); */ if (osxwrite(xhelp_fd,contxt_name,MAX_CONTXT*8 + 2) != MAX_CONTXT*8 + 2) if (osxwrite(xhelp_fd,contxt_name,MAX_CONTXT*8 + 2) != MAX_CONTXT*8 + 2) { xhelp_pid = 0; return 0; } acknowledge = 0; if (osxread(xhelp_fd,&acknowledge,1) != 1 && acknowledge != 1) { xhelp_pid = 0; return 0; } } /* * Send the message to XHELP , and then interrupt it with USR1 signal. * If error, the connection is broken. */ /* printf("sending SIGUSR1\n"); */ if (osssend(xhelp_pid,SIGUSR1) == -1) { xhelp_pid = 0; return 0; } /* printf("osxinfo=%d\n",osxinfo(xhelp_fd,0,0)); */ while (osxinfo(xhelp_fd,0,0) == NOCONN) ; /* printf("sending command=%s\n",buffer); */ if (osxwrite(xhelp_fd,buffer,20) != 20 ) if (osxwrite(xhelp_fd,buffer,20) != 20 ) { xhelp_pid = 0; return 0; } acknowledge = FALSE; if (osxread(xhelp_fd,&acknowledge,1) != 1 && acknowledge != 1) { xhelp_pid = 0; return 0; } return 0; } /* Tell the GNU Readline library how to complete. We want to try to complete * on command names if this is the first word in the line, or on filenames * if not. */ static void initialize_readline() { char **fileman_completion(); extern Keymap _rl_keymap; if (!initialize_xhelp()) { rl_add_funmap_entry("gui-xhelp",gui_xhelp); rl_bind_key('\030',gui_xhelp); /* Bind CTR-X */ rl_set_key ("\\e[11~",gui_xhelp, _rl_keymap); /* F1 on Xtectronix and PC */ rl_set_key ("\\ep\r",gui_xhelp, _rl_keymap); /* F1 on HPs */ rl_set_key ("\\e[[A",gui_xhelp, _rl_keymap); /* F1 on Digital PC */ } /* Allow conditional parsing of the ~/.inputrc file */ rl_readline_name = "MIDAS"; /* Tell the completer that we want a crack first */ rl_attempted_completion_function = (CPPFunction *)fileman_completion; if (!is_a_tty) rl_outstream = fopen("/dev/null","w"); } /* */ void fileman_ignore(matches) char **matches; { int i; char *ptr; if (!matches) return; for ( i = 0; matches[i]; i++) if ( (ptr = strchr(matches[i], '.')) != (char *)NULL) *ptr = '\0'; } /* Attempt to complete on the contents of TEXT. START and END show the * region of TEXT that contains the word to complete. We can use the entire * line in case we want to do some simple parsing. Return the array of matches, * or NULL if there aren't any. */ char **fileman_completion(text, start, end) char *text; int start, end; { char **matches; char *command_generator(); char *filename_completion_function(); char *curr_dir, *match_dir, *dest_dir; char *start_buffer; matches = (char **)NULL; match_dir = (char *)NULL; /* If this word is at the start of the line, then it is a command to complete * Otherwise it is the name of a file in the current directory. */ start_buffer = rl_line_buffer; while ( *start_buffer == ' ') { start_buffer++; start--; } if (start == 0) matches = completion_matches(text,command_generator); else if (!strncasecmp(start_buffer,"SHOW/COMM",9) || !strncasecmp(start_buffer,"HELP ",5) || !strncasecmp(start_buffer,"DELETE/COMM",11) || !strncasecmp(start_buffer,"CREATE/COMM",11)) matches = completion_matches(text,command_generator); else if (!strncasecmp(start_buffer,"@ " ,2)) match_dir = getenv("MID_PROC"); else if (!strncasecmp(start_buffer,"@a ",3)) match_dir = getenv("APP_PROC"); else if (!strncasecmp(start_buffer,"@s ",3)) match_dir = getenv("STD_PROC"); else if (!strncasecmp(start_buffer,"@c ",3)) match_dir = getenv("CON_PROC"); else if (!strncasecmp(start_buffer,"LOAD/LUT ",9) || !strncasecmp(start_buffer,"LOAD/ITT ",9)) match_dir = getenv("MID_SYSTAB"); else if (!strncasecmp(start_buffer,"SET/CONT",8) || !strncasecmp(start_buffer,"CLEAR/CONT",10) || !strncasecmp(start_buffer,"SHOW/CONT",10)) match_dir = getenv("MID_CONTEXT"); else if (!strncasecmp(start_buffer,"CREATE/GUI ",11)) match_dir = getenv("GUI_EXE"); if (match_dir != (char *)NULL) { dest_dir = malloc(strlen(match_dir)+1); (void) strcpy(dest_dir,match_dir); oshgetcwd(&curr_dir); oshchdir(dest_dir); matches = completion_matches(text,filename_completion_function); oshchdir(curr_dir); free(dest_dir); } if (!strncasecmp(start_buffer,"SET/CONT",8) || !strncasecmp(start_buffer,"CLEAR/CONT",10) || !strncasecmp(start_buffer,"CREATE/GUI ",11)) fileman_ignore(matches); return(matches); } /* */ /* * Generator fucntion for command completion. STATE lets us know weather * to start from scratch; without any state (i.e. STATE =0), then we start * at the top of the list */ char *command_generator(text, state) char *text; int state; { static int len; static char mytext[12]; static char *ptr_text; register struct COMMAND *mycmd; register char *slash_ptr; register int len_cmd, max_len_cmd, len_qua; /* * If this is a new word to complete, initialize now. This includes * saving the length of TEXT for efficiency, an initializing the index * variable to 0 */ if (MONIT.CMD_LIST_UPDA == 1) /* commands have been modified... */ update_cmd_list(); if (!state) { len = strlen(text); if ((slash_ptr = (char *)strchr(text,'/')) != (char *)NULL) { len_cmd = (long)slash_ptr - (long)text; max_len_cmd = (len_cmd > 6)? 6 : len_cmd; (void) strncpy(mytext,text,max_len_cmd); mytext[max_len_cmd] = '/'; mytext[max_len_cmd+1] = '\0'; len_qua = strlen(&text[len_cmd+1]); (void) strncat(mytext,&text[len_cmd+1],(len_qua > 4)? 4 : len_qua); len = strlen(mytext); ptr_text= mytext; } else { len = (len > 6)? 6 : len; (void) strncpy(mytext,text,len); mytext[len] = '\0'; ptr_text = text; } cmds.curr = cmds.last; } /* Return the next name which partially matches from the command list. */ while ((mycmd = cmds.curr) != (struct COMMAND *)NULL) { cmds.curr = mycmd->prev; if (strncasecmp(mycmd->name,ptr_text,len) == 0) return((char *)strcpy(malloc(strlen(mycmd->name)+1),mycmd->name)); } /* If no names matched, then return NULL */ return((char *)NULL); } #ifdef __STDC__ static void TTPRO_alarm(int sig) #else static void TTPRO_alarm(sig) /*++++++++++++++++ .PURPOSE Function called at end of time-out .RETURNS Within ostread function. Internal use only. ---------*/ int sig; #endif { siglongjmp(env,1); /* return with the saved signal mask from sigsetjmp */ } #endif /* NO_READLINE */ /* */ int Parse3(start) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE clean input line .ALGORITHM .RETURNS return no. of tokens on line ---------------------------------------------------------------------*/ int start; { int lstart, lsave; int n, count, substi, kk, nn, mm; char wbuf[MAX_TOKEN]; register int nr; register char sngc; /* extract all tokens + get their length, also use OLD_TOKEN */ lstart = start; substi = 0; for (n=0; n<10; n++) { TOKEN[n].LEN = CGN_EXTRSS(LINE.STR,LINE.LEN,blank,&lstart,TOKEN[n].STR,MAX_TOKEN); if (TOKEN[n].LEN <= 0) /* end of parsing reached... */ { if (TOKEN[n].LEN == -2) /* overflow of single token */ { ERRORS.SYS = 7; PREPERR("MIDAS",LINE.STR,TOKEN[n].STR); return (-1); } count = n ; for (nr=count; nr<10; nr++) { TOKEN[nr].STR[0] = '?'; /* init empty TOKEN + LTOKEN */ TOKEN[nr].STR[1] = '\0' ; TOKEN[nr].LEN = 1 ; } goto end_of_it; } /* test for interactive input with "." */ if ( (TOKEN[n].STR[0] == '.') && (TOKEN[n].LEN == 1) ) { TOKEN[n].LEN = CGN_COPY(TOKEN[n].STR,OLDTOKEN[n].STR); substi = 1 ; /* it's like a substitution */ } else { sngc = TOKEN[n].STR[OLDTOKMAX]; /* save char. */ TOKEN[n].STR[OLDTOKMAX] = '\0'; /* so, no overrun... */ OLDTOKEN[n].LEN = CGN_COPY(OLDTOKEN[n].STR,TOKEN[n].STR); TOKEN[n].STR[OLDTOKMAX] = sngc; } } count = 10 ; /* test, if more than 10 tokens on line */ if (LINE.LEN >= lstart) { for (nr=lstart; nr 0) { if ((lsave+nn+1) > MAX_TOKEN) { kk = 1; mm = MAX_TOKEN - nn; } else { TOKEN[9].STR[nn++] = ' '; mm = lsave; } strncpy(&TOKEN[9].STR[nn],wbuf,mm); nn += mm; if (kk == 1) break; } } TOKEN[9].STR[nn] = '\0' ; TOKEN[9].LEN = nn; } end_of_it: if (substi > 0) /* rebuild input line */ { int indx; indx = 0; for (nr=0; nr= MAX_LINE) { ERRORS.SYS = 7; PREPERR("MIDAS",LINE.STR," "); return (-1); } } LINE.STR[--indx] = '\0'; /* last blank -> \0 */ LINE.LEN = indx; } return (count); } /* */ void TTSET(flg) int flg; /* IN: 0 = clear, 1 = set terminal, 2 = byebye */ { if (TERM.FLAG == 1) { if (flg < 2) ostraw(flg); /* clear/set terminal settings */ else { CloseTerm(); /* reset terminal completely */ #if vms ospwait(1); #else #endif } } } /* */ void TTINIT(hdr) /*++++++++++++++++++++++++++++++++++++++++++++++++++ .KEYWORDS terminal, window .PURPOSE manage the windows .ALGORITHM open foreground window only .RETURNS nnothing --------------------------------------------------*/ int hdr; /* IN: header flag = 0 (no) or = 1 (yes) */ { int n, status; /* TERM.FLAG = 0/1 for TermWindow use */ if (TERM.FLAG == 1) { /* (device,termcapfile,control_chars */ status = OpenTerm((char *) 0,(char *) 0,TW_cc_VMS); if (status != OK) TERM.FLAG = 0; else { TERM.FLAG = 1; if (hdr == 0) { ClearScreen(); CursorTo(5,0); } } } #ifndef NO_READLINE else { (void) strncpy(history_file,FRONT.STARTUP,80); (void) strcat(history_file,"midtemp"); (void) strncat(history_file,FRONT.DAZUNIT,2); (void) strcat(history_file,".prg"); using_history(); read_history(history_file); initialize_readline (); /* Bind our completer */ } #endif if (hdr == 0) { if (FRONT.TITLE[0] == 'F') { (void) strcpy(wstr," FrontEnd to MIDAS "); } else { (void) strcpy(wstr," ESO-MIDAS version "); (void) strcat(wstr,&FRONT.TITLE[1]); (void) strcat(wstr," on "); FRONT.SYSTEM[19] = '\0'; (void) strcat(wstr,FRONT.SYSTEM); FRONT.SYSTEM[19] = ' '; } (void) printf("\n\n\r%s\n\r",wstr); } if (TERM.FLAG == 1) { tinfo(&n,&TERM.LINES); /* get new size of terminal window */ n = TERM.LINES - 1; CursorTo(n,0); /* move cursor to lower left corner */ } else TERM.LINES = 24; } /* */ int TTEDIT(line,len) /*++++++++++++++++++++++++++++++++++++++++++++++++++ .KEYWORDS terminal, window .PURPOSE edit a command line .ALGORITHM use FO's window interfaces .RETURNS status --------------------------------------------------*/ char *line; /* IN/OUT: line to be edited (terminated by \0) */ int len; /* IN: max. length of 'line' */ { int status, n; #ifndef NO_READLINE struct sigaction act, oact; #endif if (TERM.FLAG != 0) { tinfo(&n,&TERM.LINES); /* get new size of terminal window */ n = TERM.LINES - 1; CursorTo(n,0); /* move cursor to lower left corner */ status = tv_kmods(TERM.EDITMODE,line,len,strlen(line)); if (status != OK) return (-1); NewLine(); } else { #ifndef NO_READLINE line_read = (char *)NULL; act.sa_handler = TTPRO_alarm; sigemptyset(&act.sa_mask); act.sa_flags = 0; (void) sigaction(SIGALRM,&act,&oact); if ( sigsetjmp(env,1) == 0 ) { re_edit(line); /* line_read = readline((char *)NULL); */ line_read = readline(""); if (line_read && *line_read) { /* add_history(line_read); */ (void) strcpy(line,line_read); free(line_read); } else line[0] = '\0'; } (void) sigaction(SIGALRM,&oact,&act); #else printf("%s\n\r",line); #endif } return (0); } /* */ void TTPRO(prompt,line,len) /*++++++++++++++++++++++++++++++++++++++++++++++++++ .KEYWORDS terminal, window .PURPOSE prompt for + get an input line from windows .ALGORITHM use FO's window interfaces .RETURNS Nothing --------------------------------------------------*/ char *prompt; /* IN: prompt string (terminated by \0) */ char *line; /* OUT: input line (terminated by \0) */ int len; /* IN: length of buffer 'line' */ { int n; #ifndef NO_READLINE struct sigaction act, oact; #endif TERM.TIMEOUT = 0; if (TERM.FLAG == 0) { #ifndef NO_READLINE line_read = (char *)NULL; act.sa_handler = TTPRO_alarm; sigemptyset(&act.sa_mask); act.sa_flags = 0; (void) sigaction(SIGALRM,&act,&oact); if ( sigsetjmp(env,1) == 0 ) { if (is_a_tty) line_read = readline(prompt); else line_read = readline(""); if (line_read) { if (*line_read) { /* add_history(line_read); */ (void) strcpy(line,line_read); free(line_read); } else line[0] = '\0'; } else (void) strcpy(line,"bye"); } (void) sigaction(SIGALRM,&oact,&act); #else printf("%s ",prompt); CGN_GETLINE(line,len); #endif } else { #if vms NewLine(); #endif Put(prompt); n = tv_kmods(TERM.EDITMODE,line,len,0); /* interrupt routine `intermail' sets TERM.TIMEOUT to 2 */ if (TERM.TIMEOUT == 0) { if (n == EOF) (void) strcpy(line,"BYE"); NewLine(); } } } /* */ void tinfo(tcols,tlines) int *tcols, *tlines; { int status; struct termstatus tstat; if (TERM.FLAG != 1) { *tcols = 80; *tlines = 24; } else { status = ostinfo(&tstat); /* get no. of columns in window/terminal */ if (status == 0) { if (tstat.columns > 8) { *tcols = tstat.columns; if (*tcols > 127) *tcols = 127; /* respect size of record... */ } else *tcols = 80; /* default to 80 char window */ if (tstat.lines > 2) *tlines = tstat.lines; else *tlines = 24; /* default to 24 lines window */ } } }