/* TECO for DOS Copyright (C) 1986-1991 Matt Fichtenbaum */ /* Based on Ultrix TECO (C) 1986-1990 GenRad Inc., Concord, MA 01742 */ /* These programs may be copied if this copyright notice is included and */ /* only on a "not for sale or profit" basis */ /* te_exec2.c process "E" and "F" commands 07/26/91 20.47 */ /* 8-bit version 04/18/91 09.48 */ /* version using "swap" for EQ commands 08/27/91 22.48 */ /* version with separate filespec buffers for pri, sec I/O 08/28/91 22.24 */ /* mod for pid-dependent temp file for swapping 09/02/91 20.52 */ /* fix stack-overflow check 10/30/91 20.46 */ /* better dly_free_blist 01/12/92 14.41 */ /* E/ command 03/30/92 21.51 */ /* 4 buffers/windows 03/10/93 21.36 */ /* assignable windows/buffers 12/10/93 10.03 */ #include #include #include #include #include "te_defs.h" #ifdef FAR_BUFFER #ifdef SWAP #define DO_SWAP 1 #include "swap.h" #endif void _far * _far _fmemcpy( void _far *dest, void _far *src, size_t count ); char * near_buff(char _far *, char *); #else #define near_buff(x, y) x #endif /* FAR_BUFFER */ /* function declarations */ static short do_eq(void), do_eq1(void), do_en(void); static short do_glob(struct qh *qbuff); static int read_stream(FILE *file, long *ff_found, struct qp *rbuff, long *nchars, short endsw, short crlf_sw, short ff_sw); int read_file(struct qp *rbuff, long *nchars, short endsw); void write_file(struct qp *wbuff, long nchars, short ffsw); void pop_iteration(short); static void write_stream(FILE *file, struct qp *wbuff, long nchars, short crlf_sw); static void do_en_next(void); static short read_filename(short, char); static void find_endcond(unsigned char); static struct qh oldcstring; /* hold command string during ei */ static char back_name[CELLSIZE]; /* temp file name */ /* file stuff for input/output files */ unsigned short filbuf = FILBUF0; /* identifies FILBUFn as current filename buffer */ struct infiledata in_files[4] = /* input files */ { { NULL, -1 }, { NULL, -1 }, { NULL, -1 }, { NULL, -1 } }; #define pi_file in_files[0] #define si_file in_files[1] struct infiledata *infile = &pi_file; /* pointer to currently active input file structure */ struct outfiledata out_files[4] = { { (FILE *) 0, "tecoxxxx\000", "", "", "", "", "", "", 0, 0, "tm0" }, { (FILE *) 0, "tecoxxxx\000", "", "", "", "", "", "", 0, 0, "tm1" }, { (FILE *) 0, "tecoxxxx\000", "", "", "", "", "", "", 0, 0, "tm2" }, { (FILE *) 0, "tecoxxxx\000", "", "", "", "", "", "", 0, 0, "tm3" } }; #define po_file out_files[0]; #define so_file out_files[1]; struct outfiledata *outfile = &po_file; /* pointer to currently active output file structure */ FILE *eisw; /* indirect command file pointer */ static short active_buff = 0; /* variable for which buffer is selected */ /* process E commands */ void do_e() { unsigned char c; /* temps */ short old_var; FILE *t_eisw; short active_physbuff; #ifdef FAR_BUFFER char local_near[CELLSIZE]; #endif switch (mapch_l[getcmdc(trace_sw)]) /* read next character and dispatch */ { /* numeric values */ case 'd': /* ED */ set_var(1, &ed_val); break; case 'o': /* EO */ if (!esp->flag1) { esp->val1 = VERSION; esp->flag1 = 1; } else esp->flag1 = 0; break; case 's': /* ES */ set_var(0, &es_val); break; case 't': /* ET */ old_var = et_val; set_var(1, &et_val); et_val = (et_val & ET_MASK) | (old_var & ~ET_MASK); /* force read_only bits */ if ((et_val ^ old_var) & ET_TRUNC) window(WIN_REDRAW); /* redraw if ET & 256 changes */ break; case 'u': /* EU */ set_var(0, &eu_val); break; case 'v': /* EV */ set_var(0, &ev_val); break; case 'z': /* EZ */ old_var = ez_val; set_var(0, &ez_val); tabmask = (ez_val & EZ_TAB4) ? 3 : 7; /* set tab mask */ if ((ez_val ^ old_var) & EZ_TAB4) window(WIN_REDRAW); /* force window redisplay if EZ_TAB4 changes */ if (old_var & ~ez_val & EZ_MOUSE) clear_mouse(); /* turning off mouse bit? clear mouse */ char_mask = (ez_val & EZ_EIGHTBIT) ? 0377 : 0177; break; /* select primary or secondary buffer or reassociate buffer with window */ case 'e': /* EE */ if (!colonflag) { if (!esp->flag1) { esp->val1 = active_buff; /* return current buffer selector */ esp->flag1 = 1; } else { if (esp->val1 < 0 || esp->val1 >= MAX_BUFFERS) ERROR(E_IBS); pbuff = &lbuffs[esp->val1]; active_buff = esp->val1; esp->flag1 = 0; if (ez_val & EZ_AUTOIO) /* if EE switches input streams automatically */ { active_physbuff = pbuff->phys - &buffs[0]; filbuf = FILBUF0 + active_physbuff; infile = &in_files[active_physbuff]; outfile = &out_files[active_physbuff]; } } } else /* associate a buffer with a window */ { if (!esp->flag1) esp->val1 = active_buff; /* default to current window */ if (esp->val1 < 0 || esp->val1 >= MAX_BUFFERS) ERROR(E_IBS); if (!esp->flag2) /* "get" */ { esp->val1 = lbuffs[esp->val1].phys - &buffs[0]; esp->flag1 = 1; } else /* "set" */ { if (esp->val2 < 0 || esp->val2 >= MAX_BUFFERS) ERROR(E_IBS); lbuffs[esp->val1].phys = &buffs[esp->val2]; esp->flag2 = esp->flag1 = 0; /* consume args */ buffs[esp->val2].buff_mod = 0; /* touch buffer to force redisplay */ if (esp->val1 == active_buff) /* if updating current buffer */ { filbuf = FILBUF0 + esp->val2; /* update the current files too */ infile = &in_files[esp->val2]; outfile = &out_files[esp->val2]; } } colonflag = 0; } break; /* E_ search */ case '_': do_nsearch('e'); break; /* file I/O commands */ case 'a': /* set secondary output */ if (ez_val & EZ_AUTOIO) ERROR(E_IOS); filbuf = FILBUF1; outfile = &so_file; break; case 'b': /* open read/write with backup */ if (!read_filename(1, 'r')) ERROR(E_NFI); /* read the name */ if (infile->fd) fclose(infile->fd); /* close previously-open file */ if (!(infile->fd = fopen(near_buff(fbuf.f->ch, local_near), "r"))) { if (!colonflag) ERROR(E_FNF); } else { if (outfile->fd) ERROR(E_OFO); /* output file already open */ for (ll = 0; ll < CELLSIZE; ll++) /* save file string */ if ((outfile->f_name[ll] = fbuf.f->ch[ll]) == '\0') break; outfile->name_size = ll; /* parse file name and make temp file in same directory */ (void) itoa(getpid(), &outfile->proto[4], 16); /* put pid into temp file name */ _splitpath(outfile->f_name, outfile->drive, outfile->dir, outfile->fname, outfile->extens); _makepath(outfile->t_name, outfile->drive, outfile->dir, outfile->proto, outfile->temp_ext); if (!(outfile->fd = fopen(outfile->t_name, "w"))) ERROR(E_COF); outfile->bak = 1; /* set backup mode */ } infile->eofsw = -1 - (esp->val1 = (infile->fd) ? -1 : 0); esp->flag1 = colonflag; colonflag = 0; break; case 'x': /* finish edit and exit */ exitflag = -1; /* --- fall through to "EC" --- */ case 'c': /* finish edit */ set_pointer(0L, &aa); /* set a pointer to start of text buffer */ write_file(&aa, pbuff->phys->z, (short) pbuff->phys->ctrl_e); /* write the current buffer */ pbuff->dot = pbuff->phys->z = 0; /* empty the buffer */ window(WIN_REDRAW); /* force window redraw */ if ((outfile->fd) && (infile->fd) && !(infile->eofsw)) /* if any input remaining, copy it to output */ while ((c = getc(infile->fd)) != EOF) putc(c, outfile->fd); /* --- fall through to "EF" --- */ case 'f': /* close output file */ if (outfile->fd) /* only if one is open */ { fclose(outfile->fd); if (outfile->bak & 1) /* if this is "backup" mode */ { _makepath(back_name, outfile->drive, outfile->dir, outfile->fname, "bak"); /* rename orig. file .bak */ (void) unlink(back_name); /* remove any previous ".bak" file */ rename(outfile->f_name, back_name); /* rename orig file */ } if (!(outfile->bak & 8)) /* if output file had temp name */ { unlink(outfile->f_name); /* remove any existing file with same name */ rename(outfile->t_name, outfile->f_name); /* rename output */ } } outfile->fd = NULL; /* mark "no output file open" */ if (exitflag == -1) /* if this is EX command */ { for (active_buff = 0; active_buff < MAX_BUFFERS; active_buff++) { pbuff = &lbuffs[active_buff]; if (ez_val & EZ_AUTOIO) { filbuf = FILBUF0 + active_buff; infile = &in_files[active_buff]; outfile = &out_files[active_buff]; } if (pbuff->phys->z != 0 || outfile->fd != 0) ERROR(E_FBC); /* if file open and/or buffer not empty, error */ } } break; case 'i': /* indirect file execution */ if (!read_filename(1, 'i')) /* if no filename specified, reset command input */ { if (eisw) /* if ending a file execute, restore the previous "old command string" */ { fclose(eisw); /* return the file descriptor */ cbuf.f->usecount = 2; /* mark command string as two links, so it won't get returned immediately */ dly_free_blist(cbuf.f); /* return the command string used by the file (after execution done) */ cbuf.f = oldcstring.f; cbuf.z = oldcstring.z; } t_eisw = 0; } else if (!(t_eisw = fopen(near_buff(fbuf.f->ch, local_near), "r"))) { if (!colonflag) ERROR(E_FNF); } else if (!eisw) /* if this "ei" came from the command string */ { oldcstring.f = cbuf.f; /* save current command string */ oldcstring.z = cbuf.z; cbuf.f = NULL; /* and make it inaccessible to "rdcmd" */ } if (eisw) fclose(eisw); /* if a command file had been open, close it */ esp->val1 = (eisw = t_eisw) ? -1 : 0; esp->flag1 = colonflag; colonflag = 0; esp->op = OP_START; break; case 'k': /* kill output file */ kill_output(outfile); break; case 'p': /* switch to secondary input */ if (ez_val & EZ_AUTOIO) ERROR(E_IOS); filbuf = FILBUF1; infile = &si_file; break; case 'r': /* specify input file, or switch to primary input */ if (!read_filename(0, 'r')) { if (ez_val & EZ_AUTOIO) ERROR(E_IOS); filbuf = FILBUF0; infile = &pi_file; /* no name, switch to primary input */ } else { if (infile->fd) fclose(infile->fd); /* close previously-open file */ if (!(infile->fd = fopen(near_buff(fbuf.f->ch, local_near), "r"))) { if (!colonflag) ERROR(E_FNF); } } infile->eofsw = -1 - (esp->val1 = (infile->fd) ? -1 : 0); esp->flag1 = colonflag; colonflag = 0; esp->op = OP_START; break; case 'w': /* specify output file, or switch to primary output */ if(!read_filename(0, 'w')) { if (ez_val & EZ_AUTOIO) ERROR(E_IOS); filbuf = FILBUF0; outfile = &po_file; } else { if (outfile->fd) ERROR(E_OFO); /* output file already open */ for (ll = 0; ll < CELLSIZE; ll++) /* save file string */ if ((outfile->t_name[ll] = outfile->f_name[ll] = fbuf.f->ch[ll]) == '\0') break; outfile->name_size = ll; if (!(ez_val & EZ_NOTMPFIL)) /* if not using literal output name */ { /* parse file name and make temp file in same directory */ (void) itoa(getpid(), &outfile->proto[4], 16); /* put pid into temp file name */ _splitpath(outfile->f_name, outfile->drive, outfile->dir, outfile->fname, outfile->extens); _makepath(outfile->t_name, outfile->drive, outfile->dir, outfile->proto, outfile->temp_ext); } else unlink(outfile->t_name); /* if literal output, delete any existing */ if (!(outfile->fd = fopen(outfile->t_name, "w"))) ERROR(E_COF); outfile->bak = ez_val & EZ_NOTMPFIL; /* save "temp suffix" status */ } break; case 'y': /* EY is Y without protection */ if (esp->flag1) ERROR(E_NYA); pbuff->dot = pbuff->phys->z = 0; /* clear buffer */ set_pointer(0L, &aa); read_file(&aa, &pbuff->phys->z, (ed_val & ED_EXPMEM ? -1 : 0) ); esp->flag1 = colonflag; colonflag = 0; esp->op = OP_START; break; case 'n': /* wildcard filespec */ esp->val1 = do_en(); esp->flag1 = colonflag; colonflag = 0; esp->op = OP_START; break; case '/': /* change directory */ esp->val1 = do_e_slash(); esp->flag1 = colonflag; colonflag = 0; esp->op = OP_START; break; case 'q': /* system command */ esp->val1 = do_eq(); /* do this as a separate routine */ if (!(esp->flag1 = colonflag) && !esp->val1) ERROR(E_SYS); colonflag = 0; esp->op = OP_START; break; default: ERROR(E_IEC); } } /* routine to execute a system command */ /* this is done by forking off another process */ /* to execute a shell via 'execl' */ /* routine returns 0 if success, system error code if error */ #ifdef DO_SWAP static char swap_file[] = "teco0000.swp"; /* file for swapping to disk */ #endif /* DO_SWAP */ static short do_eq() { short t; #ifdef FAR_BUFFER char local_near[CELLSIZE+3]; #endif /* FAR_BUFFER */ #ifdef DO_SWAP unsigned char exec_ret; /* returned code from executed routine - NOT USED YET */ char *comspec; /* get path to command.com */ int pid; /* storage for pid */ #endif /* DO_SWAP */ build_string(&sysbuf); if (!sysbuf.f) ERROR(E_STR); /* error if default is empty string */ if (sysbuf.z > CELLSIZE-2) ERROR(E_STL); /* command must fit within one cell */ sysbuf.f->ch[sysbuf.z] = '\0'; /* store terminating null */ if (!esp->flag1) /* if not m,nEQ command */ { if (win_data[7]) window(WIN_SUSP); /* restore full screen */ crlf(); /* force characters out */ setup_tty(TTY_SUSP); /* restore terminal to normal mode */ #ifdef DO_SWAP if ((ez_val & EZ_SWAP) != 0) /* swap for EQ ? */ { comspec = getenv("COMSPEC"); /* get command.com pointer */ local_near[0] = '/'; local_near[1] = 'C'; local_near[2] = ' '; (void) near_buff(sysbuf.f->ch, local_near+3); pid = getpid(); /* get the "pid" */ if (pid < 0x1000) (void) itoa(pid, &swap_file[5], 16); /* put pid into temp file name */ else (void) itoa(pid, &swap_file[4], 16); swap_file[8] = '.'; /* put back what itoa overwrote with '\0' */ t = swap(comspec, local_near, &exec_ret, swap_file); } else #endif /* DO_SWAP */ t = system(near_buff(sysbuf.f->ch, local_near)); /* call the named routine */ setup_tty(TTY_RESUME); /* restore terminal to teco mode */ if (win_data[7] != 0) /* if window was enabled */ { if (t == 0) /* if there was output */ { vt(VT_SETSPEC1); /* set reverse video */ out_str("Type ENTER to continue"); /* require CR before continuing */ vt(VT_CLRSPEC); /* reverse video off */ while (gettty() != LF); type_char(CR); /* back to beginning of line */ vt(VT_EEOL); /* and erase the message */ } window(WIN_RESUME); /* re-enable window */ window(WIN_REDRAW); /* and make it redraw afterwards */ } } else t = do_eq1(); /* m,nEQ command */ return( (t == 0) ? -1 : 0); /* return failure if spawn failed */ } /* Execute m,nEQtext$ command. Run "text" as a DOS command that */ /* receives its std input from chars m through n of teco's buffer. */ /* Output from the command is placed in Q#. */ static short do_eq1(void) { char tfile_in[L_tmpnam], tfile_out[L_tmpnam]; /* temp file names */ #ifdef DO_SWAP unsigned char *comspec; unsigned char exec_ret; #endif /* DO_SWAP */ FILE *temp_in = NULL; FILE *temp_out; char eq1_cmd[CELLSIZE+15]; register char *p; short i, rtn_val, redirect_flag = 0; if (!tmpnam(tfile_in)) ERROR(E_SYS); /* make file names */ if (!tmpnam(tfile_out)) ERROR(E_SYS); p = eq1_cmd; #ifdef DO_SWAP if ((ez_val & EZ_SWAP) != 0) { *p++ = '/'; *p++ = 'C'; *p++ = ' '; } #endif /* DO_SWAP */ for (i = 0; i < sysbuf.z; i++) *p++ = sysbuf.f->ch[i]; /* make command */ *p = '\0'; /* temporary end code */ for (p = eq1_cmd; *p != '\0'; ) /* scan for redirection */ { switch (*p) { case '\"': while (*++p != '\"' && *p != '\0'); break; /* quote - scan for matching quote */ case '\'': while (*++p != '\'' && *p != '\0'); break; /* apostrophe - as above */ case '<': redirect_flag |= 1; break; /* < found */ case '>': redirect_flag |= 2; break; /* > found */ } if (*p != '\0') ++p; } if (!(redirect_flag & 1)) /* if no explicit input redirection */ { *p++ = ' '; *p++ = '<'; /* redirect its input */ ll = line_args(1, &aa); /* set aa to start of text, ll to number of chars */ pbuff->dot += ll; /* set pointer at end of text */ ctrl_s = -ll; /* set ^S to - # of chars */ if (ll > 0) /* if there are characters to send */ { for (i = 0; tfile_in[i] != '\0'; i++) *p++ = tfile_in[i]; /* < infile */ if ((temp_in = fopen(tfile_in, "w")) == 0) /* open pipe for writing; exit if open fails */ ERROR(E_SYS); write_stream(temp_in, &aa, ll, 0); /* write to stream, CRLF becomes LF */ fclose(temp_in); } else { *p++ = 'N'; /* no input: redirect from NUL */ *p++ = 'U'; *p++ = 'L'; } *p = '\0'; } if (!(redirect_flag & 2)) /* if no explicit output redirection */ { *p++ = ' '; *p++ = '>'; /* redirect its output */ for (i = 0; tfile_out[i] != '\0'; i++) *p++ = tfile_out[i]; /* copy output name */ *p++ = '\0'; /* terminating null */ } setup_tty(TTY_SUSP); if (win_data[7]) window(WIN_SUSP); #ifdef DO_SWAP if ((ez_val & EZ_SWAP) != 0) { comspec = getenv("COMSPEC"); /* get command.com pointer */ rtn_val = swap(comspec, eq1_cmd, &exec_ret, "tecoswap.tmp"); } else #endif /* DO_SWAP */ rtn_val = system(eq1_cmd); /* call the named routine */ if (!(redirect_flag & 1) && temp_in != 0) unlink(tfile_in); /* delete its input file, if any */ setup_tty(TTY_RESUME); if (win_data[7]) window(WIN_RESUME), window(WIN_REDRAW); if(!(redirect_flag & 2) && !rtn_val) { make_buffer(&timbuf); /* initialize the q# header */ bb.p = timbuf.f; /* init bb to point to q# */ timbuf.z = bb.c = 0; if (rtn_val == 0 && (temp_out = fopen(tfile_out, "r")) == 0) /* open the "std out" pipe for reading */ rtn_val = -1; else { read_stream(temp_out, 0L, &bb, &timbuf.z, 0, 0, 1); /* read from file to q# */ fclose(temp_out); unlink(tfile_out); } } return(rtn_val); } /* routine to change directory */ short do_e_slash() { int rv = -1; make_buffer(&dirname); /* create a buffer */ if (build_string(&dirname) == 0) /* read the string */ rv = 0; /* error if empty string */ if (dirname.z > CELLSIZE-1) /* if string too long */ ERROR(E_STL); else dirname.f->ch[dirname.z] = '\0'; /* terminating null */ if (rv != 0) rv = set_wd(0); if (rv == 0 && colonflag == 0) /* if failure and not :E/, abort with error */ ERROR(E_DIR); else init_wd(0); return(rv); } /* routines to manage current drive/directory */ static char orig_dir[CELLSIZE+1]; void init_wd(short a) { char dir_buff[CELLSIZE+1]; /* temporary buffer */ char *p = dir_buff; char *p1 = orig_dir; make_buffer(&dirbuf); if (getcwd(p, CELLSIZE+1) != 0) /* if getcwd succeeded */ { for (dirbuf.z = 0; dirbuf.z < CELLSIZE && *p != '\0'; dirbuf.z++) { if (a) *p1++ = *p; dirbuf.f->ch[dirbuf.z] = *p++; } } else dirbuf.z = 0; } /* set directory (and maybe drive) */ short set_wd(short a) { short rv = -1; short dcode; short drive_save = 0; #ifdef FAR_BUFFER char local_near[CELLSIZE]; #endif char *p = (a) ? orig_dir : near_buff(dirname.f->ch, local_near); if (p == 0) rv = 0; else if (*(p+1) == ':') /* if drive spec */ { drive_save = _getdrive(); if (*p >= 'a' && *p <= 'z') dcode = *p - 'a' + 1; else if (*p >= 'A' && *p <= 'Z') dcode = *p - 'A' + 1; else rv = 0; if (rv == -1) p += 2; if (rv == -1 && _chdrive(dcode) != 0) rv = 0; } if (rv == -1 && *p != '\0' && chdir(p) != 0) rv = 0; /* change directory */ if (rv == 0 && drive_save != 0) _chdrive(drive_save); /* restore drive if error */ return(rv); } /* Routines to handle EN wild-card file specification */ /* ENfilespec$ defines file class, leaving 'filespec' */ /* in filespec buffer and reading expanded list of */ /* files into local buffer. EN$ gets next filespec */ /* into filespec buffer. */ static struct qh en_buf; /* header for storage for file list */ static struct qp en_ptr; /* pointer to load/read file list */ static short do_en() { short t; if (build_string(&fbuf)) /* if a file string is specified */ { if (fbuf.z > CELLSIZE-2) ERROR(E_STL); /* upper limit on string length */ fbuf.f->ch[fbuf.z] = '\0'; /* terminating null */ t = do_glob(&en_buf); /* glob the result */ en_ptr.p = en_buf.f; /* set the buffer pointer to the beginning of the buffer */ en_ptr.dot = en_ptr.c = 0; } else /* if no string, get next filename */ { do_en_next(); t = (fbuf.z) ? -1 : 0; /* t zero if no more filespecs */ if (!t && !colonflag) ERROR(E_NFI); /* if no colon, end of spec is an error */ } return (t); } /* routine to expand the string in the filespec buffer */ /* argument is the address of a qh that gets the expanded string */ /* argument->v gets set to the number of file specs found */ short do_glob(struct qh *gbuff) { struct qp glob_ptr; char drive[_MAX_DRIVE], dir[_MAX_DIR], fname[_MAX_FNAME], ext[_MAX_EXT]; struct find_t fyle; char t_filename[_MAX_PATH]; /* temp file name */ short i; char tc; #ifdef FAR_BUFFER char local_near[CELLSIZE]; #endif make_buffer(gbuff); /* initialize expanded file buffer */ glob_ptr.p = gbuff->f; /* initialize pointer to buffer */ glob_ptr.c = glob_ptr.dot = gbuff->z = 0; _splitpath(near_buff(fbuf.f->ch, local_near), drive, dir, fname, ext); /* split file spec into its components */ for (gbuff->v = 0; ; gbuff->v++) /* loop through file names; count files */ { if (!gbuff->v) { if (_dos_findfirst(near_buff(fbuf.f->ch, local_near), _A_NORMAL, &fyle) != 0) break; /* exit if can't find more files */ } else if (_dos_findnext(&fyle) != 0) break; _makepath(t_filename, drive, dir, fyle.name, ""); for (i = 0; ; i++) { tc = glob_ptr.p->ch[glob_ptr.c] = t_filename[i]; fwdcx(&glob_ptr); if (tc == '\0') break; } } gbuff->z = glob_ptr.dot; /* save char count */ return (glob_ptr.dot != 0); } /* routine to get next file spec from "EN" list into filespec buffer */ static void do_en_next() { char c; make_buffer(&fbuf); /* initialize the filespec buffer */ fbuf.z = 0; while(en_ptr.dot < en_buf.z) /* stop at end of string */ { c = en_ptr.p->ch[en_ptr.c]; fwdc(&en_ptr); if (!c) break; /* null is terminator between file specs */ fbuf.f->ch[fbuf.z++] = c; /* store char */ if (fbuf.z >= CELLSIZE-1) ERROR(E_STL); /* limit on filespec size */ } fbuf.f->ch[fbuf.z] = '\0'; /* filespec ends with NULL */ } /* routine to read a file name into fbuf area */ /* returns nonzero if a name, 0 if none */ /* flag nonzero => empty name clears filespec buffer */ /* func is 'r' for ER or EB cmds, 'i' for EI, 'w' for EW */ static short read_filename(short flag, char func) { short i, t, glob1; char c; struct qh temp_buff; /* temp buffer for globbing filespec */ char glob_near[CELLSIZE]; if (!(t = build_string(&fbuf))) /* if no name spec'd */ { if (flag) fbuf.z = 0; /* if no name spec'd, set length to 0 */ } else { if (fbuf.z > CELLSIZE-2) ERROR(E_STL); fbuf.f->ch[fbuf.z] = '\0'; /* store terminating NULL */ /* check for wildcard characters */ for (glob1 = i = 0; i < fbuf.z; i++) if ((c = fbuf.f->ch[i]) == '*' || c == '?') ++glob1; if (glob1) /* one of these was found */ { temp_buff.f = NULL; /* make a temp buffer to glob filename into */ make_buffer(&temp_buff); do_glob(&temp_buff); /* expand the file name */ if (temp_buff.z == 0) /* no match */ { free_blist(temp_buff.f); /* return the storage */ ERROR(func == 'w' ? E_COF : E_FNF); /* "can't open" or "file not found" */ } else if (temp_buff.v == 0) /* if exactly one file name */ { free_blist(fbuf.f); /* return the old file spec */ fbuf.f = temp_buff.f; /* put the temp buffer there instead */ fbuf.z = temp_buff.z; if (fbuf.z > CELLSIZE-2) ERROR(E_STL); fbuf.f->ch[fbuf.z] = '\0'; if (func == 'w' && glob1) /* if this is EW and 'twas from a wildcard expansion */ { vt(VT_SETSPEC1); /* "file XXXX already exists: overwrite? [yn]" */ out_str("File "); out_str(near_buff(fbuf.f->ch, glob_near)); out_str(" already exists: overwrite? [ny] "); vt(VT_CLRSPEC); c = gettty(); /* read user's response */ putchar(CR); vt(VT_EEOL); /* clean up the screen */ if (c != 'y' && c != 'Y') ERROR(E_COF); /* abort here */ } } else /* multiple file specs */ { if (func != 'r' || !(ez_val & EZ_MULT)) /* if multiple file specs here aren't allowed */ { free_blist(temp_buff.f); /* return the storage */ ERROR(E_AMB); } free_blist(en_buf.f); /* substitute the expansion for the "EN" list */ en_ptr.p = en_buf.f = temp_buff.f; /* and initialize the "EN" list pointer */ en_buf.z = temp_buff.z; en_ptr.dot = en_ptr.c = 0; do_en_next(); /* get the first file */ } } } return(t); } #ifdef FAR_BUFFER /* routine to make a local copy of a far character string */ char *near_buff(char _far *farstring, char *near_buffer) { char _far *buffptr = (char _far *) near_buffer; (void) _fmemcpy(buffptr, farstring, CELLSIZE); return(near_buffer); } #endif /* fetch or set variable */ /* extend is nonzero if arg is 16 bits to be sign extended */ /* arg is pointer to variable */ void set_var(short extend, long *arg) { if (esp->flag1) /* if an argument, then set the variable */ { if (esp->flag2) /* if two arguments, then , */ *arg = (*arg & ~esp->val2) | esp->val1; else *arg = esp->val1; /* one arg is new value */ if (extend) *arg &= 0x0000ffff; /* if a 16-bit value, trim to 16 bits */ esp->flag2 = esp->flag1 = 0; /* consume argument */ } else /* otherwise fetch the variable's value */ { esp->val1 = *arg; if (extend && (esp->val1 & 0x8000)) esp->val1 |= 0xffff0000; esp->flag1 = 1; } } /* read from selected input file stream into specified buffer */ /* terminate on end-of-file or form feed */ /* if endsw > 0 terminates after that many lines */ /* if endsw < 0 stops if z > BUFF_LIMIT */ /* returns -1 if read EOF, 0 otherwise */ int read_file(struct qp *rbuff, long *nchars, short endsw) { if (!infile->fd) infile->eofsw = -1, pbuff->phys->ctrl_e = 0; /* return if no input file open */ else infile->eofsw = read_stream(infile->fd, &pbuff->phys->ctrl_e, rbuff, nchars, endsw, (short) (ez_val & EZ_CRLF), (short) (ez_val & EZ_READFF)); return(esp->val1 = infile->eofsw); } /* read from an I/O stream into specified buffer */ /* this is used by read_file and by "eq" pipe from other Unix processes */ /* args buff, nchars, endsw as above; file is stream pointer, ff_found is */ /* address of a switch to set if read ended with a FF, crlf_sw is lf->crlf */ /* conversion, ff_sw indicates whether to stop on a form feed. */ static int read_stream(FILE *file, long *ff_found, struct qp *rbuff, long *nchars, short endsw, short crlf_sw, short ff_sw) { char chr; short crflag; register BUFFPTR p; register short c; p = rbuff->p; /* copy pointer locally */ c = rbuff->c; crflag = 0; /* "last char wasn't CR" */ while (((chr = getc(file)) != EOF) && ((chr != FF) || ff_sw)) { if ((chr == LF) && !crflag && !crlf_sw) /* automatic cr before lf */ { p->ch[c] = CR; /* store a cr */ ++(*nchars); /* increment buffer count */ if (++c > CELLSIZE-1) /* next cell? */ { if (!p->f) /* need a new cell? */ { p->f = get_bcell(); p->f->b = p; } p = p->f; c = 0; } } p->ch[c] = chr; /* store char */ ++(*nchars); /* increment character count */ if (++c > CELLSIZE-1) /* next cell? */ { if (!p->f) /* need a new cell? */ { p->f = get_bcell(); p->f->b = p; } p = p->f; c = 0; } crflag = (chr == CR); /* flag set if last char was CR */ if ((chr == LF) && ((endsw < 0 && pbuff->phys->z > BUFF_LIMIT) || (endsw > 0 && --endsw == 0))) /* term after N lines */ break; } rbuff->p = p; /* update pointer */ rbuff->c = c; if (ff_found) *ff_found = (chr == FF) ? -1 : 0; /* set flag to indicate whether FF found */ return( (chr == EOF) ? -1 : 0); /* and return "eof found" value */ } /* routine to write text buffer out to selected output file */ /* arguments are qp to start of text, number of characters, */ /* and an "append FF" switch */ void write_file(struct qp *wbuff, long nchars, short ffsw) { if (!outfile->fd && (nchars)) ERROR(E_NFO); else write_stream(outfile->fd, wbuff, nchars, (short) (ez_val & EZ_CRLF)); if (outfile->fd && ffsw) putc(FF, outfile->fd); } /* routine to write text buffer to I/O stream. Used by */ /* write_file, above, and by "eq" write to pipe to other */ /* Unix processes. Arguments wbuff, nchars as above; file */ /* is stream pointer, crlf_sw zero converts CRLF to LF */ static void write_stream(FILE *file, struct qp *wbuff, long nchars, short crlf_sw) { char c; short crflag; crflag = 0; for (; nchars > 0; nchars--) { if ((c = (*wbuff).p->ch[(*wbuff).c]) == CR) crflag = 1; /* set flag if a c.r. */ else { if ((crflag) && ((c != LF) || crlf_sw)) /* if c.r. not before lf, or if not in */ putc(CR, file); /* "no cr" mode, output the c.r. */ crflag = 0; putc(c, file); } if (++(*wbuff).c > CELLSIZE-1) { (*wbuff).p = (*wbuff).p->f; (*wbuff).c = 0; } } } /* routine to kill output file: argument is pointer to an output file structure */ void kill_output(struct outfiledata *outptr) { if (outptr->fd) { fclose(outptr->fd); remove(outptr->t_name); outptr->fd = NULL; } } /* do "F" commands */ void do_f(void) { BUFFPTR delete_p; int temp_dot; void display_lines(); void do_fq(); switch (mapch_l[getcmdc(trace_sw)]) /* read next character and dispatch */ { case '<': /* back to beginning of current iteration */ if (cptr.flag & F_ITER) /* if in iteration */ { cptr.p = cptr.il->p; /* restore */ cptr.c = cptr.il->c; cptr.dot = cptr.il->dot; } else for (cptr.dot = cptr.c = 0; cptr.p->b->b != NULL; cptr.p = cptr.p->b); /* else, restart current macro */ break; case '>': /* to end of current iteration */ find_enditer(); /* find it */ if ( ( ((esp->flag1) ? esp->val1 : srch_result) >= 0) ? (~colonflag) : colonflag) /* if exit */ pop_iteration(0); /* and exit if appropriate */ break; case '\'': /* to end of conditional */ case '|': /* to "else," or end */ find_endcond(cmdc); break; /* "F" search commands */ case 'b': /* bounded search, alternative args */ do_fb(); break; case 'c': /* bounded search, alternative args, then "FR" */ if (do_fb()) goto do_fr; while (getcmdc(trace_sw) != term_char); /* otherwise skip insert string */ break; case 'n': /* do "N" and then "FR" */ if (do_nsearch('n')) goto do_fr; while (getcmdc(trace_sw) != term_char); /* otherwise skip insert string */ break; case '_': /* do "_" and then "FR" */ if (do_nsearch('_')) goto do_fr; while (getcmdc(trace_sw) != term_char); /* otherwise skip insert string */ break; case 's': /* search and replace: search, then do "FR" */ build_string(&sbuf); /* read search string and terminator */ if (end_search( do_search( setup_search() ) )) goto do_fr; /* if search passed, do "FR" */ while (getcmdc(trace_sw) != term_char); /* otherwise skip insert string */ break; case 'r': /* replace last insert, search, etc. */ if (esp->flag1) /* argument defines number of chars to delete */ { if (esp->flag2) /* if two arguments */ { ctrl_s = -line_args(1, &cc); /* dot to beginning, ctrl_s to -length (cc red herring) */ pbuff->dot -= ctrl_s; /* dot to end now - real FR conditions */ } else if (esp->val1 >= 0) /* if deleting forward */ { if ((pbuff->dot + esp->val1) > pbuff->phys->z) ERROR(E_POP); /* dot after, error if beyond buffer */ pbuff->dot += esp->val1; ctrl_s = -esp->val1; /* delete length */ } else /* deleting backward */ { if ((pbuff->dot + esp->val1) < 0) ERROR(E_POP); /* error if before buffer */ ctrl_s = esp->val1; } esp->flag1 = 0; /* consume arg in single-arg case */ } term_char = (atflag) ? getcmdc(trace_sw) : ESC; /* set terminator */ atflag = 0; do_fr: /* entry from FN, F_, and FC */ set_pointer(pbuff->dot, &cc); /* save a pointer to the current spot */ if ((pbuff->dot + ctrl_s) < 0) ctrl_s = -pbuff->dot; /* don't FR before buffer */ pbuff->dot += ctrl_s; /* back dot up over the string */ set_pointer(pbuff->dot, &aa); /* code from "insert1": convert dot to a qp */ delete_p = aa.p; /* save beginning of original cell */ if (pbuff->dot < pbuff->phys->buff_mod) pbuff->phys->buff_mod = pbuff->dot; /* update earliest char loc touched */ insert_p = bb.p = get_bcell(); /* get a new cell */ bb.c = 0; ins_count = aa.c; /* save char position of dot in cell */ aa.c = 0; movenchars(&aa, &bb, ins_count); /* copy cell up to dot */ moveuntil(&cptr, &bb, term_char, &ins_count, cptr.z - cptr.dot, trace_sw); /* insert */ cptr.dot += ins_count; /* advance command-string pointer */ getcmdc(trace_sw); /* skip terminator */ pbuff->phys->z += ctrl_s; /* subtract delete length from buffer count */ delete_p->b->f = insert_p; /* put the new cell where the old one was */ insert_p->b = delete_p->b; /* code borrowed from "insert2" */ insert_p = NULL; if (bb.c == cc.c) /* if replacement text was same length, we can save time */ { for (; bb.c < CELLSIZE; bb.c++) bb.p->ch[bb.c] = cc.p->ch[bb.c]; /* copy rest of cell */ bb.p->f = cc.p->f; /* replace orig cell's place in chain with end of new list */ if (bb.p->f) bb.p->f->b = bb.p; cc.p->f = NULL; /* terminate the part snipped out */ free_blist(delete_p); /* return the old part */ } else /* different positions: shift the remainder of the buffer */ { bb.p->f = delete_p; /* splice rest of buffer to end */ delete_p->b = bb.p; movenchars(&cc, &bb, pbuff->phys->z - pbuff->dot); /* squeeze buffer */ free_blist(bb.p->f); /* return unused cells */ bb.p->f = NULL; /* and end the buffer */ } pbuff->phys->z += ins_count; /* add # of chars inserted */ pbuff->dot += ins_count; ctrl_s = -ins_count; /* save string length */ esp->op = OP_START; break; case 'd': /* search and delete (same as FStext$$) */ build_string(&sbuf); /* read search string and terminator */ if (end_search( do_search( setup_search() ) )) /* if search passed, delete that many chars */ { pbuff->dot += ctrl_s; /* back pointer up over search string */ delete1(-ctrl_s); /* delete characters */ } ctrl_s = 0; /* set string length to 0 */ break; case 'k': /* search; delete everything between "before" and "after" pos'ns */ temp_dot = pbuff->dot; /* save "before" */ build_string(&sbuf); /* read search string and terminator */ if (end_search( do_search( setup_search() ) )) /* if search passed, delete that many chars */ { if (pbuff->dot > temp_dot) /* if search was forward */ { ctrl_s = temp_dot - pbuff->dot; pbuff->dot = temp_dot; } else ctrl_s = pbuff->dot - temp_dot; /* search was backward */ delete1(-ctrl_s); } ctrl_s = 0; /* zero string length */ break; case '[': /* push numeric part only of Q-register */ if (qsp >= &qstack[QSTACKSIZE-1]) ERROR(E_PDO); /* stack full */ else { mm = getqspec(1, getcmdc(trace_sw)); /* get the q reg name */ (++qsp)->f = NULL; /* store a zero text pointer */ qsp->v = qreg[mm].v; /* and the value */ } break; case 'l': /* move a number of screen lines */ display_lines(); break; case 'm': /* mouse commands */ do_mouse(); break; case 'q': /* convert func. key code to q spec */ do_fq(); break; case 'x': /* "external" expansion command */ te_fx(); break; default: ERROR(E_IFC); } } /* routines for macro iteration */ /* pop iteration: if arg nonzero, exit unconditionally */ /* else check exit conditions and exit or reiterate */ void pop_iteration(short arg) { if (!arg && (!cptr.il->dflag || (--(cptr.il->count) > 0)) ) /* if reiteration */ { cptr.p = cptr.il->p; /* restore */ cptr.c = cptr.il->c; cptr.dot = cptr.il->dot; } else { if (cptr.il->b) cptr.il = cptr.il->b; /* if not last thing on stack, back up */ else cptr.flag &= ~F_ITER; /* else clear "iteration" flag */ } } /* find end of iteration - read over arbitrary <> and one > */ void find_enditer() { register short icnt; for (icnt = 1; icnt > 0;) /* scan for matching > */ { while ((skipto(0) != '<') && (skipc != '>')); /* scan for next < or > */ if (skipc == '<') ++icnt; /* and keep track of macro level */ else --icnt; } } /* find end of conditional */ static void find_endcond(unsigned char arg) { register short icnt; for (icnt = 1; icnt > 0;) { while ((skipto(0) != '"') && (skipc != '\'') && (skipc != '|')); if (skipc == '"') ++icnt; else if (skipc == '\'') -- icnt; else if ((icnt == 1) && (arg == '|')) break; } }