Fix typo in license headers
[oweals/cde.git] / cde / programs / dtksh / ksh93 / src / cmd / ksh93 / sh / io.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $XConsortium: io.c /main/3 1995/11/01 16:48:07 rswiston $ */
24 /***************************************************************
25 *                                                              *
26 *                      AT&T - PROPRIETARY                      *
27 *                                                              *
28 *        THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF        *
29 *                    AT&T BELL LABORATORIES                    *
30 *         AND IS NOT TO BE DISCLOSED OR USED EXCEPT IN         *
31 *            ACCORDANCE WITH APPLICABLE AGREEMENTS             *
32 *                                                              *
33 *                Copyright (c) 1995 AT&T Corp.                 *
34 *              Unpublished & Not for Publication               *
35 *                     All Rights Reserved                      *
36 *                                                              *
37 *       The copyright notice above does not evidence any       *
38 *      actual or intended publication of such source code      *
39 *                                                              *
40 *               This software was created by the               *
41 *           Advanced Software Technology Department            *
42 *                    AT&T Bell Laboratories                    *
43 *                                                              *
44 *               For further information contact                *
45 *                    {research,attmail}!dgk                    *
46 *                                                              *
47 ***************************************************************/
48
49 /* : : generated by proto : : */
50
51 #if !defined(__PROTO__)
52 #if defined(__STDC__) || defined(__cplusplus) || defined(_proto) || defined(c_plusplus)
53 #if defined(__cplusplus)
54 #define __MANGLE__      "C"
55 #else
56 #define __MANGLE__
57 #endif
58 #define __STDARG__
59 #define __PROTO__(x)    x
60 #define __OTORP__(x)
61 #define __PARAM__(n,o)  n
62 #if !defined(__STDC__) && !defined(__cplusplus)
63 #if !defined(c_plusplus)
64 #define const
65 #endif
66 #define signed
67 #define void            int
68 #define volatile
69 #define __V_            char
70 #else
71 #define __V_            void
72 #endif
73 #else
74 #define __PROTO__(x)    ()
75 #define __OTORP__(x)    x
76 #define __PARAM__(n,o)  o
77 #define __MANGLE__
78 #define __V_            char
79 #define const
80 #define signed
81 #define void            int
82 #define volatile
83 #endif
84 #if defined(__cplusplus) || defined(c_plusplus)
85 #define __VARARG__      ...
86 #else
87 #define __VARARG__
88 #endif
89 #if defined(__STDARG__)
90 #define __VA_START__(p,a)       va_start(p,a)
91 #else
92 #define __VA_START__(p,a)       va_start(p)
93 #endif
94 #endif
95 #include        "defs.h"
96 #include        <fcin.h>
97 #include        <ls.h>
98 #if !defined(va_start)
99 #if defined(__STDARG__)
100 #include <stdarg.h>
101 #else
102 #include <varargs.h>
103 #endif
104 #endif
105
106 #include        "variables.h"
107 #include        "io.h"
108 #include        "jobs.h"
109 #include        "shnodes.h"
110 #include        "history.h"
111 #include        "edit.h"
112 #include        "timeout.h"
113 #include        "FEATURE/externs"
114 #include        "FEATURE/dynamic"
115 #include        "FEATURE/poll"
116
117 #ifdef  FNDELAY
118 #   ifdef EAGAIN
119 #       if EAGAIN!=EWOULDBLOCK
120 #           undef EAGAIN
121 #           define EAGAIN       EWOULDBLOCK
122 #       endif
123 #   else
124 #       define EAGAIN   EWOULDBLOCK
125 #   endif /* EAGAIN */
126 #   ifndef O_NONBLOCK
127 #       define O_NONBLOCK       FNDELAY
128 #   endif /* !O_NONBLOCK */
129 #endif  /* FNDELAY */
130 #define RW_ALL  (S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH)
131
132 static __V_     *timeout;
133
134 #if defined(_lib_socket) && defined(_sys_socket) && defined(_hdr_netinet_in)
135 #   include <sys/socket.h>
136 #   include <netinet/in.h>
137     static int str2inet __PROTO__((const char*, struct sockaddr_in*));
138 #   define SOCKET       1
139 #else
140 #   undef SOCKET
141 #endif
142
143 struct fdsave
144 {
145         int     orig_fd;        /* original file descriptor */
146         int     save_fd;        /* saved file descriptor */
147 };
148
149
150 static int      eval_exceptf __PROTO__((Sfio_t*, int, Sfdisc_t*));
151 static int      piperead __PROTO__((Sfio_t*, __V_*, int, Sfdisc_t*));
152 static int      slowread __PROTO__((Sfio_t*, __V_*, int, Sfdisc_t*));
153 static int      slowexcept __PROTO__((Sfio_t*, int, Sfdisc_t*));
154 static int      pipeexcept __PROTO__((Sfio_t*, int, Sfdisc_t*));
155 static int      io_prompt __PROTO__((int));
156 static int      io_heredoc __PROTO__((register struct ionod*));
157 static void     sftrack __PROTO__((Sfio_t*,int,int));
158 static int      tee_write __PROTO__((Sfio_t*,const __V_*,int,Sfdisc_t*));
159 static const Sfdisc_t eval_disc = { NULL, NULL, NULL, eval_exceptf, NULL};
160 static Sfdisc_t tee_disc = {NULL,tee_write,NULL,NULL,NULL};
161 static Sfio_t *subopen __PROTO__((Sfio_t*, off_t, long));
162 static int      subread __PROTO__((Sfio_t*, __V_*, int, Sfdisc_t*));
163 static int      subexcept __PROTO__((Sfio_t*, int, Sfdisc_t*));
164 static const Sfdisc_t sub_disc = { subread, 0, 0, subexcept, 0 };
165
166 struct subfile
167 {
168         Sfdisc_t        disc;
169         Sfio_t          *oldsp;
170         off_t           offset;
171         long            size;
172         long            left;
173 };
174
175 static struct fdsave    *filemap;
176 static short            filemapsize;
177
178 /* ======== input output and file copying ======== */
179
180 void sh_ioinit __PARAM__((void), ()){
181         register int n;
182         filemapsize = 8;
183         filemap = (struct fdsave*)malloc(8*sizeof(struct fdsave));
184 #ifdef SHOPT_FASTPIPE
185         n = sh.lim.open_max+2;
186 #else
187         n = sh.lim.open_max;
188 #endif /* SHOPT_FASTPIPE */
189         sh.fdstatus = (unsigned char*)malloc((unsigned)n);
190         memset((char*)sh.fdstatus,0,n);
191         sh.fdptrs = (int**)malloc(n*sizeof(int*));
192         memset((char*)sh.fdptrs,0,n*sizeof(int*));
193         sh.sftable = (Sfio_t**)malloc(n*sizeof(Sfio_t*));
194         memset((char*)sh.sftable,0,n*sizeof(Sfio_t*));
195         sh.sftable[0] = sfstdin;
196         sh.sftable[1] = sfstdout;
197         sh.sftable[2] = sfstderr;
198         sfnotify(sftrack);
199         sh_iostream(0);
200         /* all write steams are in the same pool and share outbuff */
201         sh.outpool = sfopen(NIL(Sfio_t*),NIL(char*),"sw");  /* pool identifier */
202         sh.outbuff = (char*)malloc(IOBSIZE);
203         sh.errbuff = (char*)malloc(IOBSIZE/4);
204         sfsetbuf(sfstderr,sh.errbuff,IOBSIZE/4);
205         sfsetbuf(sfstdout,sh.outbuff,IOBSIZE);
206         sfpool(sfstdout,sh.outpool,SF_WRITE);
207         sfpool(sfstderr,sh.outpool,SF_WRITE);
208         sfset(sfstdout,SF_LINE,0);
209 }
210
211 /*
212  * create or initialize a stream corresponding to descriptor <fd>
213  * a buffer with room for a sentinal is allocated for a read stream.
214  * A discipline is inserted when read stream is a tty or a pipe
215  * For output streams, the buffer is set to sh.output and put into
216  * the sh.outpool synchronization pool
217  */
218 Sfio_t *sh_iostream __PARAM__((register int fd), (fd)) __OTORP__(register int fd;){
219         register Sfio_t *iop;
220         register int status = sh_iocheckfd(fd);
221         register int flags = SF_WRITE;
222         char *bp;
223         int size;
224 #ifdef SHOPT_FASTPIPE
225         if(fd>=sh.lim.open_max)
226                 return(sh.sftable[fd]);
227 #endif /* SHOPT_FASTPIPE */
228         if(status==IOCLOSE)
229         {
230                 switch(fd)
231                 {
232                     case 0:
233                         return(sfstdin);
234                     case 1:
235                         return(sfstdout);
236                     case 2:
237                         return(sfstderr);
238                 }
239                 return(NIL(Sfio_t*));
240         }
241         if(status&IOREAD)
242         {
243                 size=roundof(IOBSIZE+1,sizeof(__V_*)) + sizeof(Sfdisc_t);
244                 if(!(bp = (char *)malloc(size)))
245                         return(NIL(Sfio_t*));
246                 flags |= SF_READ;
247                 if(!(status&IOWRITE))
248                         flags &= ~SF_WRITE;
249         }
250         else
251                 bp = sh.outbuff;
252         if(status&IODUP)
253                 flags |= SF_SHARE|SF_PUBLIC;
254         if((iop = sh.sftable[fd]) && sffileno(iop)>=0)
255                 sfsetbuf(iop, bp, IOBSIZE);
256         else if(!(iop=sfnew((fd<=2?iop:0),bp,IOBSIZE,fd,flags)))
257                 return(NIL(Sfio_t*));
258         if(status&IOREAD)
259         {
260                 Sfdisc_t *dp;
261                 sfset(iop,SF_MALLOC,1);
262                 {
263                         dp = (Sfdisc_t*)(bp+roundof(IOBSIZE+1,sizeof(__V_*)));
264                         if(status&IOTTY)
265                                 dp->readf = slowread;
266                         else if(status&IONOSEEK)
267                                 dp->readf = piperead;
268                         else
269                                 dp->readf = 0;
270                         dp->seekf = 0;
271                         dp->writef = 0;
272                         dp->exceptf = slowexcept;
273                         sfdisc(iop,dp);
274                 }
275         }
276         else
277                 sfpool(iop,sh.outpool,SF_WRITE);
278         sh.sftable[fd] = iop;
279         return(iop);
280 }
281
282 /*
283  * preserve the file descriptor or stream by moving it
284  */
285 static void io_preserve __PARAM__((register Sfio_t *sp, register int f2), (sp, f2)) __OTORP__(register Sfio_t *sp; register int f2;){
286         register int fd;
287         if(sp)
288                 fd = sfsetfd(sp,10);
289         else
290                 fd = fcntl(f2,F_DUPFD,10);
291         if(f2==sh.infd)
292                 sh.infd = fd;
293         if(fd<0)
294                 error(ERROR_system(1),e_toomany);
295         if(sh.fdptrs[fd]=sh.fdptrs[f2])
296         {
297                 if(f2==job.fd)
298                         job.fd=fd;
299                 *sh.fdptrs[fd] = fd;
300                 sh.fdptrs[f2] = 0;
301         }
302         sh.sftable[fd] = sp;
303         sh.fdstatus[fd] = sh.fdstatus[f2];
304         if(fcntl(f2,F_GETFD,0)&1)
305         {
306                 fcntl(fd,F_SETFD,FD_CLOEXEC);
307                 sh.fdstatus[fd] |= IOCLEX;
308         }
309         sh.sftable[f2] = 0;
310 }
311
312 /*
313  * Given a file descriptor <f1>, move it to a file descriptor number <f2>
314  * If <f2> is needed move it, otherwise it is closed first.
315  * The original stream <f1> is closed.
316  *  The new file descriptor <f2> is returned;
317  */
318 int sh_iorenumber __PARAM__((register int f1,register int f2), (f1, f2)) __OTORP__(register int f1;register int f2;){
319         register Sfio_t *sp = sh.sftable[f2];
320         if(f1!=f2)
321         {
322                 /* see whether file descriptor is in use */
323                 if(sh_inuse(f2) || (f2>2 && sp))
324                 {
325                         io_preserve(sp,f2);
326                         sp = 0;
327                 }
328                 else if(f2==0)
329                         sh.st.ioset = 1;
330                 sh_close(f2);
331                 if(f2<=2 && sp)
332                 {
333                         register Sfio_t *spnew = sh_iostream(f1);
334                         sh.fdstatus[f2] = (sh.fdstatus[f1]&~IOCLEX);
335                         sfsetfd(spnew,f2);
336                         sfswap(spnew,sp);
337                         sfset(sp,SF_SHARE|SF_PUBLIC,1);
338                 }
339                 else 
340                 {
341                         sh.fdstatus[f2] = (sh.fdstatus[f1]&~IOCLEX);
342                         if((f2 = fcntl(f1,F_DUPFD, f2)) < 0)
343                                 error(ERROR_system(1),e_file+4);
344                         else if(f2 <= 2)
345                                 sh_iostream(f2);
346                 }
347                 if(sp)
348                         sh.sftable[f1] = 0;
349                 sh_close(f1);
350         }
351         return(f2);
352 }
353
354 /*
355  * close a file descriptor and update stream table and attributes 
356  */
357 int sh_close __PARAM__((register int fd), (fd)) __OTORP__(register int fd;){
358         register Sfio_t *sp;
359         register int r = 0;
360         if(fd<0)
361                 return(-1);
362         if(fd==sh.coutpipe)
363         {
364                 sh.cpid = 0;
365                 sh.coutpipe = -1;
366         }
367         if(!(sp=sh.sftable[fd]) || sfclose(sp) < 0)
368                 r=close(fd);
369         if(fd>2)
370                 sh.sftable[fd] = 0;
371         sh.fdstatus[fd] = IOCLOSE;
372         if(sh.fdptrs[fd])
373                 *sh.fdptrs[fd] = -1;
374         sh.fdptrs[fd] = 0;
375         return(r);
376 }
377
378 /*
379  * Open a file for reading
380  * On failure, print message.
381  */
382 int sh_open __PARAM__((register const char *path, int flags, ...), (va_alist)) __OTORP__(va_dcl)
383 { __OTORP__(register const char *path; int flags; )
384         register int            fd;
385         mode_t                  mode;
386 #ifdef SOCKET
387         struct sockaddr_in      addr;
388 #endif /* SOCKET */
389         va_list                 ap;
390         __VA_START__(ap, flags); __OTORP__(path = va_arg(ap, const char *);flags = va_arg(ap, int );)
391         mode = (flags & O_CREAT) ? va_arg(ap, int) : 0;
392         va_end(ap);
393         errno = 0;
394         if(*path==0)
395         {
396                 errno = ENOENT;
397                 return(-1);
398         }
399         if (strmatch(path,e_devfdNN))
400         {
401                 fd = atoi(path+8);
402                 if((mode=sh_iocheckfd(fd))==IOCLOSE)
403                         return(-1);
404                 flags &= (O_RDWR|O_WRONLY|O_RDONLY);
405                 if(!(mode&IOWRITE) && ((flags==O_WRONLY) || (flags==O_RDWR)))
406                         return(-1);
407                 if(!(mode&IOREAD) && ((flags==O_RDONLY) || (flags==O_RDWR)))
408                         return(-1);
409                 return(fd);
410
411         }
412 #ifdef SOCKET
413         if (strmatch(path, "/dev/@(tcp|udp)/*/*") && str2inet(path + 9, &addr))
414         {
415                 if ((fd = socket(AF_INET, path[5] == 't' ? SOCK_STREAM : SOCK_DGRAM, 0)) >= 0 && connect(fd, (struct sockaddr*)&addr, sizeof(addr)))
416                 {
417                         close(fd);
418                         fd = -1;
419                 }
420         }
421         else
422 #endif /* SOCKET */
423         if((fd = open(path, flags, mode)) < 0)
424                 return(-1);
425         if(flags&O_WRONLY)
426                 mode = IOWRITE;
427         else if(flags&O_RDWR)
428                 mode = (IOREAD|IOWRITE);
429         else
430                 mode = IOREAD;
431         sh.fdstatus[fd] = mode;
432         return(fd);
433 }
434
435 int sh_chkopen __PARAM__((register const char *name), (name)) __OTORP__(register const char *name;){
436         register int fd = sh_open(name,O_RDONLY,0);
437         if(fd < 0)
438                 error(ERROR_system(1),e_open,name);
439         return(fd);
440 }
441
442 /*
443  * move open file descriptor to a number > 2
444  */
445 int sh_iomovefd __PARAM__((register int fdold), (fdold)) __OTORP__(register int fdold;){
446         register int fdnew;
447         if(fdold<0 || fdold>2)
448                 return(fdold);
449         fdnew = sh_iomovefd(dup(fdold));
450         sh.fdstatus[fdnew] = (sh.fdstatus[fdold]&~IOCLEX);
451         close(fdold);
452         sh.fdstatus[fdold] = IOCLOSE;
453         return(fdnew);
454 }
455
456 /*
457  * create a pipe and print message on failure
458  */
459 int     sh_pipe __PARAM__((register int pv[]), (pv)) __OTORP__(register int pv[];){
460         int fd[2];
461         if(pipe(fd)<0 || (pv[0]=fd[0])<0 || (pv[1]=fd[1])<0)
462                 error(ERROR_system(1),e_pipe);
463         pv[0] = sh_iomovefd(pv[0]);
464         pv[1] = sh_iomovefd(pv[1]);
465         sh.fdstatus[pv[0]] = IONOSEEK|IOREAD|IODUP;
466         sh.fdstatus[pv[1]] = IONOSEEK|IOWRITE;
467         return(0);
468 }
469
470 /*
471  * close a pipe
472  */
473 void sh_pclose __PARAM__((register int pv[]), (pv)) __OTORP__(register int pv[];){
474         if(pv[0]>=2)
475                 sh_close(pv[0]);
476         if(pv[1]>=2)
477                 sh_close(pv[1]);
478         pv[0] = pv[1] = -1;
479 }
480
481 /*
482  * I/O redirection
483  * flag = 0 if files are to be restored
484  * flag = 2 if files are to be closed on exec
485  * flag = 3 when called from $( < ...), just open file and return
486  */
487 int     sh_redirect __PARAM__((struct ionod *iop, int flag), (iop, flag)) __OTORP__(struct ionod *iop; int flag;){
488         register char *fname;
489         register int    fd, iof;
490         register Namval_t *np=0;
491         const char *message = e_open;
492         int o_mode;             /* mode flag for open */
493         static char io_op[5];   /* used for -x trace info */
494         int clexec=0, fn, traceon;
495         int indx = sh.topfd;
496         if(flag==2)
497                 clexec = 1;
498         if(iop)
499                 traceon = sh_trace(NIL(char**),0);
500         for(;iop;iop=iop->ionxt)
501         {
502                 iof=iop->iofile;
503                 fn = (iof&IOUFD);
504                 fname=iop->ioname;
505                 io_op[0] = '0'+(iof&IOUFD);
506                 if(iof&IOPUT)
507                 {
508                         io_op[1] = '>';
509                         o_mode = O_WRONLY|O_CREAT;
510                 }
511                 else
512                 {
513                         io_op[1] = '<';
514                         o_mode = O_RDONLY|O_NONBLOCK;
515                 }
516                 io_op[2] = 0;
517                 io_op[3] = 0;
518                 fname = iop->ioname;
519                 if(!(iof&IORAW))
520                         fname=sh_mactrim(fname,sh_isoption(SH_INTERACTIVE)?2:0);
521                 errno=0;
522                 if(*fname)
523                 {
524                         if(iof&IODOC)
525                         {
526                                 if(traceon)
527                                 {
528                                         io_op[2] = '<';
529                                         sfputr(sfstderr,io_op,'\n');
530                                 }
531                                 fd = io_heredoc(iop);
532                                 fname = 0;
533                         }
534                         else if(iof&IOMOV)
535                         {
536                                 int dupfd,toclose= -1;
537                                 io_op[2] = '&';
538                                 if((fd=fname[0])>='0' && fd<='9')
539                                 {
540                                         char *number = fname;
541                                         dupfd = strtol(fname,&number,10);
542                                         if(*number=='-')
543                                         {
544                                                 toclose = dupfd;
545                                                 number++;
546                                         }
547                                         if(*number || dupfd > IOUFD)
548                                         {
549                                                 message = e_file;
550                                                 goto fail;
551                                         }
552                                         if(sh.subshell && dupfd==1)
553                                         {
554                                                 sh_subtmpfile();
555                                                 dupfd = sffileno(sfstdout);
556                                         }
557                                 }
558                                 else if(fd=='-' && fname[1]==0)
559                                 {
560                                         fd= -1;
561                                         goto trace;
562                                 }
563                                 else if(fd=='p' && fname[1]==0)
564                                 {
565                                         if(iof&IOPUT)
566                                                 toclose = dupfd = sh.coutpipe;
567                                         else
568                                                 toclose = dupfd = sh.cpipe[0];
569                                 }
570                                 else
571                                 {
572                                         message = e_file;
573                                         goto fail;
574                                 }
575                                 if((fd=fcntl(dupfd,F_DUPFD,3))<0)
576                                         goto fail;
577                                 sh_iocheckfd(dupfd);
578                                 sh.fdstatus[fd] = (sh.fdstatus[dupfd]&~IOCLEX);
579                                 if(toclose<0 && sh.fdstatus[fd]&IOREAD)
580                                         sh.fdstatus[fd] |= IODUP;
581                                 else if(dupfd==sh.cpipe[0])
582                                         sh_pclose(sh.cpipe);
583                                 else if(toclose>=0)
584                                 {
585                                         if(flag==0)
586                                                 sh_iosave(toclose,indx); /* save file descriptor */
587                                         sh_close(toclose);
588                                 }
589                         }
590                         else if(iof&IORDW)
591                         {
592                                 io_op[2] = '>';
593                                 o_mode = O_RDWR|O_CREAT;
594                                 goto openit;
595                         }
596                         else if(!(iof&IOPUT))
597                                 fd=sh_chkopen(fname);
598                         else if(sh_isoption(SH_RESTRICTED))
599                                 error(ERROR_exit(1),e_restricted,fname);
600                         else
601                         {
602                                 if(iof&IOAPP)
603                                 {
604                                         io_op[2] = '>';
605                                         o_mode |= O_APPEND;
606                                 }
607                                 else
608                                 {
609                                         o_mode |= O_TRUNC;
610                                         if(iof&IOCLOB)
611                                                 io_op[2] = '|';
612                                         else if(sh_isoption(SH_NOCLOBBER))
613                                         {
614                                                 struct stat sb;
615                                                 if(stat(fname,&sb)>=0)
616                                                 {
617 #ifdef SHOPT_FS_3D
618                                                         if(S_ISREG(sb.st_mode)&&
619                                                                 (!sh.lim.fs3d || iview(&sb)==0))
620 #else
621                                                         if(S_ISREG(sb.st_mode))
622 #endif /* SHOPT_FS_3D */
623                                                         {
624                                                                 errno = EEXIST;
625                                                                 error(ERROR_system(1),e_exists,fname);
626                                                         }
627                                                 }
628                                                 else
629                                                         o_mode |= O_EXCL;
630                                         }
631                                 }
632                         openit:
633                                 if((fd=sh_open(fname,o_mode,RW_ALL)) <0)
634                                         error(ERROR_system(1),((o_mode&O_CREAT)?e_create:e_open),fname);
635                         }
636                 trace:
637                         if(traceon && fname)
638                                 sfprintf(sfstderr,"%s %s%c",io_op,fname,iop->ionxt?' ':'\n');
639                         if(flag==0)
640                                 sh_iosave(fn,indx); /* save file descriptor */
641                         if(fd<0)
642                         {
643                                 if(sh_inuse(fn) || fn==sh.infd)
644                                         io_preserve(sh.sftable[fn],fn);
645                                 sh_close(fn);
646                         }
647                         if(flag==3)
648                                 return(fd);
649                         if(fd>=0)
650                                 fd = sh_iorenumber(sh_iomovefd(fd),fn);
651                         if(fd >2 && clexec)
652                         {
653                                 fcntl(fd,F_SETFD,FD_CLOEXEC);
654                                 sh.fdstatus[fd] |= IOCLEX;
655                         }
656                 }
657         }
658         return(indx);
659 fail:
660         error(ERROR_system(1),message,fname);
661         /* NOTREACHED */
662 }
663 /*
664  * Create a tmp file for the here-document
665  */
666 static int io_heredoc __PARAM__((register struct ionod *iop), (iop)) __OTORP__(register struct ionod *iop;){
667         register Sfio_t *infile = 0, *outfile;
668         register char           fd;
669         /* create an unnamed temporary file */
670         if(!(outfile=sftmp(0)))
671                 error(ERROR_system(1),e_tmpcreate);
672         if(!(iop->iofile&IOSTRG))
673         {
674                 if(!sh.heredocs || iop->iosize==0)
675                         return(sh_open(e_devnull,O_RDONLY));
676                 infile = subopen(sh.heredocs,iop->iooffset,iop->iosize);
677         }
678         if(iop->iofile&IOQUOTE)
679         {
680                 /* This is a quoted here-document, not expansion */
681                 if(!infile)
682                         infile = sfopen(NIL(Sfio_t*),iop->ioname,"s");
683                 sfmove(infile,outfile,SF_UNBOUND,-1);
684                 sfclose(infile);
685         }
686         else
687         {
688                 if(sh_isoption(SH_XTRACE))
689                         sfdisc(outfile,&tee_disc);
690                 sh_machere(infile,outfile,iop->ioname);
691                 if(infile)
692                         sfclose(infile);
693         }
694         /* close stream outfile, but save file descriptor */
695         fd = sffileno(outfile);
696         sfsetfd(outfile,-1);
697         sfclose(outfile);
698         lseek(fd,(off_t)0,SEEK_SET);
699         sh.fdstatus[fd] = IOREAD;
700         return(fd);
701 }
702
703 /*
704  * This write discipline also writes the output on standard error
705  * This is used when tracing here-documents
706  */
707 static int tee_write __PARAM__((Sfio_t *iop,const __V_ *buff,int n,Sfdisc_t *unused), (iop, buff, n, unused)) __OTORP__(Sfio_t *iop;const __V_ *buff;int n;Sfdisc_t *unused;){
708         NOT_USED(unused);
709         sfwrite(sfstderr,buff,n);
710         return(write(sffileno(iop),buff,n));
711 }
712
713 /*
714  * copy file <origfd> into a save place
715  * The saved file is set close-on-exec
716  * if <origfd> < 0, then -origfd is saved, but not duped so that it
717  *   will be closed with sh_iorestore.
718  */
719 void sh_iosave __PARAM__((register int origfd, int oldtop), (origfd, oldtop)) __OTORP__(register int origfd; int oldtop;){
720 /*@
721         assume oldtop>=0 && oldtop<sh.lim.open_max;
722 @*/
723  
724         register int    savefd;
725         /* see if already saved, only save once */
726         for(savefd=sh.topfd; --savefd>=oldtop; )
727         {
728                 if(filemap[savefd].orig_fd == origfd)
729                         return;
730         }
731         /* make sure table is large enough */
732         if(sh.topfd >= filemapsize)
733         {
734                 filemapsize += 8;
735                 if(!(filemap = (struct fdsave*)realloc(filemap,filemapsize*sizeof(struct fdsave))))
736                         error(ERROR_exit(4),e_nospace);
737                         
738         }
739 #ifdef SHOPT_DEVFD
740         if(origfd <0)
741         {
742                 savefd = origfd;
743                 origfd = -origfd;
744         }
745         else
746 #endif /* SHOPT_DEVFD */
747         {
748                 if((savefd = fcntl(origfd, F_DUPFD, 3)) < 0 && errno!=EBADF)
749                         error(ERROR_system(1),e_toomany);
750         }
751         filemap[sh.topfd].orig_fd = origfd;
752         filemap[sh.topfd++].save_fd = savefd;
753         if(savefd >=0)
754         {
755                 register Sfio_t* sp = sh.sftable[origfd];
756                 /* make saved file close-on-exec */
757                 fcntl(savefd,F_SETFD,FD_CLOEXEC);
758                 if(origfd==job.fd)
759                         job.fd = savefd;
760                 sh.fdstatus[savefd] = sh.fdstatus[origfd];
761                 sh.fdptrs[savefd] = &filemap[sh.topfd-1].save_fd;
762                 if(!(sh.sftable[savefd]=sp))
763                         return;
764                 sfsync(sp);
765                 if(origfd <=2)
766                 {
767                         /* copy standard stream to new stream */
768                         sp = sfswap(sp,NIL(Sfio_t*));
769                         sh.sftable[savefd] = sp;
770                 }
771                 else
772                         sh.sftable[origfd] = 0;
773         }
774 }
775
776 /*
777  *  close all saved file descriptors
778  */
779 void    sh_iounsave __PARAM__((void), ()){
780         register int fd, savefd, newfd;
781         for(newfd=fd=0; fd < sh.topfd; fd++)
782         {
783                 if((savefd = filemap[fd].save_fd)< 0)
784                         filemap[newfd++] = filemap[fd];
785                 else
786                 {
787                         sh.sftable[savefd] = 0;
788                         sh_close(savefd);
789                 }
790         }
791         sh.topfd = newfd;
792 }
793
794 /*
795  *  restore saved file descriptors from <last> on
796  */
797 void    sh_iorestore __PARAM__((int last), (last)) __OTORP__(int last;){
798         register int    origfd, savefd, fd;
799         for (fd = sh.topfd - 1; fd >= last; fd--)
800         {
801                 origfd = filemap[fd].orig_fd;
802                 sh_close(origfd);
803                 if ((savefd = filemap[fd].save_fd) >= 0)
804                 {
805                         fcntl(savefd, F_DUPFD, origfd);
806                         if(savefd==job.fd)
807                                 job.fd=origfd;
808                         sh.fdstatus[origfd] = sh.fdstatus[savefd];
809                         /* turn off close-on-exec if flag if necessary */
810                         if(sh.fdstatus[origfd]&IOCLEX)
811                                 fcntl(origfd,F_SETFD,FD_CLOEXEC);
812                         if(origfd<=2)
813                         {
814                                 sfswap(sh.sftable[savefd],sh.sftable[origfd]);
815                                 if(origfd==0)
816                                         sh.st.ioset = 0;
817                         }
818                         else
819                                 sh.sftable[origfd] = sh.sftable[savefd];
820                         sh.sftable[savefd] = 0;
821                         sh_close(savefd);
822                 }
823         }
824         if(last < sh.topfd)
825                 sh.topfd = last;
826 }
827
828 /*
829  * returns access information on open file <fd>
830  * returns -1 for failure, 0 for success
831  * <mode> is the same as for access()
832  */
833 sh_ioaccess __PARAM__((int fd,register int mode), (fd, mode)) __OTORP__(int fd;register int mode;){
834         register int flags;
835         if(mode==X_OK)
836                 return(-1);
837         if((flags=sh_iocheckfd(fd))!=IOCLOSE)
838         {
839                 if(mode==F_OK)
840                         return(0);
841                 if(mode==R_OK && (flags&IOREAD))
842                         return(0);
843                 if(mode==W_OK && (flags&IOWRITE))
844                         return(0);
845         }
846         return(-1);
847 }
848
849 #ifdef SOCKET
850 /*
851  * convert string to sockaddr_in
852  * 0 returned on error
853  */
854
855 static int str2inet __PARAM__((register const char *sp, struct sockaddr_in *addr), (sp, addr)) __OTORP__(register const char *sp; struct sockaddr_in *addr;){
856         register int    n=0,c,v;
857         unsigned long   a=0;
858         unsigned short  p;
859
860         for (;;)
861         {
862                 v = 0;
863                 while ((c = *sp++) >= '0' && c <= '9')
864                         v = v * 10 + c - '0';
865                 if (++n <= 4) a = (a << 8) | (v & 0xff);
866                 else
867                 {
868                         if (c) return(0);
869                         p = v;
870                         break;
871                 }
872                 if (c != '.' && c != '/') return(0);
873         }
874         memset((char*)addr, 0, sizeof(*addr));
875         addr->sin_family = AF_INET;
876         addr->sin_addr.s_addr = htonl(a);
877         addr->sin_port = htons(p);
878         return(1);
879 }
880 #endif /* SOCKET */
881
882
883 /*
884  *  Handle interrupts for slow streams
885  */
886 static int slowexcept __PARAM__((register Sfio_t *iop, int type, Sfdisc_t *handle), (iop, type, handle)) __OTORP__(register Sfio_t *iop; int type; Sfdisc_t *handle;){
887         register int    n,fno;
888         NOT_USED(handle);
889         if(type!=SF_READ)
890                 return(0);
891         if((sh.trapnote&(SH_SIGSET|SH_SIGTRAP)) && errno!=EIO && errno!=ENXIO)
892                 errno = EINTR;
893         if((n=sfslen())<=0)
894         {
895                 fno = sffileno(iop);
896 #ifndef FNDELAY
897 #   ifdef O_NDELAY
898                 if(errno==0 && (n=fcntl(fno,F_GETFL,0))&O_NDELAY)
899                 {
900                         n &= ~O_NDELAY;
901                         fcntl(fno, F_SETFL, n);
902                         return(1);
903                 }
904 #   endif /* O_NDELAY */
905 #endif /* !FNDELAY */
906 #ifdef O_NONBLOCK
907                 if(errno==EAGAIN)
908                 {
909                         n = fcntl(fno,F_GETFL,0);
910                         n &= ~O_NONBLOCK;
911                         fcntl(fno, F_SETFL, n);
912                         return(1);
913                 }
914 #endif /* O_NONBLOCK */
915                 if(errno!=EINTR)
916                         return(0);
917                 n=1;
918         }
919         errno = 0;
920         if(sh.trapnote&SH_SIGSET)
921         {
922                 sfputc(sfstderr,'\n');
923                 sh_exit(SH_EXITSIG);
924         }
925         if(sh.trapnote&SH_SIGTRAP)
926                 sh_chktrap();
927         return(n);
928 }
929
930 /*
931  * called when slowread times out
932  */
933 static void time_grace __PARAM__((__V_ *handle), (handle)) __OTORP__(__V_ *handle;){
934         NOT_USED(handle);
935         timeout = 0;
936         if(sh_isstate(SH_GRACE))
937         {
938                 sh_offstate(SH_GRACE);
939                 if(!sh_isstate(SH_INTERACTIVE))
940                         return;
941                 ((struct checkpt*)sh.jmplist)->mode = SH_JMPEXIT;
942                 error(2,e_timeout);
943                 sh.trapnote |= SH_SIGSET;
944                 return;
945         }
946         error(0,e_timewarn);
947         sh_onstate(SH_GRACE);
948         sigrelease(SIGALRM);
949         sh.trapnote |= SH_SIGTRAP;
950 }
951
952 static int piperead __PARAM__((Sfio_t *iop,__V_ *buff,register int size,Sfdisc_t *handle), (iop, buff, size, handle)) __OTORP__(Sfio_t *iop;__V_ *buff;register int size;Sfdisc_t *handle;){
953         NOT_USED(handle);
954         size = ed_read(sffileno(iop), (char*)buff, size);
955         return(size);
956 }
957 /*
958  * This is the read discipline that is applied to slow devices
959  * This routine takes care of prompting for input
960  */
961 static int slowread __PARAM__((Sfio_t *iop,__V_ *buff,register int size,Sfdisc_t *handle), (iop, buff, size, handle)) __OTORP__(Sfio_t *iop;__V_ *buff;register int size;Sfdisc_t *handle;){
962         int     (*readf) __PROTO__((int, char*, int));
963         NOT_USED(handle);
964         if(io_prompt(sh.nextprompt)<0 && errno==EIO)
965                 return(0);
966         if(sh.timeout)
967                 timeout = (__V_*)kshtimeradd(sh_isstate(SH_GRACE)?1000L*TGRACE:1000L*sh.timeout,0,time_grace,NIL(__V_*));
968 #   ifdef SHOPT_ESH
969         if(sh_isoption(SH_EMACS|SH_GMACS))
970                 readf = ed_emacsread;
971         else
972 #   endif       /* SHOPT_ESH */
973 #   ifdef SHOPT_VSH
974         if(sh_isoption(SH_VI))
975                 readf = ed_viread;
976         else
977 #   endif       /* SHOPT_VSH */
978                 readf = ed_read;
979         size = (*readf)(sffileno(iop), (char*)buff, size);
980         if(timeout)
981                 timerdel(timeout);
982         timeout=0;
983         return(size);
984 }
985
986 /*
987  * check and return the attributes for a file descriptor
988  */
989
990 int sh_iocheckfd __PARAM__((register int fd), (fd)) __OTORP__(register int fd;){
991         register int flags, n;
992         if((n=sh.fdstatus[fd])&IOCLOSE)
993                 return(n);
994         if(!(n&(IOREAD|IOWRITE)))
995         {
996 #ifdef F_GETFL
997                 if((flags=fcntl(fd,F_GETFL,0)) < 0)
998                         return(sh.fdstatus[fd]=IOCLOSE);
999                 if(!(flags&O_WRONLY))
1000                         n |= IOREAD;
1001                 if(flags&(O_WRONLY|O_RDWR))
1002                         n |= IOWRITE;
1003 #else
1004                 struct stat statb;
1005                 if((flags = fstat(fd,&statb))< 0)
1006                         return(sh.fdstatus[fd]=IOCLOSE);
1007                 n |= (IOREAD|IOWRITE);
1008                 if(read(fd,"",0) < 0)
1009                         n &= ~IOREAD;
1010 #endif /* F_GETFL */
1011         }
1012         if(!(n&(IOSEEK|IONOSEEK)))
1013         {
1014                 struct stat statb;
1015                 /* /dev/null check is a workaround for select bug */
1016                 static ino_t null_ino;
1017                 static dev_t null_dev;
1018                 if(null_ino==0 && stat(e_devnull,&statb) >=0)
1019                 {
1020                         null_ino = statb.st_ino;
1021                         null_dev = statb.st_dev;
1022                 }
1023                 if(tty_check(fd))
1024                         n |= IOTTY;
1025                 if(lseek(fd,NIL(off_t),SEEK_CUR)<0)
1026                 {
1027                         n |= IONOSEEK;
1028 #ifdef S_ISSOCK
1029                         if((fstat(fd,&statb)>=0) && S_ISSOCK(statb.st_mode))
1030                                 n |= IOREAD|IOWRITE;
1031 #endif /* S_ISSOCK */
1032                 }
1033                 else if((fstat(fd,&statb)>=0) && (
1034                         S_ISFIFO(statb.st_mode) ||
1035 #ifdef S_ISSOCK
1036                         S_ISSOCK(statb.st_mode) ||
1037 #endif /* S_ISSOCK */
1038                         /* The following is for sockets on the sgi */
1039                         (statb.st_ino==0 && (statb.st_mode & ~(S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH|S_IXUSR|S_IXGRP|S_IXOTH|S_ISUID|S_ISGID))==0) ||
1040                         (S_ISCHR(statb.st_mode) && (statb.st_ino!=null_ino || statb.st_dev!=null_dev))
1041                 ))
1042                         n |= IONOSEEK;
1043                 else
1044                         n |= IOSEEK;
1045         }
1046         sh.fdstatus[fd] = n;
1047         return(n);
1048 }
1049
1050 /*
1051  * Display prompt PS<flag> on standard error
1052  */
1053
1054 static int      io_prompt __PARAM__((register int flag), (flag)) __OTORP__(register int flag;){
1055         register char *cp;
1056         char *endprompt;
1057         static short cmdno;
1058         int sfflags;
1059         if(flag<3 && !sh_isstate(SH_INTERACTIVE))
1060                 flag = 0;
1061         if(flag==0)
1062                 return(sfsync(sfstderr));
1063         sfflags = sfset(sfstderr,SF_SHARE|SF_PUBLIC|SF_READ,0);
1064         if(!(sh.prompt=(char*)sfreserve(sfstderr,0,0)))
1065                 sh.prompt = "";
1066         switch(flag)
1067         {
1068                 case 1:
1069                 {
1070                         register int c;
1071 #if defined(TIOCLBIC) && defined(LFLUSHO)
1072                         if(!sh_isoption(SH_VI|SH_EMACS|SH_GMACS))
1073                         {
1074                                 /*
1075                                  * re-enable output in case the user has
1076                                  * disabled it.  Not needed with edit mode
1077                                  */
1078                                 int mode = LFLUSHO;
1079                                 ioctl(sffileno(sfstderr),TIOCLBIC,&mode);
1080                         }
1081 #endif  /* TIOCLBIC */
1082                         cp = sh_mactry(nv_getval(nv_scoped(PS1NOD)));
1083                         for(;c= *cp;cp++)
1084                         {
1085                                 if(c==HIST_CHAR)
1086                                 {
1087                                         /* look at next character */
1088                                         c = *++cp;
1089                                         /* print out line number if not !! */
1090                                         if(c!= HIST_CHAR)
1091                                         {
1092                                                 sfprintf(sfstderr,"%d", sh.hist_ptr?(int)sh.hist_ptr->histind:++cmdno);
1093                                         }
1094                                         if(c==0)
1095                                                 goto done;
1096                                 }
1097                                 sfputc(sfstderr,c);
1098                         }
1099                         goto done;
1100                 }
1101                 case 2:
1102                         cp = nv_getval(nv_scoped(PS2NOD));
1103                         break;
1104                 case 3:
1105                         cp = nv_getval(nv_scoped(PS3NOD));
1106                         break;
1107                 default:
1108                         goto done;
1109         }
1110         if(cp)
1111                 sfputr(sfstderr,cp,-1);
1112 done:
1113         if(*sh.prompt && (endprompt=(char*)sfreserve(sfstderr,0,0)))
1114                 *endprompt = 0;
1115         sfset(sfstderr,sfflags&SF_READ|SF_SHARE|SF_PUBLIC,1);
1116         return(sfsync(sfstderr));
1117 }
1118
1119 /*
1120  * This discipline is inserted on write pipes to prevent SIGPIPE
1121  * from causing an infinite loop
1122  */
1123 static int pipeexcept __PARAM__((Sfio_t* iop, int mode, Sfdisc_t* handle), (iop, mode, handle)) __OTORP__(Sfio_t* iop; int mode; Sfdisc_t* handle;){
1124         NOT_USED(iop);
1125         if(mode==SF_WRITE && errno==EINTR && sh.lastsig==SIGPIPE)
1126                 return(-1);
1127         else if(mode==SF_DPOP || mode==SF_CLOSE)
1128                 free((__V_*)handle);
1129         return(0);
1130 }
1131
1132 /*
1133  * keep track of each stream that is opened and closed
1134  */
1135 static void     sftrack __PARAM__((Sfio_t* sp,int flag, int newfd), (sp, flag, newfd)) __OTORP__(Sfio_t* sp;int flag; int newfd;){
1136         register int fd = sffileno(sp);
1137         register struct checkpt *pp;
1138         register int mode;
1139         if(flag==SF_SETFD && newfd<0)
1140         {
1141                 flag = SF_CLOSE;
1142         }
1143 #ifdef DEBUG
1144         if(flag==SF_READ || flag==SF_WRITE)
1145         {
1146                 char *z = fmtbase((long)getpid(),0,0);
1147                 write(ERRIO,z,strlen(z));
1148                 write(ERRIO,": ",2);
1149                 write(ERRIO,"attempt to ",11);
1150                 if(flag==SF_READ)
1151                         write(ERRIO,"read from",9);
1152                 else
1153                         write(ERRIO,"write to",8);
1154                 write(ERRIO," locked stream\n",15);
1155                 return;
1156         }
1157 #endif
1158         if((unsigned)fd >= sh.lim.open_max)
1159                 return;
1160         if(sh_isstate(SH_NOTRACK))
1161                 return;
1162         mode = sfset(sp,0,0);
1163         if(flag==SF_NEW && (mode&SF_WRITE) && (sh_iocheckfd(fd)&IONOSEEK))
1164         {
1165                 Sfdisc_t *dp = newof(0,Sfdisc_t,1,0);
1166                 dp->exceptf = pipeexcept;
1167                 sfdisc(sp,dp);
1168         }
1169         if(fd < 3)
1170                 return;
1171         if(flag==SF_NEW)
1172         {
1173                 sh.sftable[fd] = sp;
1174                 if(sh.fdstatus[fd]==IOCLOSE)
1175                 {
1176                         flag = (mode&SF_WRITE)?IOWRITE:0;
1177                         if(mode&SF_READ)
1178                                 flag |= IOREAD;
1179                         sh.fdstatus[fd] = flag;
1180                         sh_iostream(fd);
1181                 }
1182                 if((pp=(struct checkpt*)sh.jmplist) && pp->mode==SH_JMPCMD)
1183                 {
1184                         struct openlist *item;
1185                         /*
1186                          * record open file descriptors so they can
1187                          * be closed in case a longjmp prevents
1188                          * built-ins from cleanup
1189                          */
1190                         item = new_of(struct openlist, 0);
1191                         item->strm = sp;
1192                         item->next = pp->olist;
1193                         pp->olist = item;
1194                 }
1195         }
1196         else if(flag==SF_CLOSE) 
1197         {
1198                 sh.sftable[fd] = 0;
1199                 sh.fdstatus[fd]=IOCLOSE;
1200                 if(pp=(struct checkpt*)sh.jmplist)
1201                 {
1202                         struct openlist *item;
1203                         for(item=pp->olist; item; item=item->next)
1204                         {
1205                                 if(item->strm == sp)
1206                                 {
1207                                         item->strm = 0;
1208                                         break;
1209                                 }
1210                         }
1211                 }
1212         }
1213 }
1214
1215 struct eval
1216 {
1217         Sfdisc_t        disc;
1218         char            **argv;
1219         short           slen;
1220         char            addspace;
1221 };
1222
1223 /*
1224  * Create a stream consisting of a space separated argv[] list 
1225  */
1226
1227 Sfio_t *sh_sfeval __PARAM__((register char *argv[]), (argv)) __OTORP__(register char *argv[];){
1228         register Sfio_t *iop;
1229         register char *cp;
1230         if(argv[1])
1231                 cp = "";
1232         else
1233                 cp = argv[0];
1234         iop = sfopen(NIL(Sfio_t*),(char*)cp,"s");
1235         if(argv[1])
1236         {
1237                 register struct eval *ep;
1238                 if(!(ep = new_of(struct eval,0)))
1239                         return(NIL(Sfio_t*));
1240                 ep->disc = eval_disc;
1241                 ep->argv = argv;
1242                 ep->slen  = -1;
1243                 ep->addspace  = 0;
1244                 sfdisc(iop,&ep->disc);
1245         }
1246         return(iop);
1247 }
1248
1249 /*
1250  * This code gets called whenever an end of string is found with eval
1251  */
1252
1253 static int eval_exceptf __PARAM__((Sfio_t *iop,int type, Sfdisc_t *handle), (iop, type, handle)) __OTORP__(Sfio_t *iop;int type; Sfdisc_t *handle;){
1254         register struct eval *ep = (struct eval*)handle;
1255         register char   *cp;
1256         register int    len;
1257
1258         /* no more to do */
1259         if(type!=SF_READ || !(cp = ep->argv[0]))
1260         {
1261                 if(type==SF_CLOSE)
1262                 {
1263                         sfdisc(iop,SF_POPDISC);
1264                         if(ep)
1265                                 free((__V_*)ep);
1266                 }
1267                 return(0);
1268         }
1269
1270         if(!ep->addspace)
1271         {
1272                 /* get the length of this string */
1273                 ep->slen = len = strlen(cp);
1274                 /* move to next string */
1275                 ep->argv++;
1276         }
1277         else /* insert space between arguments */
1278         {
1279                 len = 1;
1280                 cp = " ";
1281         }
1282         /* insert the new string */
1283         sfsetbuf(iop,cp,len);
1284         ep->addspace = !ep->addspace;
1285         return(1);
1286 }
1287
1288 /*
1289  * This routine returns a stream pointer to a segment of length <size> from
1290  * the stream <sp> starting at offset <offset>
1291  * The stream can be read with the normal stream operations
1292  */
1293
1294 static Sfio_t *subopen __PARAM__((Sfio_t* sp, off_t offset, long size), (sp, offset, size)) __OTORP__(Sfio_t* sp; off_t offset; long size;){
1295         register struct subfile *disp;
1296         register int fd = sffileno(sp);
1297         if(sfseek(sp,offset,SEEK_SET) <0)
1298                 return(NIL(Sfio_t*));
1299         if(!(disp = (struct subfile*)malloc(sizeof(struct subfile)+IOBSIZE+1)))
1300                 return(NIL(Sfio_t*));
1301         disp->disc = sub_disc;
1302         disp->oldsp = sp;
1303         disp->offset = offset;
1304         disp->size = disp->left = size;
1305         sp = sfnew(NIL(Sfio_t*),(char*)(disp+1),IOBSIZE,sh.lim.open_max,SF_READ);
1306         sfdisc(sp,&disp->disc);
1307         return(sp);
1308 }
1309
1310 /*
1311  * read function for subfile discipline
1312  */
1313 static int subread __PARAM__((Sfio_t* sp,__V_* buff,register int size,Sfdisc_t* handle), (sp, buff, size, handle)) __OTORP__(Sfio_t* sp;__V_* buff;register int size;Sfdisc_t* handle;){
1314         register struct subfile *disp = (struct subfile*)handle;
1315         NOT_USED(sp);
1316         if(disp->left == 0)
1317                 return(0);
1318         if(size > disp->left)
1319                 size = disp->left;
1320         disp->left -= size;
1321         return(sfread(disp->oldsp,buff,size));
1322 }
1323
1324 /*
1325  * exception handler for subfile discipline
1326  */
1327 static int subexcept __PARAM__((Sfio_t* sp,register int mode, Sfdisc_t* handle), (sp, mode, handle)) __OTORP__(Sfio_t* sp;register int mode; Sfdisc_t* handle;){
1328         register struct subfile *disp = (struct subfile*)handle;
1329         if(mode==SF_CLOSE)
1330         {
1331                 sfdisc(sp,SF_POPDISC);
1332                 free((__V_*)handle);
1333         }
1334         else  if(mode==SF_READ)
1335                 return(0);
1336         return(-1);
1337 }
1338
1339 #define NROW    15      /* number of rows before going to multi-columns */
1340 #define LBLSIZ  3       /* size of label field and interfield spacing */
1341 /* 
1342  * print a list of arguments in columns
1343  */
1344 void    sh_menu __PARAM__((Sfio_t *outfile,int argn,char *argv[]), (outfile, argn, argv)) __OTORP__(Sfio_t *outfile;int argn;char *argv[];){
1345         register int i,j;
1346         register char **arg;
1347         int nrow, ncol=1, ndigits=1;
1348         int fldsize, wsize = ed_window();
1349         char *cp = nv_getval(nv_scoped(LINES));
1350         nrow = (cp?1+2*(atoi(cp)/3):NROW);
1351         for(i=argn;i >= 10;i /= 10)
1352                 ndigits++;
1353         if(argn < nrow)
1354         {
1355                 nrow = argn;
1356                 goto skip;
1357         }
1358         i = 0;
1359         for(arg=argv; *arg;arg++)
1360         {
1361                 if((j=strlen(*arg)) > i)
1362                         i = j;
1363         }
1364         i += (ndigits+LBLSIZ);
1365         if(i < wsize)
1366                 ncol = wsize/i;
1367         if(argn > nrow*ncol)
1368         {
1369                 nrow = 1 + (argn-1)/ncol;
1370         }
1371         else
1372         {
1373                 ncol = 1 + (argn-1)/nrow;
1374                 nrow = 1 + (argn-1)/ncol;
1375         }
1376 skip:
1377         fldsize = (wsize/ncol)-(ndigits+LBLSIZ);
1378         for(i=0;i<nrow;i++)
1379         {
1380                 if(sh.trapnote&SH_SIGSET)
1381                         return;
1382                 j = i;
1383                 while(1)
1384                 {
1385                         arg = argv+j;
1386                         sfprintf(outfile,"%*d) %s",ndigits,j+1,*arg);
1387                         j += nrow;
1388                         if(j >= argn)
1389                                 break;
1390                         sfnputc(outfile,' ',fldsize-strlen(*arg));
1391                 }
1392                 sfputc(outfile,'\n');
1393         }
1394 }
1395
1396 #undef read
1397 /*
1398  * shell version of read() for user added builtins
1399  */
1400 ssize_t sh_read __PARAM__((register int fd, __V_* buff, size_t n), (fd, buff, n)) __OTORP__(register int fd; __V_* buff; size_t n;){
1401         register Sfio_t *sp;
1402         if(sp=sh.sftable[fd])
1403                 return(sfread(sp,buff,n));
1404         else
1405                 return(read(fd,buff,n));
1406 }
1407
1408 #undef write
1409 /*
1410  * shell version of write() for user added builtins
1411  */
1412 ssize_t sh_write __PARAM__((register int fd, const __V_* buff, size_t n), (fd, buff, n)) __OTORP__(register int fd; const __V_* buff; size_t n;){
1413         register Sfio_t *sp;
1414         if(sp=sh.sftable[fd])
1415                 return(sfwrite(sp,buff,n));
1416         else
1417                 return(write(fd,buff,n));
1418 }
1419
1420 #undef lseek
1421 /*
1422  * shell version of lseek() for user added builtins
1423  */
1424 off_t sh_seek __PARAM__((register int fd, off_t offset, int whence), (fd, offset, whence)) __OTORP__(register int fd; off_t offset; int whence;){
1425         register Sfio_t *sp;
1426         if(sp=sh.sftable[fd])
1427                 return(sfseek(sp,offset,whence));
1428         else
1429                 return(lseek(fd,offset,whence));
1430 }
1431
1432 #undef dup
1433 int sh_dup __PARAM__((register int old), (old)) __OTORP__(register int old;){
1434         register int fd = dup(old);
1435         if(fd>=0)
1436                 sh.fdstatus[fd] = (sh.fdstatus[old]&~IOCLEX);
1437         return(fd);
1438 }