Wc_t* wc_init(int mode) { register int n; register int w; Wc_t* wp; if (!(wp = (Wc_t*)stakalloc(sizeof(Wc_t)))) return 0; if (!mbwide()) wp->mb = 0; #if _hdr_wchar && _hdr_wctype && _lib_iswctype else if (!(mode & WC_NOUTF8) && (lcinfo(LC_CTYPE)->lc->flags & LC_utf8)) wp->mb = 1; #endif else wp->mb = -1; w = mode & WC_WORDS; for (n = (1<<CHAR_BIT); --n >= 0;) wp->type[n] = (w && isspace(n)) ? WC_SP : 0; wp->type['\n'] = WC_SP|WC_NL; if ((mode & (WC_MBYTE|WC_WORDS)) && wp->mb > 0) { for (n = 0; n < 64; n++) { wp->type[0x80+n] |= WC_MB; if (n<32) wp->type[0xc0+n] |= WC_MB+1; else if (n<48) wp->type[0xc0+n] |= WC_MB+2; else if (n<56) wp->type[0xc0+n] |= WC_MB+3; else if (n<60) wp->type[0xc0+n] |= WC_MB+4; else if (n<62) wp->type[0xc0+n] |= WC_MB+5; } wp->type[0xc0] = WC_MB|WC_ERR; wp->type[0xc1] = WC_MB|WC_ERR; wp->type[0xfe] = WC_MB|WC_ERR; wp->type[0xff] = WC_MB|WC_ERR; } wp->mode = mode; return wp; }
static Cut_t* cutinit(int mode, char* str, Delim_t* wdelim, Delim_t* ldelim, size_t reclen) { register int* lp; register int c; register int n = 0; register int range = 0; register char* cp = str; Cut_t* cut; if (!(cut = (Cut_t*)stakalloc(sizeof(Cut_t) + strlen(cp) * sizeof(int)))) error(ERROR_exit(1), "out of space"); if (cut->mb = mbwide()) { memset(cut->space, 0, sizeof(cut->space) / 2); memset(cut->space + sizeof(cut->space) / 2, SP_WIDE, sizeof(cut->space) / 2); } else memset(cut->space, 0, sizeof(cut->space)); cut->wdelim = *wdelim; if (wdelim->len == 1) cut->space[wdelim->chr] = SP_WORD; cut->ldelim = *ldelim; cut->eob = (ldelim->len == 1) ? ldelim->chr : 0; cut->space[cut->eob] = SP_LINE; cut->cflag = (mode&C_CHARS) && cut->mb; cut->nosplit = (mode&(C_BYTES|C_NOSPLIT)) == (C_BYTES|C_NOSPLIT) && cut->mb; cut->sflag = (mode&C_SUPRESS) != 0; cut->nlflag = (mode&C_NONEWLINE) != 0; cut->reclen = reclen; lp = cut->list; for (;;) switch(c = *cp++) { case ' ': case '\t': while(*cp==' ' || *cp=='\t') cp++; /*FALLTHROUGH*/ case 0: case ',': if(range) { --range; if((n = (n ? (n-range) : (HUGE-1))) < 0) error(ERROR_exit(1),"invalid range for c/f option"); *lp++ = range; *lp++ = n; } else { *lp++ = --n; *lp++ = 1; } if(c==0) { register int *dp; *lp = HUGE; n = 1 + (lp-cut->list)/2; qsort(lp=cut->list,n,2*sizeof(*lp),mycomp); /* eliminate overlapping regions */ for(n=0,range= -2,dp=lp; *lp!=HUGE; lp+=2) { if(lp[0] <= range) { if(lp[1]==HUGE) { dp[-1] = HUGE; break; } if((c = lp[0]+lp[1]-range)>0) { range += c; dp[-1] += c; } } else { range = *dp++ = lp[0]; if(lp[1]==HUGE) { *dp++ = HUGE; break; } range += (*dp++ = lp[1]); } } *dp = HUGE; lp = cut->list; /* convert ranges into gaps */ for(n=0; *lp!=HUGE; lp+=2) { c = *lp; *lp -= n; n = c+lp[1]; } return cut; } n = range = 0; break; case '-': if(range) error(ERROR_exit(1),"bad list for c/f option"); range = n?n:1; n = 0; break; default: if(!isdigit(c)) error(ERROR_exit(1),"bad list for c/f option"); n = 10*n + (c-'0'); break; } /* NOTREACHED */ }
int b_paste(int argc, char** argv, Shbltin_t* context) { register int n, sflag=0; register Sfio_t *fp, **streams; register char *cp, *delim; char *ep; Delim_t *mp; int dlen, dsiz; char defdelim[2]; cmdinit(argc, argv, context, ERROR_CATALOG, 0); delim = 0; for (;;) { switch (optget(argv, usage)) { case 'd': delim = opt_info.arg; continue; case 's': sflag++; continue; case ':': error(2, "%s", opt_info.arg); break; case '?': error(ERROR_usage(2), "%s", opt_info.arg); break; } break; } argv += opt_info.index; if(error_info.errors) error(ERROR_usage(2),"%s", optusage(NiL)); if(!delim || !*delim) { delim = defdelim; delim[0] = '\t'; delim[1] = 0; } if (!(delim = strdup(delim))) error(ERROR_system(1), "out of space"); dlen = dsiz = stresc(delim); mp = 0; if (mbwide()) { cp = delim; ep = delim + dlen; dlen = 0; while (cp < ep) { mbchar(cp); dlen++; } if(dlen < dsiz) { if (!(mp = newof(0, Delim_t, dlen, 0))) { free(delim); error(ERROR_system(1), "out of space"); } cp = delim; dlen = 0; while (cp < ep) { mp[dlen].chr = cp; mbchar(cp); mp[dlen].len = cp - mp[dlen].chr; dlen++; } } } if(cp = *argv) { n = argc - opt_info.index; argv++; } else n = 1; if(!sflag) { if (!(streams = (Sfio_t**)stakalloc(n*sizeof(Sfio_t*)))) error(ERROR_exit(1), "out of space"); n = 0; } do { if(!cp || streq(cp,"-")) fp = sfstdin; else if(!(fp = sfopen(NiL,cp,"r"))) error(ERROR_system(0),"%s: cannot open",cp); if(fp && sflag) { if(spaste(fp,sfstdout,delim,dsiz,dlen,mp) < 0) error(ERROR_system(0),"write failed"); if(fp!=sfstdin) sfclose(fp); } else if(!sflag) streams[n++] = fp; } while(cp= *argv++); if(!sflag) { if(error_info.errors==0 && paste(n,streams,sfstdout,delim,dsiz,dlen,mp) < 0) error(ERROR_system(0),"write failed"); while(--n>=0) if((fp=streams[n]) && fp!=sfstdin) sfclose(fp); } if (mp) free(mp); free(delim); return(error_info.errors); }
int ed_expand(Edit_t *ep, char outbuff[],int *cur,int *eol,int mode, int count) { struct comnod *comptr; struct argnod *ap; register char *out; char *av[2], *begin , *dir=0; int addstar=0, rval=0, var=0, strip=1; int nomarkdirs = !sh_isoption(SH_MARKDIRS); sh_onstate(SH_FCOMPLETE); if(ep->e_nlist) { if(mode=='=' && count>0) { if(count> ep->e_nlist) return(-1); mode = '?'; av[0] = ep->e_clist[count-1]; av[1] = 0; } else { stakset(ep->e_stkptr,ep->e_stkoff); ep->e_nlist = 0; } } comptr = (struct comnod*)stakalloc(sizeof(struct comnod)); ap = (struct argnod*)stakseek(ARGVAL); #if SHOPT_MULTIBYTE { register int c = *cur; register genchar *cp; /* adjust cur */ cp = (genchar *)outbuff + *cur; c = *cp; *cp = 0; *cur = ed_external((genchar*)outbuff,(char*)stakptr(0)); *cp = c; *eol = ed_external((genchar*)outbuff,outbuff); } #endif /* SHOPT_MULTIBYTE */ out = outbuff + *cur + (sh_isoption(SH_VI)!=0); if(out[-1]=='"' || out[-1]=='\'') { rval = -(sh_isoption(SH_VI)!=0); goto done; } comptr->comtyp = COMSCAN; comptr->comarg = ap; ap->argflag = (ARG_MAC|ARG_EXP); ap->argnxt.ap = 0; ap->argchn.cp = 0; { register int c; char *last = out; c = *(unsigned char*)out; var = mode; begin = out = find_begin(outbuff,last,0,&var); /* addstar set to zero if * should not be added */ if(var=='$') { stakputs("${!"); stakwrite(out,last-out); stakputs("@}"); out = last; } else { addstar = '*'; while(out < last) { c = *(unsigned char*)out; if(isexp(c)) addstar = 0; if (c == '/') { if(addstar == 0) strip = 0; dir = out+1; } stakputc(c); out++; } } if(mode=='?') mode = '*'; if(var!='$' && mode=='\\' && out[-1]!='*') addstar = '*'; if(*begin=='~' && !strchr(begin,'/')) addstar = 0; stakputc(addstar); ap = (struct argnod*)stakfreeze(1); } if(mode!='*') sh_onoption(SH_MARKDIRS); { register char **com; char *cp=begin, *left=0, *saveout="."; int nocase=0,narg,cmd_completion=0; register int size='x'; while(cp>outbuff && ((size=cp[-1])==' ' || size=='\t')) cp--; if(!var && !strchr(ap->argval,'/') && (((cp==outbuff&&ep->sh->nextprompt==1) || (strchr(";&|(",size)) && (cp==outbuff+1||size=='('||cp[-2]!='>') && *begin!='~' ))) { cmd_completion=1; sh_onstate(SH_COMPLETE); } if(ep->e_nlist) { narg = 1; com = av; if(dir) begin += (dir-begin); } else { com = sh_argbuild(ep->sh,&narg,comptr,0); /* special handling for leading quotes */ if(begin>outbuff && (begin[-1]=='"' || begin[-1]=='\'')) begin--; } sh_offstate(SH_COMPLETE); /* allow a search to be aborted */ if(ep->sh->trapnote&SH_SIGSET) { rval = -1; goto done; } /* match? */ if (*com==0 || (narg <= 1 && (strcmp(ap->argval,*com)==0) || (addstar && com[0][strlen(*com)-1]=='*'))) { rval = -1; goto done; } if(mode=='\\' && out[-1]=='/' && narg>1) mode = '='; if(mode=='=') { if (strip && !cmd_completion) { register char **ptrcom; for(ptrcom=com;*ptrcom;ptrcom++) /* trim directory prefix */ *ptrcom = path_basename(*ptrcom); } sfputc(sfstderr,'\n'); sh_menu(sfstderr,narg,com); sfsync(sfstderr); ep->e_nlist = narg; ep->e_clist = com; goto done; } /* see if there is enough room */ size = *eol - (out-begin); if(mode=='\\') { int c; if(dir) { c = *dir; *dir = 0; saveout = begin; } if(saveout=astconf("PATH_ATTRIBUTES",saveout,(char*)0)) nocase = (strchr(saveout,'c')!=0); if(dir) *dir = c; /* just expand until name is unique */ size += strlen(*com); } else { size += narg; { char **savcom = com; while (*com) size += strlen(cp=fmtx(*com++)); com = savcom; } } /* see if room for expansion */ if(outbuff+size >= &outbuff[MAXLINE]) { com[0] = ap->argval; com[1] = 0; } /* save remainder of the buffer */ if(*out) left=stakcopy(out); if(cmd_completion && mode=='\\') out = strcopy(begin,path_basename(cp= *com++)); else if(mode=='*') { if(ep->e_nlist && dir && var) { if(*cp==var) cp++; else *begin++ = var; out = strcopy(begin,cp); var = 0; } else out = strcopy(begin,fmtx(*com)); com++; } else out = strcopy(begin,*com++); if(mode=='\\') { saveout= ++out; while (*com && *begin) { if(cmd_completion) out = overlaid(begin,path_basename(*com++),nocase); else out = overlaid(begin,*com++,nocase); } mode = (out==saveout); if(out[-1]==0) out--; if(mode && out[-1]!='/') { if(cmd_completion) { Namval_t *np; /* add as tracked alias */ Pathcomp_t *pp; if(*cp=='/' && (pp=path_dirfind(ep->sh->pathlist,cp,'/')) && (np=nv_search(begin,ep->sh->track_tree,NV_ADD))) path_alias(np,pp); out = strcopy(begin,cp); } /* add quotes if necessary */ if((cp=fmtx(begin))!=begin) out = strcopy(begin,cp); if(var=='$' && begin[-1]=='{') *out = '}'; else *out = ' '; *++out = 0; } else if((cp=fmtx(begin))!=begin) { out = strcopy(begin,cp); if(out[-1] =='"' || out[-1]=='\'') *--out = 0; } if(*begin==0) ed_ringbell(); } else { while (*com) { *out++ = ' '; out = strcopy(out,fmtx(*com++)); } } if(ep->e_nlist) { cp = com[-1]; if(cp[strlen(cp)-1]!='/') { if(var=='$' && begin[-1]=='{') *out = '}'; else *out = ' '; out++; } else if(out[-1] =='"' || out[-1]=='\'') out--; *out = 0; } *cur = (out-outbuff); /* restore rest of buffer */ if(left) out = strcopy(out,left); *eol = (out-outbuff); } done: sh_offstate(SH_FCOMPLETE); if(!ep->e_nlist) stakset(ep->e_stkptr,ep->e_stkoff); if(nomarkdirs) sh_offoption(SH_MARKDIRS); #if SHOPT_MULTIBYTE { register int c,n=0; /* first re-adjust cur */ c = outbuff[*cur]; outbuff[*cur] = 0; for(out=outbuff; *out;n++) mbchar(out); outbuff[*cur] = c; *cur = n; outbuff[*eol+1] = 0; *eol = ed_internal(outbuff,(genchar*)outbuff); } #endif /* SHOPT_MULTIBYTE */ return(rval); }
int glob(const char* pattern, int flags, int (*errfn)(const char*, int), register glob_t* gp) { register globlist_t* ap; register char* pat; globlist_t* top; Stak_t* oldstak; char** argv; char** av; size_t skip; unsigned long f; int n; int x; const char* nocheck = pattern; int optlen = 0; int suflen = 0; int extra = 1; unsigned char intr = 0; gp->gl_rescan = 0; gp->gl_error = 0; gp->gl_errfn = errfn; if (flags & GLOB_APPEND) { if ((gp->gl_flags |= GLOB_APPEND) ^ (flags|GLOB_MAGIC)) return GLOB_APPERR; if (((gp->gl_flags & GLOB_STACK) == 0) == (gp->gl_stak == 0)) return GLOB_APPERR; if (gp->gl_starstar > 1) gp->gl_flags |= GLOB_STARSTAR; else gp->gl_starstar = 0; } else { gp->gl_flags = (flags&0xffff)|GLOB_MAGIC; gp->re_flags = REG_SHELL|REG_NOSUB|REG_LEFT|REG_RIGHT|((flags&GLOB_AUGMENTED)?REG_AUGMENTED:0); gp->gl_pathc = 0; gp->gl_ignore = 0; gp->gl_ignorei = 0; gp->gl_starstar = 0; if (!(flags & GLOB_DISC)) { gp->gl_fignore = 0; gp->gl_suffix = 0; gp->gl_intr = 0; gp->gl_delim = 0; gp->gl_handle = 0; gp->gl_diropen = 0; gp->gl_dirnext = 0; gp->gl_dirclose = 0; gp->gl_type = 0; gp->gl_attr = 0; gp->gl_nextdir = 0; gp->gl_stat = 0; gp->gl_lstat = 0; gp->gl_extra = 0; } if (!(flags & GLOB_ALTDIRFUNC)) { gp->gl_opendir = (GL_opendir_f)opendir; gp->gl_readdir = (GL_readdir_f)readdir; gp->gl_closedir = (GL_closedir_f)closedir; if (!gp->gl_stat) gp->gl_stat = (GL_stat_f)pathstat; } if (!gp->gl_lstat) gp->gl_lstat = (GL_stat_f)lstat; if (!gp->gl_intr) gp->gl_intr = &intr; if (!gp->gl_delim) gp->gl_delim = '/'; if (!gp->gl_diropen) gp->gl_diropen = gl_diropen; if (!gp->gl_dirnext) gp->gl_dirnext = gl_dirnext; if (!gp->gl_dirclose) gp->gl_dirclose = gl_dirclose; if (!gp->gl_type) gp->gl_type = gl_type; if (!gp->gl_attr) gp->gl_attr = gl_attr; if (flags & GLOB_ICASE) gp->re_flags |= REG_ICASE; if (!gp->gl_fignore) gp->re_flags |= REG_SHELL_DOT; else if (*gp->gl_fignore) { if (regcomp(&gp->re_ignore, gp->gl_fignore, gp->re_flags)) return GLOB_APPERR; gp->gl_ignore = &gp->re_ignore; } if (gp->gl_flags & GLOB_STACK) gp->gl_stak = 0; else if (!(gp->gl_stak = stakcreate(0))) return GLOB_NOSPACE; if ((gp->gl_flags & GLOB_COMPLETE) && !gp->gl_nextdir) gp->gl_nextdir = gl_nextdir; } skip = gp->gl_pathc; if (gp->gl_stak) oldstak = stakinstall(gp->gl_stak, 0); if (flags & GLOB_DOOFFS) extra += gp->gl_offs; if (gp->gl_suffix) suflen = strlen(gp->gl_suffix); if (*(pat = (char*)pattern) == '~' && *(pat + 1) == '(') { f = gp->gl_flags; n = 1; x = 1; pat += 2; for (;;) { switch (*pat++) { case 0: case ':': break; case '-': n = 0; continue; case '+': n = 1; continue; case 'i': if (n) f |= GLOB_ICASE; else f &= ~GLOB_ICASE; continue; case 'M': if (n) f |= GLOB_BRACE; else f &= ~GLOB_BRACE; continue; case 'N': if (n) f &= ~GLOB_NOCHECK; else f |= GLOB_NOCHECK; continue; case 'O': if (n) f |= GLOB_STARSTAR; else f &= ~GLOB_STARSTAR; continue; case ')': flags = (gp->gl_flags = f) & 0xffff; if (f & GLOB_ICASE) gp->re_flags |= REG_ICASE; else gp->re_flags &= ~REG_ICASE; if (x) optlen = pat - (char*)pattern; break; default: x = 0; continue; } break; } } top = ap = (globlist_t*)stakalloc((optlen ? 2 : 1) * strlen(pattern) + sizeof(globlist_t) + suflen + gp->gl_extra); ap->gl_next = 0; ap->gl_flags = 0; ap->gl_begin = ap->gl_path + gp->gl_extra; pat = strcopy(ap->gl_begin, pattern + optlen); if (suflen) pat = strcopy(pat, gp->gl_suffix); gp->gl_pat = optlen ? strncpy(gp->gl_opt = pat + 1, pattern, optlen) : (char*)0; suflen = 0; if (!(flags & GLOB_LIST)) gp->gl_match = 0; do { gp->gl_rescan = ap->gl_next; glob_dir(gp, ap); } while (!gp->gl_error && (ap = gp->gl_rescan)); if (gp->gl_pathc == skip) { if (flags & GLOB_NOCHECK) { gp->gl_pathc++; top->gl_next = gp->gl_match; gp->gl_match = top; strcopy(top->gl_path + gp->gl_extra, nocheck); } else gp->gl_error = GLOB_NOMATCH; } if (flags & GLOB_LIST) gp->gl_list = gp->gl_match; else { argv = (char**)stakalloc((gp->gl_pathc + extra) * sizeof(char*)); if (gp->gl_flags & GLOB_APPEND) { skip += --extra; memcpy(argv, gp->gl_pathv, skip * sizeof(char*)); av = argv + skip; } else { av = argv; while (--extra > 0) *av++ = 0; } gp->gl_pathv = argv; argv = av; ap = gp->gl_match; while (ap) { *argv++ = ap->gl_path + gp->gl_extra; ap = ap->gl_next; } *argv = 0; if (!(flags & GLOB_NOSORT) && (argv - av) > 1) { strsort(av, argv - av, strcoll); if (gp->gl_starstar > 1) av[gp->gl_pathc = struniq(av, argv - av)] = 0; gp->gl_starstar = 0; } } if (gp->gl_starstar > 1) gp->gl_flags &= ~GLOB_STARSTAR; if (gp->gl_stak) stakinstall(oldstak, 0); return gp->gl_error; }
int sh_access(register const char *name, register int mode) { Shell_t *shp = sh_getinterp(); struct stat statb; if(*name==0) return(-1); if(sh_isdevfd(name)) return(sh_ioaccess((int)strtol(name+8, (char**)0, 10),mode)); /* can't use access function for execute permission with root */ if(mode==X_OK && shp->gd->euserid==0) goto skip; if(shp->gd->userid==shp->gd->euserid && shp->gd->groupid==shp->gd->egroupid) return(access(name,mode)); #ifdef _lib_setreuid /* swap the real uid to effective, check access then restore */ /* first swap real and effective gid, if different */ if(shp->gd->groupid==shp->gd->euserid || setregid(shp->gd->egroupid,shp->gd->groupid)==0) { /* next swap real and effective uid, if needed */ if(shp->gd->userid==shp->gd->euserid || setreuid(shp->gd->euserid,shp->gd->userid)==0) { mode = access(name,mode); /* restore ids */ if(shp->gd->userid!=shp->gd->euserid) setreuid(shp->gd->userid,shp->gd->euserid); if(shp->gd->groupid!=shp->gd->egroupid) setregid(shp->gd->groupid,shp->gd->egroupid); return(mode); } else if(shp->gd->groupid!=shp->gd->egroupid) setregid(shp->gd->groupid,shp->gd->egroupid); } #endif /* _lib_setreuid */ skip: if(test_stat(name, &statb) == 0) { if(mode == F_OK) return(mode); else if(shp->gd->euserid == 0) { if(!S_ISREG(statb.st_mode) || mode!=X_OK) return(0); /* root needs execute permission for someone */ mode = (S_IXUSR|S_IXGRP|S_IXOTH); } else if(shp->gd->euserid == statb.st_uid) mode <<= 6; else if(shp->gd->egroupid == statb.st_gid) mode <<= 3; #ifdef _lib_getgroups /* on some systems you can be in several groups */ else { static int maxgroups; gid_t *groups; register int n; if(maxgroups==0) { /* first time */ if((maxgroups=getgroups(0,(gid_t*)0)) <= 0) { /* pre-POSIX system */ maxgroups=NGROUPS_MAX; } } groups = (gid_t*)stakalloc((maxgroups+1)*sizeof(gid_t)); n = getgroups(maxgroups,groups); while(--n >= 0) { if(groups[n] == statb.st_gid) { mode <<= 3; break; } } } # endif /* _lib_getgroups */ if(statb.st_mode & mode) return(0); } return(-1); }
int b_tee(int argc, register char** argv, void* context) { register Tee_t* tp = 0; register int oflag = O_WRONLY|O_TRUNC|O_CREAT|O_BINARY; register int* hp; register char* cp; int line; if (argc <= 0) { if (context && (tp = (Tee_t*)sh_context(context)->data)) { sh_context(context)->data = 0; tee_cleanup(tp); } return 0; } cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_CALLBACK); line = -1; for (;;) { switch (optget(argv, usage)) { case 'a': oflag &= ~O_TRUNC; oflag |= O_APPEND; continue; case 'i': signal(SIGINT, SIG_IGN); continue; case 'l': line = sfset(sfstdout, 0, 0) & SF_LINE; if ((line == 0) == (opt_info.num == 0)) line = -1; else sfset(sfstdout, SF_LINE, !!opt_info.num); continue; case ':': error(2, "%s", opt_info.arg); break; case '?': error(ERROR_usage(2), "%s", opt_info.arg); break; } break; } if (error_info.errors) error(ERROR_usage(2), "%s", optusage(NiL)); argv += opt_info.index; argc -= opt_info.index; #if _ANCIENT_BSD_COMPATIBILITY if (*argv && streq(*argv, "-")) { signal(SIGINT, SIG_IGN); argv++; argc--; } #endif if (argc > 0) { if (tp = (Tee_t*)stakalloc(sizeof(Tee_t) + argc * sizeof(int))) { memset(&tp->disc, 0, sizeof(tp->disc)); tp->disc.writef = tee_write; if (context) sh_context(context)->data = (void*)tp; tp->line = line; hp = tp->fd; while (cp = *argv++) { if ((*hp = open(cp, oflag, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) < 0) error(ERROR_system(0), "%s: cannot create", cp); else hp++; } if (hp == tp->fd) tp = 0; else { *hp = -1; sfdisc(sfstdout, &tp->disc); } } else error(ERROR_exit(0), "out of space"); } if ((sfmove(sfstdin, sfstdout, SF_UNBOUND, -1) < 0 || !sfeof(sfstdin)) && errno != EPIPE) error(ERROR_system(0), "read error"); if (sfsync(sfstdout)) error(ERROR_system(0), "write error"); tee_cleanup(tp); return error_info.errors; }