// // Open stream <f> for fast character input. // int fcfopen(Sfio_t *f) { int n; char *buff; Fcin_t save; errno = 0; _Fcin.fcbuff = _Fcin.fcptr; _Fcin._fcfile = f; fcsave(&save); if (!(buff = (char *)sfreserve(f, SF_UNBOUND, SF_LOCKR))) { fcrestore(&save); _Fcin.fcchar = 0; _Fcin.fcptr = _Fcin.fcbuff = &_Fcin.fcchar; _Fcin.fclast = NULL; _Fcin._fcfile = NULL; return EOF; } n = sfvalue(f); fcrestore(&save); sfread(f, buff, 0); _Fcin.fcoff = sftell(f); buff = (char *)sfreserve(f, SF_UNBOUND, SF_LOCKR); _Fcin.fclast = (_Fcin.fcptr = _Fcin.fcbuff = (unsigned char *)buff) + n; if (sffileno(f) >= 0) *_Fcin.fclast = 0; return n; }
tmain() { UNUSED(argc); UNUSED(argv); Sfio_t *f1, *f2; char *s; Sfoff_t p; char buf[1024]; int r, w; if (!(f1 = sfopen(NULL, tstfile("sf", 0), "w"))) terror("Can't open f1"); if (!(f1 = sfopen(f1, tstfile("sf", 0), "a+"))) terror("Can't open f1"); if (!(f2 = sfopen(NULL, tstfile("sf", 0), "a+"))) terror("Can't open f2"); if (sfwrite(f1, "012345678\n", 10) != 10 || sfsync(f1) < 0) terror("Writing to f1"); if ((p = sftell(f1)) != 10) terror("Bad sftell1 %ld", p); if (sfwrite(f2, "abcdefghi\n", 10) != 10 || sfsync(f2) < 0) terror("Writing to f2"); if ((p = sftell(f2)) != 20) terror("Bad sftell2"); if ((p = sfseek(f1, (Sfoff_t)0, 0)) != 0) terror("Bad seek"); if (!(s = sfgetr(f1, '\n', 1))) terror("Bad getr1"); if (strcmp(s, "012345678") != 0) terror("Bad input1"); if ((p = sftell(f1)) != 10) terror("Bad sftell3"); if (sfwrite(f1, "012345678\n", 10) != 10 || sfsync(f1) < 0) terror("Writing to f1"); if ((p = sftell(f1)) != 30) terror("Bad sftell4"); if ((p = sfseek(f2, (Sfoff_t)10, 0)) != 10) terror("Bad seek"); if (!(s = sfgetr(f2, '\n', 1))) terror("Bad getr2"); if (strcmp(s, "abcdefghi") != 0) terror("Bad input2"); if (!(s = sfgetr(f2, '\n', 1))) terror("Bad getr3"); if (strcmp(s, "012345678") != 0) terror("Bad input3"); if (!(f1 = sfopen(f1, tstfile("sf", 0), "w"))) terror("Can't open file to write"); for (r = 0; r < 1024; ++r) buf[r] = 'a'; if ((w = sfwrite(f1, buf, 1024)) != 1024) terror("writing w=%d", w); if (!(f1 = sfopen(f1, tstfile("sf", 0), "a"))) terror("Can't open file to append"); sfseek(f1, (Sfoff_t)0, 0); if ((w = sfwrite(f1, buf, 64)) != 64) terror("writing w=%d", w); if ((r = (int)sftell(f1)) != (1024 + 64)) terror("seek position wrong s=%d", r); texit(0); }
Dssrecord_t* dssfread(Dssfile_t* file) { int r; file->count++; file->offset += file->length; if ((r = (*file->readf)(file, &file->record, file->dss->disc)) <= 0) { if (r < 0) file->flags |= DSS_FILE_ERROR; file->count--; return 0; } file->length = sftell(file->io) - file->offset; return &file->record; }
/* is there stuff left? if not, go to next file */ static int advFile(generalStream_s *gs) { if (gs->fpLen == -1) { int c = sfgetc(gs->fp); if (c == EOF) return 0; sfungetc(gs->fp, c); return 1; } else { if (sftell(gs->fp) == gs->fpLen) { while (++gs->count < gs->numFilesinStream) { HRSclosefile(gs->fp); gs->fp = HRSopenfile(gs->fileNames[gs->count], "r"); gs->fpLen = sfsize(gs->fp); if (gs->fpLen != 0) return 1; } return 0; } return 1; } }
// This routine turns options on and off. // The options "PDicr" are illegal from set command. // The -o option is used to set option by name. // This routine returns the number of non-option arguments. int sh_argopts(int argc, char *argv[], void *context) { Shell_t *shp = context; int n, o; Arg_t *ap = shp->arg_context; Lex_t *lp = shp->lex_context; Shopt_t newflags; int setflag = 0, action = 0, trace = (int)sh_isoption(shp, SH_XTRACE); Namval_t *np = NULL; const char *sp; char *keylist = NULL; int verbose, f, unsetnp = 0; Optdisc_t disc; newflags = shp->options; memset(&disc, 0, sizeof(disc)); disc.version = OPT_VERSION; disc.infof = infof; opt_info.disc = &disc; if (argc > 0) { setflag = 4; } else { argc = -argc; } while ((n = optget(argv, setflag ? sh_optset : sh_optksh))) { o = 0; f = *opt_info.option == '-' && (opt_info.num || opt_info.arg); switch (n) { case 'A': { np = nv_open(opt_info.arg, shp->var_tree, NV_ARRAY | NV_VARNAME); if (f) unsetnp = 1; continue; } case 'K': { keylist = opt_info.arg; continue; } #if SHOPT_BASH case 'O': { // shopt options, only in bash mode if (!sh_isoption(shp, SH_BASH)) { errormsg(SH_DICT, ERROR_exit(1), e_option, opt_info.name); __builtin_unreachable(); } } #endif // FALLTHRU case 'o': { // set options byname: if (!opt_info.arg || !*opt_info.arg || *opt_info.arg == '-') { action = PRINT; // print style: -O => shopt options. // bash => print unset options also, no heading. verbose = (f ? PRINT_VERBOSE : PRINT_NO_HEADER) | (n == 'O' ? PRINT_SHOPT : 0) | (sh_isoption(shp, SH_BASH) ? PRINT_ALL | PRINT_NO_HEADER : 0) | ((opt_info.arg && (!*opt_info.arg || *opt_info.arg == '-')) ? (PRINT_TABLE | PRINT_NO_HEADER) : 0); continue; } o = sh_lookopt(opt_info.arg, &f); if (o <= 0 || (!sh_isoption(shp, SH_BASH) && (o & SH_BASHEXTRA)) || ((!sh_isoption(shp, SH_BASH) || n == 'o') && (o & SH_BASHOPT)) || (setflag && (o & SH_COMMANDLINE))) { errormsg(SH_DICT, 2, e_option, opt_info.arg); error_info.errors++; } o &= 0xff; if (sh_isoption(shp, SH_RESTRICTED) && !f && o == SH_RESTRICTED) { errormsg(SH_DICT, ERROR_exit(1), e_restricted, opt_info.arg); __builtin_unreachable(); } break; } #if SHOPT_BASH case -1: { // --rcfile shp->gd->rcfile = opt_info.arg; continue; } case -2: { // --noediting if (!f) { off_option(&newflags, SH_VI); off_option(&newflags, SH_EMACS); off_option(&newflags, SH_GMACS); } continue; } case -3: { // --profile n = 'l'; sp = strchr(optksh, n); if (sp) o = flagval[sp - optksh]; break; } case -4: { // --posix // Mask lower 8 bits to find char in optksh string. n &= 0xff; sp = strchr(optksh, n); if (sp) o = flagval[sp - optksh]; break; } case -5: { // --version sfputr(sfstdout, "ksh bash emulation, version ", -1); np = nv_open("BASH_VERSION", shp->var_tree, 0); sfputr(sfstdout, nv_getval(np), -1); np = nv_open("MACHTYPE", shp->var_tree, 0); sfprintf(sfstdout, " (%s)\n", nv_getval(np)); sh_exit(shp, 0); } #endif case -6: { // --default const Shtable_t *tp; for (tp = shtab_options; (o = tp->sh_number); tp++) { if (!(o & SH_COMMANDLINE) && is_option(&newflags, o & 0xff)) { off_option(&newflags, o & 0xff); } } continue; } case -7: { f = 0; goto byname; } case 'D': { on_option(&newflags, SH_NOEXEC); // Cppcheck doesn't recognize the "goto" in the preceding case and thus thinks we // might fall through and call strchr() with n == -7. Even though this it // technically a bug in cppcheck it is one reason why `goto` shouldn't be used; at // least inside `switch` blocks. // cppcheck-suppress invalidFunctionArg sp = strchr(optksh, n); if (sp) o = flagval[sp - optksh]; break; } case 'T': { if (opt_info.num) { shp->test |= opt_info.num; } else { shp->test = 0; } continue; } case 's': { if (setflag) { action = SORT; continue; } sp = strchr(optksh, n); if (sp) o = flagval[sp - optksh]; break; } case 'R': { if (setflag) { n = ':'; } else { ap->kiafile = opt_info.arg; n = 'n'; } sp = strchr(optksh, n); if (sp) o = flagval[sp - optksh]; break; } case ':': { if (opt_info.name[0] == '-' && opt_info.name[1] == '-') { opt_info.arg = argv[opt_info.index - 1] + 2; f = 1; goto byname; } errormsg(SH_DICT, 2, "%s", opt_info.arg); continue; } case '?': { errormsg(SH_DICT, ERROR_usage(0), "%s", opt_info.arg); return -1; } default: { sp = strchr(optksh, n); if (sp) o = flagval[sp - optksh]; break; } } if (f) { if (o == SH_VI || o == SH_EMACS || o == SH_GMACS) { off_option(&newflags, SH_VI); off_option(&newflags, SH_EMACS); off_option(&newflags, SH_GMACS); } on_option(&newflags, o); off_option(&shp->offoptions, o); } else { if (o == SH_RESTRICTED && sh_isoption(shp, SH_RESTRICTED)) { errormsg(SH_DICT, ERROR_exit(1), e_restricted, "r"); __builtin_unreachable(); } if (o == SH_XTRACE) trace = 0; off_option(&newflags, o); if (setflag == 0) on_option(&shp->offoptions, o); } } if (error_info.errors) { errormsg(SH_DICT, ERROR_usage(2), "%s", optusage(NULL)); __builtin_unreachable(); } // Check for '-' or '+' argument. sp = argv[opt_info.index]; if (sp && sp[1] == 0 && (*sp == '+' || *sp == '-') && strcmp(argv[opt_info.index - 1], "--")) { opt_info.index++; off_option(&newflags, SH_XTRACE); off_option(&newflags, SH_VERBOSE); trace = 0; } if (trace) sh_trace(shp, argv, 1); argc -= opt_info.index; argv += opt_info.index; if (action == PRINT) sh_printopts(shp, newflags, verbose, 0); if (setflag) { if (action == SORT) { int (*sortfn)(const char *, const char *) = strcoll; Namarr_t *arp; struct Value *args; unsigned char *bits; if (argc > 0) { strsort(argv, argc, sortfn); } else if (np && (arp = nv_arrayptr(np)) && (args = nv_aivec(np, &bits))) { char *cp; int i, c, keys = 0; if (keylist) { for (cp = keylist; (c = *cp); cp++) { if (c == ',') keys++; } keys++; } else { keylist = ""; } arp->nelem = nv_aipack(arp); cp = nv_name(np); c = strlen(cp); // This used to multiply by `(keys - 1)` but `keys` can be zero which means the // nodesize can be less than `sizeof(struct Node)` which is obviously wrong. // Whether multiplying by `keys` is correct is unclear. // See issue #824. size_t nodesize = sizeof(struct Node) + keys * sizeof(Namval_t *); struct Sort *sp = malloc(sizeof(struct Sort) + strlen(keylist) + (sizeof(char *) + 1) * keys + (arp->nelem + 1) * (nodesize + sizeof(void *)) + c + 3); sp->shp = shp; sp->np = np; if (!(sp->root = shp->last_root)) sp->root = shp->var_tree; sp->vp = args; sp->cur = 0; sp->nodes = (struct Node *)&sp->keys[keys + 2]; memset(sp->nodes, 0, arp->nelem * nodesize); sp->nptrs = (struct Node **)((char *)sp->nodes + arp->nelem * nodesize); sp->flags = (char *)&sp->nptrs[arp->nelem + 1]; memset(sp->flags, 0, keys + 1); sp->name = sp->flags + keys + 1; memcpy(sp->name, cp, c + 1); sp->keys[0] = sp->name + c + 1; strcpy(sp->keys[0], keylist); cp = (char *)sp->nodes; for (c = 0; c < arp->nelem; c++) { if (*keylist && *keylist != ':') { struct Namval *np = FETCH_VT(args[c], np); ((struct Node *)cp)->index = strtol(np->nvname, NULL, 10); ((struct Node *)cp)->bits = bits[c]; } else { ((struct Node *)cp)->index = c; } ((struct Node *)cp)->vp = args[c]; sp->nptrs[c] = (struct Node *)cp; cp += nodesize; } if (!(cp = sp->keys[0])) cp = keylist; for (keys = 0; (c = *cp); cp++) { if (c == ',') { *cp++ = 0; sp->keys[++keys] = cp; sp->flags[keys] = 0; } else if (c == ':') { again: *cp++ = 0; c = *cp; if (c == 'r') { sp->flags[keys] |= SORT_reverse; c = cp[1]; } else if (c == 'n') { sp->flags[keys] |= SORT_numeric; c = cp[1]; } if (c == 'n' || c == 'r') goto again; } } sp->keys[++keys] = 0; Sp = sp; if (sp->keys[0] && *sp->keys[0]) { sortfn = arraysort; } else if (sp->flags[0] & SORT_numeric) { sortfn = numsort; } else { sortfn = alphasort; } strsort((char **)sp->nptrs, arp->nelem, sortfn); cp = (char *)sp->nodes; for (c = 0; c < arp->nelem; c++) { i = (char *)sp->nptrs[c] - (char *)&sp->nodes[0]; if (i / nodesize != c) { args[c] = ((struct Node *)(cp + i))->vp; bits[c] = ((struct Node *)(cp + i))->bits; } } free(sp); nv_close(np); np = NULL; } else { strsort(shp->st.dolv + 1, shp->st.dolc, sortfn); } } if (np) { if (unsetnp) nv_unset(np); nv_setvec(np, 0, argc, argv); nv_close(np); } else if (argc > 0 || ((sp = argv[-1]) && strcmp(sp, "--") == 0)) { sh_argset(ap, argv - 1); } } else if (is_option(&newflags, SH_CFLAG)) { if (!(shp->comdiv = *argv++)) { errormsg(SH_DICT, 2, e_cneedsarg); errormsg(SH_DICT, ERROR_usage(2), optusage(NULL)); __builtin_unreachable(); } argc--; } // Handling SH_INTERACTIVE and SH_PRIVILEGED has been moved to // sh_applyopts(), so that the code can be reused from b_shopt(), too. sh_applyopts(shp, newflags); if (!ap->kiafile) return argc; if (!argv[0]) { errormsg(SH_DICT, ERROR_usage(2), "-R requires scriptname"); __builtin_unreachable(); } if (!(lp->kiafile = sfopen(NULL, ap->kiafile, "w+"))) { errormsg(SH_DICT, ERROR_system(3), e_create, ap->kiafile); __builtin_unreachable(); } if (!(lp->kiatmp = sftmp(2 * SF_BUFSIZE))) { errormsg(SH_DICT, ERROR_system(3), e_tmpcreate); __builtin_unreachable(); } sfputr(lp->kiafile, ";vdb;CIAO/ksh", '\n'); lp->kiabegin = sftell(lp->kiafile); lp->entity_tree = dtopen(&_Nvdisc, Dtbag); lp->scriptname = strdup(sh_fmtq(argv[0])); lp->script = kiaentity(lp, lp->scriptname, -1, 'p', -1, 0, 0, 's', 0, ""); lp->fscript = kiaentity(lp, lp->scriptname, -1, 'f', -1, 0, 0, 's', 0, ""); lp->unknown = kiaentity(lp, "<unknown>", -1, 'p', -1, 0, 0, '0', 0, ""); kiaentity(lp, "<unknown>", -1, 'p', 0, 0, lp->unknown, '0', 0, ""); lp->current = lp->script; ap->kiafile = NULL; return argc; }
int sfdcpzip(Sfio_t* sp, const char* path, unsigned long flags, Pzdisc_t* disc) { Sfio_t* io; Sfpzip_t* pz; Pz_t* oz; if (flags & PZ_HANDLE) { oz = (Pz_t*)sp; sp = oz->io; } else oz = 0; if (sfset(sp, 0, 0) & SF_WRITE) { if (flags & PZ_STAT) return -1; } else if (!(flags & PZ_FORCE)) { unsigned char* s; int r; int m1; int m2; if (!(r = sfset(sp, 0, 0) & SF_SHARE)) sfset(sp, SF_SHARE, 1); s = (unsigned char*)sfreserve(sp, PZ_GZ_MAGOFF + 2, 1); if (!r) sfset(sp, SF_SHARE, 0); if (!s) return -1; m1 = s[0]; m2 = s[1]; r = m1 == PZ_MAGIC_1 && m2 == PZ_MAGIC_2 && s[2] > 0 && s[3] < 10 || m1 == GZ_MAGIC_1 && m2 == GZ_MAGIC_2 && s[PZ_GZ_MAGOFF] == PZ_GZ_MAGIC_1 && s[PZ_GZ_MAGOFF+1] == PZ_GZ_MAGIC_2; sfread(sp, s, 0); if (flags & PZ_STAT) return r; if (!r) { if (!(flags & PZ_NOGZIP)) { if (m1 == GZ_MAGIC_1) { if (m2 == GZ_MAGIC_2) r = sfdcgzip(sp, (flags & PZ_CRC) ? 0 : SFGZ_NOCRC); else if (m2 == LZ_MAGIC_2) r = sfdclzw(sp, 0); } else if (m1 == 'B' && m2 == 'Z' && s[2] == 'h' && s[3] >= '1' && s[3] <= '9') r = sfdcbzip(sp, 0); } return r; } sfsync(sp); } if (!(io = sfnew(NiL, NiL, SF_UNBOUND, sffileno(sp), (sfset(sp, 0, 0) & (SF_READ|SF_WRITE))))) return -1; if (!(pz = newof(0, Sfpzip_t, 1, 0))) { io->_file = -1; sfclose(io); return -1; } pz->disc.version = PZ_VERSION; flags &= ~(PZ_READ|PZ_WRITE|PZ_STAT|PZ_STREAM|PZ_INTERNAL); flags |= PZ_PUSHED|PZ_STREAM|((sfset(sp, 0, 0) & SF_READ) ? PZ_READ : PZ_WRITE); if (oz && (oz->flags & PZ_WRITE)) flags |= PZ_DELAY; if (disc) { pz->disc.errorf = disc->errorf; pz->disc.window = disc->window; pz->disc.options = disc->options; pz->disc.partition = disc->partition; if (disc->splitf) flags |= PZ_ACCEPT; } if (!(pz->pz = pzopen(&pz->disc, (char*)io, flags)) || (sp->_file = open("/dev/null", 0)) < 0) { io->_file = -1; sfclose(io); free(pz); return -1; } if (path) pz->pz->path = path; pz->sfdisc.exceptf = sfpzexcept; if (flags & PZ_WRITE) { pz->sfdisc.writef = sfpzwrite; pz->io = io; } else pz->sfdisc.readf = sfpzread; sfset(sp, SF_SHARE|SF_PUBLIC, 0); if (sfdisc(sp, &pz->sfdisc) != &pz->sfdisc) { close(sp->_file); sp->_file = io->_file; sfseek(sp, sftell(io), SEEK_SET); io->_file = -1; pzclose(pz->pz); free(pz); return -1; } if (oz) oz->flags |= pz->pz->flags & PZ_INTERNAL; return 1; }
/* * This routine will turn the sftmp() file into a real /tmp file or pipe */ void sh_subtmpfile(int pflag) { Shell_t *shp = &sh; int fds[2]; Sfoff_t off; register struct checkpt *pp = (struct checkpt*)shp->jmplist; register struct subshell *sp = subshell_data->pipe; if(sfset(sfstdout,0,0)&SF_STRING) { register int fd; /* save file descriptor 1 if open */ if((sp->tmpfd = fd = fcntl(1,F_DUPFD,10)) >= 0) { fcntl(fd,F_SETFD,FD_CLOEXEC); shp->fdstatus[fd] = shp->fdstatus[1]|IOCLEX; close(1); shp->fdstatus[1] = IOCLOSE; } else if(errno!=EBADF) { ((struct checkpt*)shp->jmplist)->mode = SH_JMPERREXIT; shp->toomany = 1; errormsg(SH_DICT,ERROR_system(1),e_toomany); } if(shp->subshare || !pflag) { sfdisc(sfstdout,SF_POPDISC); if((fd=sffileno(sfstdout))>=0) { shp->fdstatus[fd] = IOREAD|IOWRITE; sfsync(sfstdout); if(fd==1) fcntl(1,F_SETFD,0); else { sfsetfd(sfstdout,1); shp->fdstatus[1] = shp->fdstatus[fd]; shp->fdstatus[fd] = IOCLOSE; } goto skip; } } } if(sp && (shp->fdstatus[1]==IOCLOSE || (!shp->subshare && !(shp->fdstatus[1]&IONOSEEK)))) { struct stat statb,statx; int fd; sh_pipe(fds); sp->pipefd = fds[0]; sh_fcntl(sp->pipefd,F_SETFD,FD_CLOEXEC); /* write the data to the pipe */ if(off = sftell(sfstdout)) { write(fds[1],sfsetbuf(sfstdout,(Void_t*)sfstdout,0),(size_t)off); sfpurge(sfstdout); } if((sfset(sfstdout,0,0)&SF_STRING) || fstat(1,&statb)<0) statb.st_ino = 0; sfclose(sfstdout); if((sh_fcntl(fds[1],F_DUPFD, 1)) != 1) errormsg(SH_DICT,ERROR_system(1),e_redirect); sh_close(fds[1]); if(statb.st_ino) for(fd=0; fd < 10; fd++) { if(fd==1 || ((shp->fdstatus[fd]&(IONOSEEK|IOSEEK|IOWRITE))!=(IOSEEK|IOWRITE)) || fstat(fd,&statx)<0) continue; if(statb.st_ino==statx.st_ino && statb.st_dev==statx.st_dev) { sh_close(fd); fcntl(1,F_DUPFD, fd); } } skip: sh_iostream(shp,1); sfset(sfstdout,SF_SHARE|SF_PUBLIC,1); sfpool(sfstdout,shp->outpool,SF_WRITE); if(pp && pp->olist && pp->olist->strm == sfstdout) pp->olist->strm = 0; } }
main(int argc, char *argv[]) { register int n; register char *cp; Sfio_t *in, *out; error_info.id = argv[0]; NoP(id[0]); #if _AST_VERSION >= 20060701L cmdinit(argc, argv,0, NULL, 0); #else cmdinit(argv,0, NULL, 0); #endif while (n = optget(argv, " [file ...]")) switch (n) { case ':': error(2, opt_info.arg); break; case '?': error(ERROR_usage(2), opt_info.arg); break; } argv += opt_info.index; if(error_info.errors) error(ERROR_usage(2),optusage((char*)0)); if(cp = *argv) argv++; do { if(!cp || strcmp(cp,"-")==0) { in = sfstdin; out = sfstdout; } else { if(!(in = sfopen((Sfio_t*)0,cp,"rb"))) { error(ERROR_system(0),"%s: cannot open for input",cp); error_info.errors = 1; continue; } if(!(out = sfopen((Sfio_t*)0,cp,"r+b"))) { error(ERROR_system(0),"%s: cannot open for writing",cp); error_info.errors = 1; continue; } } nocrnl(in,out); if(in!=sfstdin) sfclose(in); if(out!=sfstdout) { sfsync(out); ftruncate(sffileno(out),sftell(out)); sfclose(out); } } while(cp= *argv++); return(error_info.errors); }
int hist_expand(const char *ln, char **xp) { int off, /* stack offset */ q, /* quotation flags */ p, /* flag */ c, /* current char */ flag=0; /* HIST_* flags */ Sfoff_t n, /* history line number, counter, etc. */ i, /* counter */ w[2]; /* word range */ char *sp, /* stack pointer */ *cp, /* current char in ln */ *str, /* search string */ *evp, /* event/word designator string, for error msgs */ *cc=0, /* copy of current line up to cp; temp ptr */ hc[3], /* default histchars */ *qc="\'\"`"; /* quote characters */ Sfio_t *ref=0, /* line referenced by event designator */ *tmp=0, /* temporary line buffer */ *tmp2=0;/* temporary line buffer */ Histloc_t hl; /* history location */ static Namval_t *np = 0; /* histchars variable */ static struct subst sb = {0,0}; /* substition strings */ static Sfio_t *wm=0; /* word match from !?string? event designator */ if(!wm) wm = sfopen(NULL, NULL, "swr"); hc[0] = '!'; hc[1] = '^'; hc[2] = 0; if((np = nv_open("histchars",sh.var_tree,0)) && (cp = nv_getval(np))) { if(cp[0]) { hc[0] = cp[0]; if(cp[1]) { hc[1] = cp[1]; if(cp[2]) hc[2] = cp[2]; } } } /* save shell stack */ if(off = staktell()) sp = stakfreeze(0); cp = (char*)ln; while(cp && *cp) { /* read until event/quick substitution/comment designator */ if((*cp != hc[0] && *cp != hc[1] && *cp != hc[2]) || (*cp == hc[1] && cp != ln)) { if(*cp == '\\') /* skip escaped designators */ stakputc(*cp++); else if(*cp == '\'') /* skip quoted designators */ { do stakputc(*cp); while(*++cp && *cp != '\''); } stakputc(*cp++); continue; } if(hc[2] && *cp == hc[2]) /* history comment designator, skip rest of line */ { stakputc(*cp++); stakputs(cp); DONE(); } n = -1; str = 0; flag &= HIST_EVENT; /* save event flag for returning later */ evp = cp; ref = 0; if(*cp == hc[1]) /* shortcut substitution */ { flag |= HIST_QUICKSUBST; goto getline; } if(*cp == hc[0] && *(cp+1) == hc[0]) /* refer to line -1 */ { cp += 2; goto getline; } switch(c = *++cp) { case ' ': case '\t': case '\n': case '\0': case '=': case '(': stakputc(hc[0]); continue; case '#': /* the line up to current position */ flag |= HIST_HASH; cp++; n = staktell(); /* terminate string and dup */ stakputc('\0'); cc = strdup(stakptr(0)); stakseek(n); /* remove null byte again */ ref = sfopen(ref, cc, "s"); /* open as file */ n = 0; /* skip history file referencing */ break; case '-': /* back reference by number */ if(!isdigit(*(cp+1))) goto string_event; cp++; case '0': /* reference by number */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = 0; while(isdigit(*cp)) n = n * 10 + (*cp++) - '0'; if(c == '-') n = -n; break; case '$': n = -1; case ':': break; case '?': cp++; flag |= HIST_QUESTION; string_event: default: /* read until end of string or word designator/modifier */ str = cp; while(*cp) { cp++; if((!(flag&HIST_QUESTION) && (*cp == ':' || isspace(*cp) || *cp == '^' || *cp == '$' || *cp == '*' || *cp == '-' || *cp == '%') ) || ((flag&HIST_QUESTION) && (*cp == '?' || *cp == '\n'))) { c = *cp; *cp = '\0'; } } break; } getline: flag |= HIST_EVENT; if(str) /* !string or !?string? event designator */ { /* search history for string */ hl = hist_find(shgd->hist_ptr, str, shgd->hist_ptr->histind, flag&HIST_QUESTION, -1); if((n = hl.hist_command) == -1) n = 0; /* not found */ } if(n) { if(n < 0) /* determine index for backref */ n = shgd->hist_ptr->histind + n; /* search and use history file if found */ if(n > 0 && hist_seek(shgd->hist_ptr, n) != -1) ref = shgd->hist_ptr->histfp; } if(!ref) { /* string not found or command # out of range */ c = *cp; *cp = '\0'; errormsg(SH_DICT, ERROR_ERROR, "%s: event not found", evp); *cp = c; DONE(); } if(str) /* string search: restore orig. line */ { if(flag&HIST_QUESTION) *cp++ = c; /* skip second question mark */ else *cp = c; } /* colon introduces either word designators or modifiers */ if(*(evp = cp) == ':') cp++; w[0] = 0; /* -1 means last word, -2 means match from !?string? */ w[1] = -1; /* -1 means last word, -2 means suppress last word */ if(flag & HIST_QUICKSUBST) /* shortcut substitution */ goto getsel; n = 0; while(n < 2) { switch(c = *cp++) { case '^': /* first word */ if(n == 0) { w[0] = w[1] = 1; goto skip; } else goto skip2; case '$': /* last word */ w[n] = -1; goto skip; case '%': /* match from !?string? event designator */ if(n == 0) { if(!str) { w[0] = 0; w[1] = -1; ref = wm; } else { w[0] = -2; w[1] = sftell(ref) + hl.hist_char; } sfseek(wm, 0, SEEK_SET); goto skip; } default: skip2: cp--; n = 2; break; case '*': /* until last word */ if(n == 0) w[0] = 1; w[1] = -1; skip: flag |= HIST_WORDDSGN; n = 2; break; case '-': /* until last word or specified index */ w[1] = -2; flag |= HIST_WORDDSGN; n = 1; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': /* specify index */ if((*evp == ':') || w[1] == -2) { w[n] = c - '0'; while(isdigit(c=*cp++)) w[n] = w[n] * 10 + c - '0'; flag |= HIST_WORDDSGN; if(n == 0) w[1] = w[0]; n++; } else n = 2; cp--; break; } } if(w[0] != -2 && w[1] > 0 && w[0] > w[1]) { c = *cp; *cp = '\0'; errormsg(SH_DICT, ERROR_ERROR, "%s: bad word specifier", evp); *cp = c; DONE(); } /* no valid word designator after colon, rewind */ if(!(flag & HIST_WORDDSGN) && (*evp == ':')) cp = evp; getsel: /* open temp buffer, let sfio do the (re)allocation */ tmp = sfopen(NULL, NULL, "swr"); /* push selected words into buffer, squash whitespace into single blank or a newline */ n = i = q = 0; while((c = sfgetc(ref)) > 0) { if(isspace(c)) { flag |= (c == '\n' ? HIST_NEWLINE : 0); continue; } if(n >= w[0] && ((w[0] != -2) ? (w[1] < 0 || n <= w[1]) : 1)) { if(w[0] < 0) sfseek(tmp, 0, SEEK_SET); else i = sftell(tmp); if(i > 0) sfputc(tmp, flag & HIST_NEWLINE ? '\n' : ' '); flag &= ~HIST_NEWLINE; p = 1; } else p = 0; do { cc = strchr(qc, c); q ^= cc ? 1<<(int)(cc - qc) : 0; if(p) sfputc(tmp, c); } while((c = sfgetc(ref)) > 0 && (!isspace(c) || q)); if(w[0] == -2 && sftell(ref) > w[1]) break; flag |= (c == '\n' ? HIST_NEWLINE : 0); n++; } if(w[0] != -2 && w[1] >= 0 && w[1] >= n) { c = *cp; *cp = '\0'; errormsg(SH_DICT, ERROR_ERROR, "%s: bad word specifier", evp); *cp = c; DONE(); } else if(w[1] == -2) /* skip last word */ sfseek(tmp, i, SEEK_SET); /* remove trailing newline */ if(sftell(tmp)) { sfseek(tmp, -1, SEEK_CUR); if(sfgetc(tmp) == '\n') sfungetc(tmp, '\n'); } sfputc(tmp, '\0'); if(str) { if(wm) sfclose(wm); wm = tmp; } if(cc && (flag&HIST_HASH)) { /* close !# temp file */ sfclose(ref); flag &= ~HIST_HASH; free(cc); cc = 0; } evp = cp; /* selected line/words are now in buffer, now go for the modifiers */ while(*cp == ':' || (flag & HIST_QUICKSUBST)) { if(flag & HIST_QUICKSUBST) { flag &= ~HIST_QUICKSUBST; c = 's'; cp--; } else c = *++cp; sfseek(tmp, 0, SEEK_SET); tmp2 = sfopen(tmp2, NULL, "swr"); if(c == 'g') /* global substitution */ { flag |= HIST_GLOBALSUBST; c = *++cp; } if(cc = strchr(modifiers, c)) flag |= mod_flags[cc - modifiers]; else { errormsg(SH_DICT, ERROR_ERROR, "%c: unrecognized history modifier", c); DONE(); } if(c == 'h' || c == 'r') /* head or base */ { n = -1; while((c = sfgetc(tmp)) > 0) { /* remember position of / or . */ if((c == '/' && *cp == 'h') || (c == '.' && *cp == 'r')) n = sftell(tmp2); sfputc(tmp2, c); } if(n > 0) { /* rewind to last / or . */ sfseek(tmp2, n, SEEK_SET); /* end string there */ sfputc(tmp2, '\0'); } } else if(c == 't' || c == 'e') /* tail or suffix */ { n = 0; while((c = sfgetc(tmp)) > 0) { /* remember position of / or . */ if((c == '/' && *cp == 't') || (c == '.' && *cp == 'e')) n = sftell(tmp); } /* rewind to last / or . */ sfseek(tmp, n, SEEK_SET); /* copy from there on */ while((c = sfgetc(tmp)) > 0) sfputc(tmp2, c); } else if(c == 's' || c == '&') { cp++; if(c == 's') { /* preset old with match from !?string? */ if(!sb.str[0] && wm) sb.str[0] = strdup(sfsetbuf(wm, (Void_t*)1, 0)); cp = parse_subst(cp, &sb); } if(!sb.str[0] || !sb.str[1]) { c = *cp; *cp = '\0'; errormsg(SH_DICT, ERROR_ERROR, "%s%s: no previous substitution", (flag & HIST_QUICKSUBST) ? ":s" : "", evp); *cp = c; DONE(); } /* need pointer for strstr() */ str = sfsetbuf(tmp, (Void_t*)1, 0); flag |= HIST_SUBSTITUTE; while(flag & HIST_SUBSTITUTE) { /* find string */ if(cc = strstr(str, sb.str[0])) { /* replace it */ c = *cc; *cc = '\0'; sfputr(tmp2, str, -1); sfputr(tmp2, sb.str[1], -1); *cc = c; str = cc + strlen(sb.str[0]); } else if(!sftell(tmp2)) { /* not successfull */ c = *cp; *cp = '\0'; errormsg(SH_DICT, ERROR_ERROR, "%s%s: substitution failed", (flag & HIST_QUICKSUBST) ? ":s" : "", evp); *cp = c; DONE(); } /* loop if g modifier specified */ if(!cc || !(flag & HIST_GLOBALSUBST)) flag &= ~HIST_SUBSTITUTE; } /* output rest of line */ sfputr(tmp2, str, -1); if(*cp) cp--; } if(sftell(tmp2)) { /* if any substitions done, swap buffers */ if(wm != tmp) sfclose(tmp); tmp = tmp2; tmp2 = 0; } cc = 0; if(*cp) cp++; } /* flush temporary buffer to stack */ if(tmp) { sfseek(tmp, 0, SEEK_SET); if(flag & HIST_QUOTE) stakputc('\''); while((c = sfgetc(tmp)) > 0) { if(isspace(c)) { flag = flag & ~HIST_NEWLINE; /* squash white space to either a blank or a newline */ do flag |= (c == '\n' ? HIST_NEWLINE : 0); while((c = sfgetc(tmp)) > 0 && isspace(c)); sfungetc(tmp, c); c = (flag & HIST_NEWLINE) ? '\n' : ' '; if(flag & HIST_QUOTE_BR) { stakputc('\''); stakputc(c); stakputc('\''); } else stakputc(c); } else if((c == '\'') && (flag & HIST_QUOTE)) { stakputc('\''); stakputc('\\'); stakputc(c); stakputc('\''); } else stakputc(c); } if(flag & HIST_QUOTE) stakputc('\''); } } stakputc('\0'); done: if(cc && (flag&HIST_HASH)) { /* close !# temp file */ sfclose(ref); free(cc); cc = 0; } /* error? */ if(staktell() && !(flag & HIST_ERROR)) *xp = strdup(stakfreeze(1)); /* restore shell stack */ if(off) stakset(sp,off); else stakseek(0); /* drop temporary files */ if(tmp && tmp != wm) sfclose(tmp); if(tmp2) sfclose(tmp2); return (flag & HIST_ERROR ? HIST_ERROR : flag & HIST_FLAG_RETURN_MASK); }