diff options
Diffstat (limited to 'teco.c')
-rw-r--r-- | teco.c | 1888 |
1 files changed, 1888 insertions, 0 deletions
@@ -0,0 +1,1888 @@ +char *teco_c_version = "teco.c: $Revision: 1.1 $"; +char *copyright = "Copyright (c) 1985-2003 Paul Cantrell & Joyce Nishinaga"; + +/* + * $Date: 2007/12/10 21:59:20 $ + * $Source: /cvsroot/videoteco/videoteco/teco.c,v $ + * $Revision: 1.1 $ + * $Locker: $ + */ + +/* teco.c + * Main TECO entry point with most of the initialization code + * + * COPYRIGHT (c) 1985-2003 BY + * PAUL CANTRELL & J. M. NISHINAGA + * SUDBURY, MA 01776 + * ALL RIGHTS RESERVED + * + * This software is furnished in it's current state free of charge. + * The authors reserve all rights to the software. Further + * distribution of the software is not authorized. Modifications to + * the software may be made locally, but shall not be distributed + * without the consent of the authors. This software or any other + * copies thereof, may not be provided or otherwise made available + * to anyone without express permission of the authors. Title to and + * ownership of this software remains with the authors. + * + */ + +#include "teco.h" +#include "tecparse.h" + +/* + * Global Storage is defined here for lack of a better place. + * First off are all the flags. + */ + char readonly_flag = NO; + char piped_input_flag = NO; + char piped_output_flag = NO; + char tty_modes_saved = NO; + char exit_flag = NO; + char intr_flag = 0; + char susp_flag = 0; + char input_pending_flag = NO; + int tab_width = NORMAL_TAB_WIDTH; + char alternate_escape_character = ESCAPE; + char main_delete_character = RUBOUT; + char alternate_delete_character = RUBOUT; + char eight_bit_output_flag = NO; + char hide_cr_flag = NO; + char waiting_for_input_flag = NO; + char resize_flag = NO; + char teco_startup = YES; + char screen_startup = YES; + char checkpoint_flag = NO; + char checkpoint_enabled = YES; + char checkpoint_modified = NO; + char outof_memory = NO; + char suspend_is_okay_flag = YES; + +/* + * Global Variables + */ + int tty_input_chan; + int tty_output_chan; + int term_speed; + FILE *teco_debug_log; + + int term_columns; + int term_lines; + int forced_width,forced_height; + + int checkpoint_interval = DEFAULT_CHECKPOINT; + + char *output_tty_name; +/* + * Table of Bit Positions + */ + unsigned int IntBits[BITS_PER_INT]; + +/* + * Table of Spaces for tab expansion + */ + char tab_expand[MAX_TAB_WIDTH+1]; +/* + * Global structures + */ +#if HAVE_TERMIOS_H + static struct termios tty_input_modes; + static struct termios tty_output_modes; +#else +#ifdef ATT_UNIX + static struct termio tty_input_modes; + static struct termio tty_output_modes; +#endif +#ifdef BSD_UNIX + static struct sgttyb tty_input_modes; + static struct sgttyb tty_output_modes; +#endif +#endif + +/* + * External Variables + */ + +/* + * Prototypes + */ + void init_signals(void); + void teco_ini(void); + void process_directory(char *,char*,int); + int match_name(char *,char *); + int map_baud(int); + + + +/* TECO - Text Edit and COrrector + * + * Function: + * + * This is the entry point for TECO. It initializes the terminal, reads + * in the specified files, and generally gets things going. + */ +int +main(argc,argv) +int argc; +char **argv; +{ +register int i; + + initialize_memory_stats(); + + for(i = 0; i < BITS_PER_INT; i++){ + IntBits[i] = 1 << i; + }/* End FOR */ + for(i = 0; i < MAX_TAB_WIDTH; i++){ + tab_expand[i] = ' '; + }/* End FOR */ + tab_expand[MAX_TAB_WIDTH] = '\0'; + + if(buff_init() == FAIL){ + tec_error(ENOMEM,"Unable to initialize buffers"); + }/* End IF */ + + handle_command_line(0,argc,argv); + + init_signals(); + initialize_tty(); + + if(strcmp(argv[0],TECO_READONLY_NAME) == 0){ + readonly_flag = YES; + }/* End IF */ + + handle_command_line(1,argc,argv); + + teco_ini(); + + teco_startup = NO; + + while(exit_flag == NO){ + tecparse(); + intr_flag = 0; + }/* End While */ + + if(piped_output_flag){ + extern struct buff_header *curbuf; + buff_openbuffnum(-1,0); + buff_write(curbuf,fileno(stdout),0,curbuf->zee); + }/* End IF */ + + restore_tty(); +/* + * Output a final newline to get the terminal onto a fresh + * line. + */ + term_putc('\n'); + term_flush(); + +#ifdef CHECKPOINT + remove_checkpoint_file(); +#endif /* CHECKPOINT */ + +#ifdef UNIX + return(0); +#endif + +#ifdef VMS + return(STS$M_SUCCESS); +#endif + +}/* End Routine */ + + + +#ifdef UNIX + +/* INITIALIZE_TTY - Set the tty up the way we want it + * + * Function: + * + * This routine is called at startup time to set up the terminal the way + * that we require, i.e., it sets the tty modes. It also gives the screen + * package a chance to initialize itself. + */ +void +initialize_tty() +{ +#if HAVE_TERMIOS_H + struct termios tmp_tty_modes; +#else +#ifdef ATT_UNIX +#define GET_IOCTL TCGETA +#define SET_IOCTL TCSETA + struct termio tmp_tty_modes; +#endif +#ifdef BSD_UNIX +#define GET_IOCTL TIOCGETP +#define SET_IOCTL TIOCSETN +#ifdef INTEGRATED_SOLUTIONS + int mask; +#endif /* INTEGRATED_SOLUTIONS */ + + struct sgttyb tmp_tty_modes; +#endif /* BSD_UNIX */ +#endif /* HAVE_TERMIOS_H */ + +#ifdef SUN_STYLE_WINDOW_SIZING + struct winsize os_window; +#endif + +/* + * We start by assuming that standard input is a tty device. If it turns out + * that it is not, however, then we want to use /dev/tty as the keyboard. + */ + if(!isatty(tty_input_chan = fileno(stdin))){ + tty_input_chan = open("/dev/tty",O_RDONLY); + piped_input_flag = YES; + if(tty_input_chan < 0){ + perror("cannot open /dev/tty"); + punt(errno); + }/* End IF */ + }/* End IF */ +/* + * Standard output MUST by a tty at this time. + */ + if(output_tty_name){ + tty_output_chan = open(output_tty_name,O_RDWR); + }/* End IF */ + + else { + tty_output_chan = fileno(stdout); + }/* End Else */ + + if(!isatty(tty_output_chan)){ + tty_output_chan = open("/dev/tty",O_RDWR); + piped_output_flag = YES; + if(tty_output_chan < 0){ + perror("cannot open /dev/tty"); + punt(errno); + }/* End IF */ + }/* End IF */ +/* + * Get the current modes of the tty so it can be restored later. + */ +#if HAVE_TERMIOS_H + if(tcgetattr(tty_input_chan,&tty_input_modes) < 0){ + perror("could not get input tty modes"); + punt(errno); + }/* End IF */ + + if(tcgetattr(tty_output_chan,&tty_output_modes) < 0){ + perror("could not get output tty modes"); + punt(errno); + }/* End IF */ + +#else + if(ioctl(tty_input_chan,GET_IOCTL,&tty_input_modes) < 0){ + perror("could not get input tty modes"); + punt(errno); + }/* End IF */ + + if(ioctl(tty_output_chan,GET_IOCTL,&tty_output_modes) < 0){ + perror("could not get output tty modes"); + punt(errno); + }/* End IF */ + +#endif + +#if 0 /* Turned off because xterm is getting clobbered by this */ +#if defined(ATT_UNIX) || defined(_POSIX_SOURCE) + if((tty_output_modes.c_cflag & CSIZE) == CS8){ + eight_bit_output_flag = YES; + }/* End IF */ +#endif +#endif + +#if defined(HAVE_DECL_CFGETOSPEED) && HAVE_DECL_CFGETOSPEED + term_speed = map_baud( cfgetospeed(&tty_output_modes) ); +#else +# if defined(HAVE_STRUCT_TERMIOS_C_CFLAG) && defined(CBAUD) + term_speed = map_baud(tty_output_modes.c_cflag & CBAUD); +# else +# if HAVE_STRUCT_TERMIOS_C_OSPEED + term_speed = map_baud(tty_output_modes.c_ospeed); +#else +# if HAVE_STRUCT_TERMIOS_SG_OSPEED + term_speed = map_baud(tty_output_modes.sg_ospeed); +# endif +# endif +# endif +#endif + +#if HAVE_TERMIOS_H + main_delete_character = tty_input_modes.c_cc[VERASE]; +#else +#if defined(BSD_UNIX) + main_delete_character = tty_input_modes.sg_erase; +#endif +#endif + + tty_modes_saved = YES; +/* + * Check out the termcap description for this tty now. It makes + * no sense to go changing the terminal modes all around until + * we decide whether we can run on this terminal or not. + */ + init_term_description(); +/* + * If the OS supports window size ioctls, we try for that since it + * will tend to be more correct for a window environment. + */ +#ifdef SUN_STYLE_WINDOW_SIZING + if(ioctl(tty_output_chan,TIOCGWINSZ,&os_window) >= 0){ + + if((unsigned)os_window.ws_row >= SCREEN_MAX_LINES){ + fprintf(stderr,"os_window.ws_row %d os_window.ws_col %d\n", + (int)os_window.ws_row,(int)os_window.ws_col); + fprintf(stderr,"term_lines %d term_columns %d\n", + term_lines,term_columns); + fprintf(stderr,"forced_height %d forced_width %d\n", + forced_height,forced_width); + os_window.ws_row = term_lines; + os_window.ws_col = term_columns; + }/* End IF */ + + if(forced_height > 0) os_window.ws_row = forced_height; + if(forced_width > 0) os_window.ws_col = forced_width; + if(teco_startup == YES){ + term_lines = os_window.ws_row; + term_columns = os_window.ws_col; + }/* End IF */ + if(term_lines == 0) term_lines = 24; + if(term_columns == 0) term_columns = 80; + if(term_lines != os_window.ws_row || term_columns != os_window.ws_col){ + screen_resize(); + }/* End IF */ + }/* End IF */ +#else + if(forced_height > 0) term_lines = forced_height; + if(forced_width > 0) term_columns = forced_width; + if(term_lines == 0) term_lines = 24; + if(term_columns == 0) term_columns = 80; +#endif + + if(term_lines >= SCREEN_MAX_LINES){ + char tmp_message[LINE_BUFFER_SIZE]; + sprintf( + tmp_message, + "terminal line count of %d exceeds maximum of %d", + term_lines, + SCREEN_MAX_LINES + ); + tec_error(E2BIG,tmp_message); + }/* End IF */ + +/* + * Now set the modes that we want + */ + tmp_tty_modes = tty_input_modes; + +#if HAVE_TERMIOS_H + tmp_tty_modes.c_lflag &= ~(ICANON | ECHO); + tmp_tty_modes.c_cc[VMIN] = 1; + tmp_tty_modes.c_cc[VTIME] = 1; +#else +#ifdef SYS_III_UNIX + tmp_tty_modes.c_lflag &= ~(ICANON | ECHO | ISTATUS); + tmp_tty_modes.c_cc[VMIN] = 1; + tmp_tty_modes.c_cc[VTIME] = 1; +#endif + +#ifdef BSD_UNIX + tmp_tty_modes.sg_flags |= CBREAK; + tmp_tty_modes.sg_flags &= ~(ECHO); +#endif +#endif /* SYSV or POSIX */ + +#if HAVE_TERMIOS_H + if(tcsetattr(tty_input_chan,TCSANOW,&tmp_tty_modes) < 0){ + perror("could not set input tty modes"); + punt(errno); + }/* End IF */ +#else + if(ioctl(tty_input_chan,SET_IOCTL,&tmp_tty_modes) < 0){ + perror("could not set input tty modes"); + punt(errno); + }/* End IF */ +#endif + +#ifdef INTEGRATED_SOLUTIONS + mask = LLITOUT; + ioctl(tty_output_chan,TIOCLBIS,&mask); +#endif + + screen_init(); + +}/* End Routine */ + +#endif /* UNIX */ + +/* END OF UNIX CONDITIONAL CODE */ + + + +#if !HAVE_TCGETATTR + +/* TCGETATTR - Mimic the POSIX terminal get/set functions + * + * Function: + * + * For a system being compiled with POSIX headers, but which doesn't + * actually have all the POSIX functions in libc, we have to fake up + * this call. + */ +int +tcgetattr(fd,termios_p) +int fd; +struct termios *termios_p; +{ +struct termio tmp_modes; +register int i; +int status; + + bzero(&tmp_modes,sizeof(tmp_modes)); + if(status = ioctl(fd,TCGETA,&tmp_modes) < 0){ + return(-1); + }/* End IF */ + + termios_p->c_iflag = tmp_modes.c_iflag; + termios_p->c_oflag = tmp_modes.c_oflag; + termios_p->c_cflag = tmp_modes.c_cflag; + termios_p->c_lflag = tmp_modes.c_lflag; + termios_p->c_line = tmp_modes.c_line; + for(i = 0; i < (NCCS < NCC ? NCCS : NCC); i++){ + termios_p->c_cc[i] = tmp_modes.c_cc[i]; + }/* End FOR */ + + return(status); + +}/* End Routine */ + +#endif /* !HAVE_TCGETATTR */ + +#if !HAVE_TCSETATTR + +/* TCSETATTR - Mimic the POSIX terminal get/set functions + * + * Function: + * + * For a system being compiled with POSIX headers, but which doesn't + * actually have all the POSIX functions in libc, we have to fake up + * this call. + */ +tcsetattr(fd,flags,termios_p) +int fd; +int flags; +const struct termios *termios_p; +{ +struct termio tmp_modes; +register int i; +int status; + + bzero(&tmp_modes,sizeof(tmp_modes)); + tmp_modes.c_iflag = termios_p->c_iflag; + tmp_modes.c_oflag = termios_p->c_oflag; + tmp_modes.c_cflag = termios_p->c_cflag; + tmp_modes.c_lflag = termios_p->c_lflag; + tmp_modes.c_line = termios_p->c_line; + for(i = 0; i < (NCCS < NCC ? NCCS : NCC); i++){ + tmp_modes.c_cc[i] = termios_p->c_cc[i]; + }/* End FOR */ + if(status = ioctl(fd,TCSETA,&tmp_modes) < 0){ + return(-1); + }/* End IF */ + + return(status); + +}/* End Routine */ + +#endif /* !HAVE_TCSETATTR */ + +#ifdef VMS + + +/* INITIALIZE_TTY - Set the tty up the way we want it + * + * Function: + * + * This routine is called at startup time to set up the terminal the way + * that we require, i.e., it sets the tty modes. It also gives the screen + * package a chance to initialize itself. + */ +void +initialize_tty() +{ +register int status; +$DESCRIPTOR(input_name,"SYS$INPUT"); +$DESCRIPTOR(output_name,"SYS$OUTPUT"); + +struct sense_mode_buffer { + unsigned char class; + unsigned char type; + unsigned short buffer_size; + unsigned char characteristics[3]; + unsigned char page_length; +} sense_buffer; + +struct sense_iosb { + unsigned short status; + unsigned char transmit_speed; + unsigned char receive_speed; + unsigned char cr_fill_count; + unsigned char lf_fill_count; + unsigned char parity_flags; + unsigned char unused; +} sense_iosb; + +/* + * Assign channels to the terminal + */ + status = sys$assign(&input_name,&tty_input_chan,0,0); + if(!(status & STS$M_SUCCESS)){ + tec_error(status,"VTECO: ASSIGN of SYS$INPUT Failed"); + }/* End IF */ + +/* + * Standard output MUST by a tty at this time. + */ + status = sys$assign(&output_name,&tty_output_chan,0,0); + if(!(status & STS$M_SUCCESS)){ + tec_error(status,"VTECO: ASSIGN of SYS$OUTPUT Failed"); + }/* End IF */ + +/* + * Get the baud rate of the terminal + */ + status = sys$qiow(0,tty_output_chan,IO$_SENSEMODE, + &sense_iosb,0,0,&sense_buffer,sizeof(sense_buffer),0,0,0,0); + if(!(status & STS$M_SUCCESS)){ + tec_error(status,"VTECO: Error on sense mode %d (%x)",status,status); + }/* End IF */ + + term_lines = sense_buffer.page_length; + term_columns = sense_buffer.buffer_size; + + term_speed = map_baud(sense_iosb.transmit_speed); + tty_modes_saved = YES; + +/* + * Check out the termcap description for this tty now. It makes + * no sense to go changing the terminal modes all around until + * we decide whether we can run on this terminal or not. + */ + init_term_description(); +/* + * Now set the modes that we want + */ + screen_init(); + +}/* End Routine */ + +/* END OF VMS CONDITIONAL CODE */ + +#endif + + + +/* RESTORE_TTY - Set the tty back to the way that it was + * + * Function: + * + * This routine is called at exit time to set the terminal modes back + * how they were when we started. + */ +void +restore_tty() +{ + +/* + * Give the screen package a chance to cleanup + */ + screen_finish(); + +/* + * Can't restore the terminal modes if they haven't been saved yet + */ + if(tty_modes_saved != YES) return; + +/* + * Restore the modes that we saved away previously + */ +#if HAVE_TERMIOS_H + if(tcsetattr(tty_input_chan,TCSADRAIN,&tty_input_modes) < 0){ + perror("could not set input tty modes"); + punt(errno); + }/* End IF */ + if(tcsetattr(tty_output_chan,TCSADRAIN,&tty_output_modes) < 0){ + perror("could not set output tty modes"); + punt(errno); + }/* End IF */ +#else +#ifdef UNIX + if(ioctl(tty_input_chan,SET_IOCTL,&tty_input_modes) < 0){ + perror("could not restore input tty modes"); + }/* End IF */ + + if(ioctl(tty_output_chan,SET_IOCTL,&tty_output_modes) < 0){ + perror("could not restore output tty modes"); + }/* End IF */ +#endif +#endif + +}/* End Routine */ + + + +/* TTY_INPUT_PENDING - Check if there is input waiting on the cmd channel + * + * Function: + * + * This routine returns a true false indication of whether or not there + * is input waiting on the command channel. This generally gets used to + * set the input_pending_flag variable. Now it is set to only work if + * the baud rate is quite low, since some of the current side effects are + * undesirable. + */ +int +tty_input_pending() +{ +#ifdef HAS_FIONREAD +long temp; +#endif + +#ifdef VMS +struct sense_typeahead_buffer { + unsigned short count; + unsigned char first_char; + unsigned char reserved1; + unsigned long reserved2; +} sense_typeahead_buffer; +#endif + +#ifdef SEQUOIA +struct sequoia_weirdo_get_param_buffer { + unsigned short fc_open_mode; + unsigned long fc_num_bytes_remaining; + char fc_no_delay; + char fc_modify_no_delay; + char fc_append; + char fc_modify_append; + char fc_close_across_exec; + char fc_modify_close_across_exec; + char fc_no_further_opens; + char fc_modify_no_further_opens; +}term_params; + +typedef enum +{ + FC_SET_PARAMETERS, + FC_GET_PARAMETERS, + FC_SET_BUFFER_REPLACEMENT_PARAMETERS, + FC_GET_BUFFER_REPLACEMENT_PARAMETERS, +} FILE_CONTROL_REQUEST; +#endif + +#ifdef SEQUOIA +int err; + err = seqksr_file_control(&term_params,tty_input_chan,FC_GET_PARAMETERS); + if(err == 0 && term_params.fc_num_bytes_remaining != 0) return(YES); +#endif + +#ifdef HAS_POLL + int err; + struct pollfd pollStructs[1]; +#endif + +#ifdef HAS_SELECT + int err; + fd_set theFds; + struct timeval zeroTime; +#endif + + PREAMBLE(); + +#ifdef HAS_SELECT + FD_ZERO( &theFds ); + FD_SET( tty_input_chan, &theFds ); + zeroTime.tv_sec = 0; + zeroTime.tv_usec = 0; + err = select( tty_input_chan+1, &theFds, 0, 0, &zeroTime ); + if(err > 0) + { + return(YES); + } +#endif + +#ifdef HAS_POLL + pollStructs[0].fd = tty_input_chan; + pollStructs[0].events = POLLIN; + pollStructs[0].revents = 0; + err = poll( pollStructs, ELEMENTS(pollStructs), 0 ); + if( err >= 0 && pollStructs[0].revents & POLLIN) return(YES); +#endif + +#ifdef HAS_FIONREAD + temp = 0; + if(ioctl(tty_input_chan,FIONREAD,&temp) < 0) return(NO); + if(temp > 0) return(YES); +#endif + +#ifdef VMS + temp = sys$qiow(0,tty_input_chan,IO$_SENSEMODE|IO$M_TYPEAHDCNT, + 0,0,0,&sense_typeahead_buffer,sizeof(sense_typeahead_buffer),0,0,0,0); + if(!(temp & STS$M_SUCCESS)){ + return(NO); + }/* End IF */ + + if(sense_typeahead_buffer.count > 0) return(YES); + +#endif + + return(NO); + +}/* End Routine */ + + + +/* INIT_SIGNALS - Initialize the signal interface for our use + * + * Function: + * + * This routine sets up the way that we want signals to behave + */ +void +init_signals() +{ + +#if 0 +int cmd_suspend(); +#endif +int cmd_interrupt(); +int cmd_alarm(); +int cmd_winch(); + + PREAMBLE(); + +#ifdef CHECKPOINT + signal(SIGALRM,(void (*)())cmd_alarm); + alarm(checkpoint_interval); +#endif /* CHECKPOINT */ + + signal(SIGINT,(void (*)())cmd_interrupt); + +#ifdef JOB_CONTROL + if(suspend_is_okay_flag == YES){ + signal(SIGTSTP,(void (*)())cmd_suspend); + } +#endif + +#ifdef SUN_STYLE_WINDOW_SIZING + signal(SIGWINCH,(void (*)())cmd_winch); +#endif + +}/* End Routine */ + + + +/* TECO_INI - Routine to read the TECO_INI file + * + * Function: + * + * This routine reads the user's TECO_INI file and sets up the default + * Q-Register contents he has specified. It also executes Q-Register 0. + */ +void +teco_ini() +{ +register char *cp; +register int c,dc; +register int i; +char filename[LINE_BUFFER_SIZE]; +char tmp_message[LINE_BUFFER_SIZE]; +FILE *fp; +struct buff_header *hbp; +char zero_flag = NO; +char comment_flag = NO; + + PREAMBLE(); + +#ifdef UNIX + + if((cp = getenv("HOME"))){ + (void) strcpy(filename,cp); + }/* End IF */ + + else return; + + (void) strcat(filename,"/.teco_ini"); + +#endif + +#ifdef VMS + + strcpy(filename,"SYS$LOGIN:TECO.INI"); + +#endif + + fp = fopen(filename,"r"); + + if(fp == NULL) return; + +/* + * Outer loop reads the name of the Q-Register to be loaded and the delimiter, + * inner loop reads contents of a Q-Register in. + */ + while(1){ + while(1){ + if((c = getc(fp)) == EOF){ + fclose(fp); + if(zero_flag) exec_doq0(); + return; + }/* End IF */ + + if(comment_flag){ + if(c == '!') comment_flag = NO; + continue; + }/* End IF */ + + if(c == '!'){ + comment_flag = YES; + continue; + }/* End IF */ + + if(isspace(c)) continue; + break; + + }/* End While */ + + if(c == '0') zero_flag = YES; + + hbp = buff_qfind(c,1); + if(hbp == NULL){ + fclose(fp); + return; + }/* End IF */ + + if((dc = getc(fp)) == EOF){ + sprintf(tmp_message,"?EOF while loading Q-REGISTER <%c>",c); + error_message(tmp_message); + fclose(fp); + return; + }/* End IF */ + + while(1){ + if((i = getc(fp)) == EOF){ + sprintf(tmp_message,"?EOF while loading Q-REGISTER <%c>",c); + error_message(tmp_message); + fclose(fp); + return; + }/* End IF */ + + if(i == dc) break; + + buff_insert_char(hbp,hbp->zee,i); + + }/* End While */ + + hbp->ismodified = NO; + + }/* End While */ + +}/* End Routine */ + + + +void +check_for_forced_screen_size(argc,argv) +int argc; +char **argv; +{ +register int i; +char *cp, c; + + PREAMBLE(); + + for(i = 1; i < argc; i++){ + if(argv[i][0] == '-'){ + cp = &argv[i][1]; + while((c = *cp++)){ + switch(c){ + case 'X': case 'x': + case 'W': case 'w': + if(isdigit((int)*cp)){ + forced_width = 0; + while(isdigit((int)*cp)){ + forced_width = forced_width * 10 + *cp++ - '0'; + }/* End While */ + }/* End IF */ + else if( ((i+1) < argc) && isdigit((int)argv[i+1][0])){ + forced_width = atoi(argv[i+1]); + i++; + } + else forced_width = 80; + break; + case 'Y': case 'y': + case 'H': case 'h': + if(isdigit((int)*cp)){ + forced_height = 0; + while(isdigit((int)*cp)){ + forced_height = forced_height * 10 + + *cp++ - '0'; + }/* End While */ + }/* End IF */ + else if( ((i+1) < argc) && isdigit((int)argv[i+1][0])){ + forced_height = atoi(argv[i+1]); + i++; + } + else forced_height = 24; + break; + case 'O': case 'o': + output_tty_name = argv[i+1]; + i++; + break; + }/* End Switch */ + }/* End While */ + continue; + }/* End IF */ + + }/* End FOR */ + +}/* End Routine */ + + + +/* HANDLE_COMMAND_LINE - OS Dependent Code to handle command line arguments + * + * Function: + * + * This code handles the command line arguments to VTECO. This requires + * different processing for different operating systems. + */ +#ifdef UNIX +int +handle_command_line(which_time,argc,argv) +int which_time; +int argc; +char **argv; +{ +register int i; +extern struct buff_header *curbuf; +int first_file_argument; +char *cp, c; +int arg_skip; + + PREAMBLE(); + + first_file_argument = 0; + for(i = 1; i < argc; i += arg_skip){ + arg_skip = 1; + if(argv[i][0] == '-'){ + cp = &argv[i][1]; + while((c = *cp++)){ + switch(c){ + case 'C': case 'c': + checkpoint_enabled = NO; + break; + case 'X': case 'x': + case 'W': case 'w': + if(isdigit((int)*cp)){ + forced_width = 0; + while(isdigit((int)*cp)){ + forced_width = forced_width * 10 + *cp++ - '0'; + }/* End While */ + }/* End IF */ + else if( ((i+1) < argc) && isdigit((int)argv[i+1][0])){ + forced_width = atoi(argv[i+1]); + arg_skip++; + } + else forced_width = 80; + break; + case 'Y': case 'y': + case 'H': case 'h': + if(isdigit((int)*cp)){ + forced_height = 0; + while(isdigit((int)*cp)){ + forced_height = forced_height * 10 + + *cp++ - '0'; + }/* End While */ + }/* End IF */ + else if( ((i+1) < argc) && isdigit((int)argv[i+1][0])){ + forced_height = atoi(argv[i+1]); + i++; + } + else forced_height = 24; + break; + case 'O': case 'o': + output_tty_name = argv[i+1]; + arg_skip++; + break; + default: + fprintf(stderr,"teco: unknown switch '%c'\n",c); + cp = ""; + break; + }/* End Switch */ + }/* End While */ + continue; + }/* End IF */ + + if(which_time == 0) return( SUCCESS ); + + if(!first_file_argument) first_file_argument = i; + if(!outof_memory){ + buff_openbuffer(argv[i],0,readonly_flag); + }/* End IF */ + + }/* End FOR */ + + if(first_file_argument){ + buff_openbuffer(argv[first_file_argument],0,readonly_flag); + }/* End IF */ + + if(piped_input_flag){ + buff_openbuffnum(-1,0); + buff_readfd(curbuf,"Standard Input",fileno(stdin)); + curbuf->dot = 0; + }/* End IF */ + + return( SUCCESS ); + +}/* End Routine */ +#endif + +#ifdef VMS + +int +handle_command_line(which_time,argc,argv) +int which_time; +int argc; +char **argv; +{ +register int i,j; +int count; +register char *cp,*dp; +struct wildcard_expansion *name_list,*np; +struct wildcard_expansion *expand_filename(); +int number_of_buffers_opened = 0; +char switch_buffer[TECO_FILENAME_TOTAL_LENGTH]; +char *command_line; +struct dsc$descriptor command_desc; +struct dsc$descriptor symbol_value; +$DESCRIPTOR (symbol_name,"teco_spawn_execute"); +long table = LIB$K_CLI_LOCAL_SYM; +long flags = 0; +long status; +long completion_status; +long sub_pid; + + PREAMBLE(); + + for(i = 1; i < argc; i++){ + cp = argv[i]; + if(*cp == '/'){ + cp++; + dp = switch_buffer; + while(isalnum(*cp)) *dp++ = *cp++; + *dp = '\0'; + if(strcmp(switch_buffer,"subjb") == 0){ + continue; + }/* End IF */ + + else if(strcmp(switch_buffer,"nosuspend") == 0){ + suspend_is_okay_flag = NO; + }/* End IF */ + + else if(strcmp(switch_buffer,"nocheckpoint") == 0){ + checkpoint_enabled = NO; + }/* End IF */ + + else if(strcmp(switch_buffer,"spawn") == 0){ + if(which_time == 1){ + cp -= 5; + dp = "subjb"; + while(*dp) *cp++ = *dp++; + strcpy(switch_buffer,"$ "); + strcat(switch_buffer,argv[0]); + symbol_value.dsc$w_length = strlen(switch_buffer); + symbol_value.dsc$b_dtype = 0; + symbol_value.dsc$b_class = DSC$K_CLASS_S; + symbol_value.dsc$a_pointer = switch_buffer; + status = lib$set_symbol(&symbol_name,&symbol_value,&table); + if(status != SS$_NORMAL){ + perror("could not create symbol 'teco_spawn_execute'"); + punt(status); + }/* End IF */ + count = strlen(symbol_name.dsc$a_pointer); + for(j = 1; j < argc; j++){ + count += strlen(argv[j]) + 1; + }/* End FOR */ + command_line = tec_alloc(TYPE_C_CBUFF,count+8); + if(command_line == NULL) return(FAIL); + dp = command_line; + cp = symbol_name.dsc$a_pointer; + while(*dp++ = *cp++); dp--; + for(j = 1; j < argc; j++){ + *dp++ = ' '; + cp = argv[j]; + while(*dp++ = *cp++); + dp--; + }/* End FOR */ + command_desc.dsc$w_length = strlen(command_line); + command_desc.dsc$b_dtype = 0; + command_desc.dsc$b_class = DSC$K_CLASS_S; + command_desc.dsc$a_pointer = command_line; + + printf("VTECO: Spawning subjob for edit\n"); + + status = lib$spawn(&command_desc,0,0,&flags,0, + &sub_pid,&completion_status,0,0,0,0,0); + if(status != SS$_NORMAL){ + perror("VTECO: LIB$SPAWN failed"); + punt(status); + }/* End IF */ + + exit(SS$_NORMAL); + + }/* End IF */ + }/* End IF */ + + else { + char tmp_message[LINE_BUFFER_SIZE]; + sprintf( + tmp_message, + "?Unknown VTECO switch '%s'", + switch_buffer + ); + tec_error(EINVAL,tmp_message); + }/* End Else */ + + }/* End IF */ + + if(which_time == 0) return(SUCCESS); + + if(argv[i][0] != '/'){ + np = expand_filename(argv[i]); + while(np){ + number_of_buffers_opened++; + buff_openbuffer(np->we_name,0,readonly_flag); + name_list = np; + np = np->we_next; + tec_release(TYPE_C_WILD,name_list); + }/* End While */ + }/* End IF */ + + }/* End FOR */ + + if(number_of_buffers_opened > 0) buff_openbuffer(0,1,readonly_flag); + + return(SUCCESS); + +}/* End Routine */ + + + +/* EXPAND_FILENAME - Expand VMS wildcards to a list of files + * + * Function: + * + * This routine returns a linked list of expanded filenames. + */ +struct wildcard_expansion * +expand_filename(wildcard_string) +char *wildcard_string; +{ +struct wildcard_expansion *name_list,*np; +struct dsc$descriptor argument; +struct dsc$descriptor result; +struct { + unsigned short curlen; + char body[TECO_FILENAME_TOTAL_LENGTH]; +}result_buffer; +int context; +int fnd_flags; +int status; +char flags = 0; +char *cp,*sp; + +#define NODE_SEEN (1<<0) +#define DEV_SEEN (1<<1) +#define DIR_SEEN (1<<2) +#define VER_SEEN (1<<3) + + PREAMBLE(); + +/* + * Determine which fields are present in the supplied string so that we + * may trim the resulting filespecs some. + */ + for(cp = wildcard_string; *cp != '\0'; cp++){ + switch(*cp){ + case ':': + if(cp[1] == ':'){ + flags |= NODE_SEEN; + cp++; + }/* End IF */ + else flags |= DEV_SEEN | DIR_SEEN; + break; + case '[': + flags |= DIR_SEEN; + break; + case ';': + if(cp[1] != '\0') flags |= VER_SEEN; + break; + }/* End Switch */ + }/* End FOR */ + + argument.dsc$w_length = strlen(wildcard_string); + argument.dsc$b_dtype = 0; + argument.dsc$b_class = DSC$K_CLASS_S; + argument.dsc$a_pointer = wildcard_string; + + result.dsc$w_length = sizeof(result_buffer.body); + result.dsc$b_dtype = DSC$K_DTYPE_VT; + result.dsc$b_class = DSC$K_CLASS_VS; + result.dsc$a_pointer = &result_buffer; + + context = 0; + fnd_flags = (1<<1); + + name_list = NULL; + + while(1){ + status = lib$find_file(&argument,&result,&context,0,0,0,&fnd_flags); + if((status & 0xFFFF) != SS$_NORMAL) break; + np = tec_alloc(TYPE_C_WILD, sizeof(struct wildcard_expansion)); + if(np == NULL) return(NULL); + +/* + * Trim the resulting filespec as much as possible + */ + sp = result_buffer.body; + sp[result_buffer.curlen] = '\0'; + + if((flags & DEV_SEEN) == 0){ + cp = sp; + while(*cp && *cp != ':') cp++; + if(*cp == ':') strcpy(sp,cp + 1); + }/* End IF */ + + if((flags & DIR_SEEN) == 0){ + cp = sp; + while(*cp && *cp != '[') cp++; + if(*cp == '['){ + sp = cp + 1; + while(*sp && *sp != ']') sp++; + if(*sp == ']'){ + strcpy(cp,sp+1); + }/* End IF */ + sp = result_buffer.body; + }/* End IF */ + }/* End IF */ + + if((flags & VER_SEEN) == 0){ + cp = sp; + while(*cp && *cp != ';') cp++; + *cp = '\0'; + }/* End IF */ + + if(strlen(sp) > (sizeof(np->we_name)-1)){ + error_message("Filename too long"); + continue; + }/* End IF */ + + strcpy(np->we_name,sp); + np->we_next = name_list; + name_list = np; + }/* End While */ + + lib$find_file_end(&context); + +/* + * If no expansion names at all, perhaps he specified a non-wild file which + * he wants to create. If so, we will just use the wild specification as is. + */ + if(name_list == NULL){ + np = tec_alloc(TYPE_C_WILD, sizeof(struct wildcard_expansion)); + if(np == NULL) return(NULL); + if(strlen(wildcard_string) > (sizeof(np->we_name)-1)){ + error_message("Filename too long"); + return(NULL); + }/* End IF */ + + strcpy(np->we_name,wildcard_string); + np->we_next = name_list; + name_list = np; + + }/* End IF */ + + return(name_list); + +}/* End Routine */ + +/* END OF VMS CONDITIONAL CODE */ + +#endif + + + +/* + * EXPAND_FILENAME - Expand a wildcard specification to a list of files + * + * Function: + * + * This routine returns a linked list of expanded filenames. + * + */ +#ifdef UNIX +struct wildcard_expansion *name_list; +struct wildcard_expansion *name_list_end; + +struct wildcard_expansion * +expand_filename(wildcard_string) +char *wildcard_string; +{ +char temp_name[TECO_FILENAME_TOTAL_LENGTH]; +register char *cp,*sp; +struct passwd *pw; +struct passwd *getpwnam(); + + PREAMBLE(); + + name_list = NULL; + name_list_end = NULL; + +/* + * Else, start branching down into the directory list he specified + */ + if(wildcard_string[0] == '/'){ + process_directory(&wildcard_string[0],"/",2); + } + else if (wildcard_string[0] == '~'){ + cp = temp_name; + sp = &wildcard_string[1]; + while(*sp && *sp != '/') *cp++ = *sp++; + *cp++ = '\0'; + if(temp_name[0] == '\0'){ + if((cp = getenv("HOME"))) strcpy(temp_name,cp); + } + else { + pw = getpwnam(temp_name); + if(pw) strcpy(temp_name,pw->pw_dir); + } + strcat(temp_name,sp); + process_directory(temp_name,"/",2); + } + else { + strcpy(temp_name,"./"); + strcat(temp_name,wildcard_string); + process_directory(temp_name,".",1); + } + + return(name_list); + +}/* End Routine */ + +void +process_directory(wildstr,path,flags) +char *wildstr; +char *path; +int flags; +{ +char directory_path[TECO_FILENAME_TOTAL_LENGTH]; +struct wildcard_expansion *np; +DIR *dirp; + +struct dirent *dp; + +struct stat statbuf; +int c; +char *cp; + + PREAMBLE(); + +/* + * Find the next slash, and if there isn't one, then we have reached + * the end of the file specification, and this should be a file rather + * than a directory. + */ + wildstr = (char *)strchr(wildstr,'/'); + + directory_path[0] = '\0'; + + if(wildstr == NULL){ + if(flags < 0){ + if(stat(path,&statbuf) < 0) return; +#ifndef USE_S_MACS + if((statbuf.st_mode & S_IFMT) != S_IFREG) return; +#else + if(!S_ISREG(statbuf.st_mode)) return; +#endif + }/* End IF */ + if(strlen(path) > (sizeof(np->we_name)-1)){ + error_message("Filename too long"); + return; + }/* End IF */ + np = (struct wildcard_expansion *) + tec_alloc(TYPE_C_WILD, sizeof(struct wildcard_expansion)); + if(np == NULL) return; + np->we_next = NULL; + strcpy(np->we_name,path); + if(name_list == NULL) name_list = np; + if(name_list_end)name_list_end->we_next = np; + name_list_end = np; + return; + }/* End IF */ + +#ifndef USE_S_MACS + if(stat(path,&statbuf) < 0 || ((statbuf.st_mode & S_IFMT) != S_IFDIR)){ + return; + }/* End IF */ +#else + if (stat(path,&statbuf) < 0 || !S_ISDIR(statbuf.st_mode)) { + return; + } +#endif + +/* + * This loop tests whether there are any wildcard characters in the + * next directory. If not, don't go through the overhead of reading + * directories, just simply hop down to that subdir. + */ + cp = wildstr+1; + while((c = *cp++)){ + switch(c){ + case '*': + case '?': + case '[': + case '{': + while(*cp != '\0' && *cp != '/') cp++; + goto read_directory; + case '\0': + strcpy(directory_path,path); + strcat(directory_path,"/"); + strcat(directory_path,wildstr+1); + process_directory(NULL,directory_path,-1); + return; + case '/': + cp--; + *cp = '\0'; + switch(flags){ + case 2: + strcpy(directory_path,"/"); + strcat(directory_path,wildstr+1); + flags = 0; + break; + case 1: + strcpy(directory_path,wildstr+1); + flags = 0; + break; + case 0: + case -1: + strcpy(directory_path,path); + strcat(directory_path,"/"); + strcat(directory_path,wildstr+1); + break; + }/* End Switch */ + + *cp = '/'; + process_directory(wildstr+1,directory_path,flags); + return; + }/* End Switch */ + }/* End While */ + + switch(flags){ + case 2: + strcat(directory_path,wildstr); + flags = 0; + break; + case 1: + strcpy(directory_path,wildstr+1); + flags = 0; + break; + case 0: + case -1: + strcpy(directory_path,path); + strcat(directory_path,wildstr); + break; + }/* End Switch */ + process_directory("",directory_path,flags); + return; + +read_directory: + + dirp = opendir(path); + + while((dp = readdir(dirp)) != NULL) { +#ifndef _POSIX_SOURCE + if(dp->d_ino == 0) continue; +#endif /* _POSIX_SOURCE */ + if(match_name(dp->d_name,wildstr+1)){ + switch(flags){ + case 2: + strcpy(directory_path,"/"); + strcat(directory_path,dp->d_name); + break; + case 1: + strcpy(directory_path,dp->d_name); + break; + case -1: + case 0: + strcpy(directory_path,path); + strcat(directory_path,"/"); + strcat(directory_path,dp->d_name); + }/* End Switch */ + process_directory(cp,directory_path,-1); + }/* End IF */ + }/* End While */ + + closedir(dirp); + +}/* End Routine */ + +/* + * MATCH_NAME - Check whether a name satisfies a wildcard specification + * + * Function: + * + * This routine attempts to do csh style wildcard matching so that + * internal teco routines may support this behavior. + */ +int +match_name(name,pattern) +char *name; +char *pattern; +{ +char temp_buff[TECO_FILENAME_TOTAL_LENGTH]; +register int c; +register char *cp,*sp; +int match; +int previous_char; +int pattern_char; + + PREAMBLE(); + + while(1){ + switch(pattern_char = *pattern++){ + case '/': + case '\0': + return(*name == '\0' ? 1 : 0); + case '?': + return(*name == '\0' || *name == '/' ? 0 : 1); +/* + * Open-Bracket allows any of a list of characters to match, such as [abc], + * or a range such as [a-c], or a combination such as [abg-mz]. + */ + case '[': + match = 0; + c = *name; + previous_char = 'A'; + while((pattern_char = *pattern++)){ + if(pattern_char == c) match = 1; + if(pattern_char == ']'){ + if(match) break; + return(0); + }/* End IF */ + if(pattern_char == '-'){ + pattern_char = *pattern++; + if(pattern_char == ']'){ + pattern--; + pattern_char = 'z'; + }/* End IF */ + if(c >= previous_char && c <= pattern_char){ + match = 1; + }/* End IF */ + }/* End IF */ + previous_char = pattern_char; + }/* End While */ + break; +/* + * Brace allows a list of strings, any one of which may match, and any + * one of which may contain further wildcard specifications. + */ + case '{': + cp = temp_buff; + while((pattern_char = *pattern++)){ + if(pattern_char == ',' || pattern_char == '}'){ + sp = pattern; + if(pattern_char == ','){ + while((c = *sp++)){ + if(c == '}') break; + }/* End While */ + }/* End IF */ + while((*cp++ = *sp++)); + if(match_name(name,temp_buff)) return(1); + if(pattern_char == '}') return(0); + cp = temp_buff; + }/* End IF */ + else *cp++ = pattern_char; + }/* End While */ + break; +/* + * Asterisk matches any string + */ + case '*': + if(*pattern == '\0') return(1); + if(*pattern == '/') return(1); + for(c = 0; name[c] != '\0'; c++){ + if(match_name(&name[c],pattern)) return(1); + }/* End FOR */ + return(0); + default: + if(pattern_char != *name) return(0); + break; + }/* End Switch */ + name++; + }/* End While */ +}/* End Routine */ + +#endif + + + +/* PUNT - Exit with a specified error code + * + * Function: + * + * PUNT is called with an errno code with which we want to exit + * + */ +void +punt(exit_code) +int exit_code; +{ + PREAMBLE(); + + exit(exit_code); + +}/* End Routine */ + + +/* TEC_PANIC - Print an error string + * + * Function: + * + * This routine is called previous to punt to print an error string + */ +void +tec_panic(string) +char *string; +{ + PREAMBLE(); + + fputs(string,stdout); + fputs("\n",stdout); + +#ifndef UNIX + exit(1); +#else + kill(getpid(),SIGQUIT); +#endif + +}/* End Routine */ + +/* TEC_ERROR - Exit with specified error code, printing an error string + * + * Function: + * + * This routine is called to exit with the specified error code + * after printing the supplied error string. + */ +void +tec_error(code,string) +int code; +char *string; +{ + PREAMBLE(); + + fprintf(stderr,"%s\n",string); + +#ifdef VMS + exit(1); +#endif + + exit(code); + +}/* End Routine */ + +#if DEBUG_UNUSED +/* OPEN_DEBUG_LOG_FILE - Open a file to write debug output to + * + * Function: + * + * This routine is called so that a log file is open for writing of + * debugging messages. + */ +void +open_debug_log_file() +{ + PREAMBLE(); + + teco_debug_log = fopen("teco.dbg","w"); + if(teco_debug_log == NULL){ + teco_debug_log = stderr; + }/* End IF */ + +}/* End Routine */ +#endif + + + +/* MAP_BAUD - Map system dependent baud fields to a standard integer + * + * Function: + * + * This routine takes an operating system specific baud rate + * representation, and maps it into a simple integer value. + */ +int +map_baud(input_baud_rate) +int input_baud_rate; +{ +register int i; +int return_baud; + +#ifdef UNIX + +#ifndef B19200 +#define B19200 B9600 +#endif +#ifndef B38400 +#define B38400 B9600 +#endif + +static unsigned int encoded_bauds[] = { + B0,B50,B75,B110,B134,B150,B200,B300,B600,B1200, + B1800,B2400,B4800,B9600,B19200,B38400, + EXTA,EXTB }; +static int equivalent_baudrates[] = { + 0,50,75,110,134,150,200,300,600,1200, + 1800,2400,4800,9600,19200,38400, + 9600,9600 }; + +/* END OF UNIX CONDITIONAL CODE */ + +#endif + +#ifdef VMS +static unsigned char encoded_bauds[] = { + TT$C_BAUD_50,TT$C_BAUD_75,TT$C_BAUD_110,TT$C_BAUD_134, + TT$C_BAUD_150,TT$C_BAUD_300,TT$C_BAUD_600,TT$C_BAUD_1200, + TT$C_BAUD_1800,TT$C_BAUD_2000,TT$C_BAUD_2400,TT$C_BAUD_3600, + TT$C_BAUD_4800,TT$C_BAUD_7200,TT$C_BAUD_9600,TT$C_BAUD_19200 }; +static int equivalent_baudrates[] = { + 50,75,110,134, + 150,300,600,1200, + 1800,2000,2400,3600, + 4800,7200,9600,19200 }; + +/* END OF VMS CONDITIONAL CODE */ + +#endif + + PREAMBLE(); + +/* + * Default is a common baud rate incase we miss somehow + */ + return_baud = 9600; + +/* + * Determine how many entries there are in the baud table + */ + i = sizeof(encoded_bauds) / sizeof(encoded_bauds[0]); + +/* + * Now scan the table for our input baud rate + */ + while(--i >= 0){ + if(input_baud_rate != encoded_bauds[i]) continue; + return_baud = equivalent_baudrates[i]; + break; + }/* End While */ + + return(return_baud); + +}/* End Routine */ + + + +/* ERROR_TEXT - Return 'errno' style error string + * + * Function: + * + * This routine is called to return the error string associated with an + * errno error code. + */ +char * +error_text(err_num) +register int err_num; +{ + +#ifdef VMS + + int sys_nerr; + +static char *tec_errlist[] = { + "success", /* ESUCCESS */ + "not owner", /* EPERM */ + "no such file or directory", /* ENOENT */ + "no such process", /* ESRCH */ + "interrupted system call", /* EINTR */ + "i/o error", /* EIO */ + "no such device or address", /* ENXIO */ + "arg list too long", /* E2BIG */ + "exec format error", /* ENOEXEC */ + "bad file number", /* EBADF */ + "no children", /* ECHILD */ + "no more processes", /* EAGAIN */ + "not enough core", /* ENOMEM */ + "permission denied", /* EACCES */ + "bad address", /* EFAULT */ + "block device required", /* ENOTBLK */ + "mount device busy", /* EBUSY */ + "file exists", /* EEXIST */ + "cross-device link", /* EXDEV */ + "no such device", /* ENODEV */ + "not a directory", /* ENOTDIR */ + "is a directory", /* EISDIR */ + "invalid argument", /* EINVAL */ + "file table overflow", /* ENFILE */ + "too many open files", /* EMFILE */ + "not a typewriter", /* ENOTTY */ + "text file busy", /* ETXTBSY */ + "file too large", /* EFBIG */ + "no space left on device", /* ENOSPC */ + "illegal seek", /* ESPIPE */ + "read-only file system", /* EROFS */ + "too many links", /* EMLINK */ + "broken pipe", /* EPIPE */ + "math argument", /* EDOM */ + "result too large" /* ERANGE */ +}; + + PREAMBLE(); + + sys_nerr = sizeof(tec_errlist) / sizeof(tec_errlist[0]) - 1; + +/* END OF VMS CONDITIONAL CODE */ + +#endif + +#if 0 + if(err_num < 0 || err_num > sys_nerr){ + return("unknown error"); + }/* End IF */ +#endif + +#ifdef UNIX +#if 0 + return((char *)sys_errlist[err_num]); +#endif + return( strerror( err_num )); +#endif + +#ifdef VMS + return(tec_errlist[err_num]); +#endif + +}/* End Routine */ |