1 /***********************************************************************
3 * This software is part of the ast package *
4 * Copyright (c) 1982-2010 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Common Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
13 * Information and Software Systems Research *
17 * David Korn <dgk@research.att.com> *
19 ***********************************************************************/
22 * read [-ACprs] [-d delim] [-u filenum] [-t timeout] [-n n] [-N n] [name...]
32 #include "variables.h"
33 #include "lexstates.h"
41 #define R_FLAG 1 /* raw mode */
42 #define S_FLAG 2 /* save in history file */
43 #define A_FLAG 4 /* read into array */
44 #define N_FLAG 8 /* fixed size read at most */
45 #define NN_FLAG 0x10 /* fixed size read exact */
46 #define V_FLAG 0x20 /* use default value */
47 #define C_FLAG 0x40 /* read into compound variable */
48 #define D_FLAG 8 /* must be number of bits for all flags */
60 int b_read(int argc
,char *argv
[], void *extra
)
64 register int r
, flags
=0, fd
=0;
65 register Shell_t
*shp
= ((Shbltin_t
*)extra
)->shp
;
66 long timeout
= 1000*shp
->st
.tmout
;
67 int save_prompt
, fixargs
=((Shbltin_t
*)extra
)->invariant
;
69 static char default_prompt
[3] = {ESC
,ESC
};
70 rp
= (struct read_save
*)(((Shbltin_t
*)extra
)->data
);
80 timeout
= rp
->timeout
;
87 while((r
= optget(argv
,sh_optread
))) switch(r
)
96 sec
= sh_strnum(opt_info
.arg
, (char**)0,1);
97 timeout
= sec
? 1000*sec
: 1;
100 if(opt_info
.arg
&& *opt_info
.arg
!='\n')
102 char *cp
= opt_info
.arg
;
103 flags
&= ~((1<<D_FLAG
)-1);
104 flags
|= (mbchar(cp
)<< D_FLAG
);
108 if((fd
= shp
->cpipe
[0])<=0)
109 errormsg(SH_DICT
,ERROR_exit(1),e_query
);
112 flags
&= ((1<<D_FLAG
)-1);
113 flags
|= (r
=='n'?N_FLAG
:NN_FLAG
);
114 r
= (int)opt_info
.num
;
115 if((unsigned)r
> (1<<((8*sizeof(int))-D_FLAG
))-1)
116 errormsg(SH_DICT
,ERROR_exit(1),e_overlimit
,opt_info
.name
);
117 flags
|= (r
<< D_FLAG
);
123 /* save in history file */
127 fd
= (int)opt_info
.num
;
135 errormsg(SH_DICT
,2, "%s", opt_info
.arg
);
138 errormsg(SH_DICT
,ERROR_usage(2), "%s", opt_info
.arg
);
141 argv
+= opt_info
.index
;
142 if(error_info
.errors
)
143 errormsg(SH_DICT
,ERROR_usage(2), "%s", optusage((char*)0));
144 if(!((r
=shp
->fdstatus
[fd
])&IOREAD
) || !(r
&(IOSEEK
|IONOSEEK
)))
145 r
= sh_iocheckfd(shp
,fd
);
146 if(fd
<0 || !(r
&IOREAD
))
147 errormsg(SH_DICT
,ERROR_system(1),e_file
+4);
148 /* look for prompt */
149 if((name
= *argv
) && (name
=strchr(name
,'?')) && (r
&IOTTY
))
153 if(argc
==fixargs
&& (rp
=newof(NIL(struct read_save
*),struct read_save
,1,0)))
155 ((Shbltin_t
*)extra
)->data
= (void*)rp
;
158 rp
->timeout
= timeout
;
164 shp
->prompt
= default_prompt
;
165 if(r
&& (shp
->prompt
=(char*)sfreserve(sfstderr
,r
,SF_LOCKR
)))
167 memcpy(shp
->prompt
,name
,r
);
168 sfwrite(sfstderr
,shp
->prompt
,r
-1);
171 save_prompt
= shp
->nextprompt
;
173 r
=sh_readline(shp
,argv
,fd
,flags
,timeout
);
174 shp
->nextprompt
= save_prompt
;
175 if(r
==0 && (r
=(sfeof(shp
->sftable
[fd
])||sferror(shp
->sftable
[fd
]))))
177 if(fd
== shp
->cpipe
[0])
179 sh_pclose(shp
->cpipe
);
183 sfclrerr(shp
->sftable
[fd
]);
188 * here for read timeout
190 static void timedout(void *handle
)
192 sfclrlock((Sfio_t
*)handle
);
197 * This is the code to read a line and to split it into tokens
198 * <names> is an array of variable names
199 * <fd> is the file descriptor
200 * <flags> is union of -A, -r, -s, and contains delimiter if not '\n'
201 * <timeout> is number of milli-seconds until timeout
204 int sh_readline(register Shell_t
*shp
,char **names
, int fd
, int flags
,long timeout
)
207 register unsigned char *cp
;
208 register Namval_t
*np
;
209 register char *name
, *val
;
210 register Sfio_t
*iop
;
213 unsigned char *cpmax
;
217 volatile char was_write
= 0;
218 volatile char was_share
= 1;
220 long array_index
= 0;
227 if(!(iop
=shp
->sftable
[fd
]) && !(iop
=sh_iostream(shp
,fd
)))
229 sh_stats(STAT_READS
);
230 if(names
&& (name
= *names
))
233 if(val
= strchr(name
,'?'))
235 np
= nv_open(name
,shp
->var_tree
,NV_NOASSIGN
|NV_VARNAME
);
236 if(np
&& nv_isarray(np
) && (mp
=nv_opensub(np
)))
238 if((flags
&V_FLAG
) && shp
->ed_context
)
239 ((struct edit
*)shp
->ed_context
)->e_default
= np
;
245 nv_putsub(np
,NIL(char*),0L);
247 else if(flags
&C_FLAG
)
261 if(dtvnext(shp
->var_tree
) || shp
->namespace)
262 np
= nv_open(nv_name(REPLYNOD
),shp
->var_tree
,0);
266 if(flags
>>D_FLAG
) /* delimiter not new-line or fixed size read */
268 if(flags
&(N_FLAG
|NN_FLAG
))
269 size
= ((unsigned)flags
)>>D_FLAG
;
271 delim
= ((unsigned)flags
)>>D_FLAG
;
272 if(shp
->fdstatus
[fd
]&IOTTY
)
275 binary
= nv_isattr(np
,NV_BINARY
);
276 if(!binary
&& !(flags
&(N_FLAG
|NN_FLAG
)))
279 /* set up state table based on IFS */
280 ifs
= nv_getval(mp
=sh_scoped(shp
,IFSNOD
));
281 if((flags
&R_FLAG
) && shp
->ifstable
['\\']==S_ESC
)
282 shp
->ifstable
['\\'] = 0;
283 else if(!(flags
&R_FLAG
) && shp
->ifstable
['\\']==0)
284 shp
->ifstable
['\\'] = S_ESC
;
285 shp
->ifstable
[delim
] = S_NL
;
288 shp
->ifstable
['\n'] = 0;
289 nv_putval(mp
, ifs
, NV_RDONLY
);
291 shp
->ifstable
[0] = S_EOF
;
294 for(nfp
=np
->nvfun
; nfp
; nfp
= nfp
->next
)
296 if(nfp
->disc
&& nfp
->disc
->readf
)
298 if((c
=(*nfp
->disc
->readf
)(np
,iop
,delim
,nfp
))>=0)
302 if(binary
&& !(flags
&(N_FLAG
|NN_FLAG
)))
307 was_write
= (sfset(iop
,SF_WRITE
,0)&SF_WRITE
)!=0;
309 was_share
= (sfset(iop
,SF_SHARE
,1)&SF_SHARE
)!=0;
310 if(timeout
|| (shp
->fdstatus
[fd
]&(IOTTY
|IONOSEEK
)))
312 sh_pushcontext(&buff
,1);
313 jmpval
= sigsetjmp(buff
.buff
,0);
317 timeslot
= (void*)sh_timeradd(timeout
,0,timedout
,(void*)iop
);
319 if(flags
&(N_FLAG
|NN_FLAG
))
321 char buf
[256],*var
=buf
,*cur
,*end
,*up
,*v
;
322 /* reserved buffer */
323 if((c
=size
)>=sizeof(buf
))
325 if(!(var
= (char*)malloc(c
+1)))
330 end
= var
+ sizeof(buf
) - 1;
332 if((sfset(iop
,SF_SHARE
,1)&SF_SHARE
) && fd
!=0)
336 cp
= sfreserve(iop
,0,0);
346 cp
= sfreserve(iop
,c
,SF_LOCKR
);
350 else if(flags
&NN_FLAG
)
353 m
= (cp
= sfreserve(iop
,c
,0)) ? sfvalue(iop
) : 0;
359 m
= (cp
= sfreserve(iop
,c
,SF_LOCKR
)) ? sfvalue(iop
) : 0;
361 if(m
>0 && (flags
&N_FLAG
) && !binary
&& (v
=memchr(cp
,'\n',m
)))
372 ssize_t cx
= cur
- var
, ux
= up
- var
;
373 m
= (end
- var
) + (c
- (end
- cur
));
376 v
= (char*)malloc(m
+1);
377 var
= memcpy(v
, var
, cur
- var
);
380 var
= newof(var
, char, m
, 1);
385 memcpy((void*)cur
,cp
,c
);
390 if(!binary
&& mbwide())
398 while (up
< cur
&& (z
= mbsize(up
)) > 0)
403 if((size
-= x
) > 0 && (up
>= cur
|| z
< 0) && ((flags
& NN_FLAG
) || z
< 0 || m
> c
))
409 if(!binary
&& mbwide() && (up
== var
|| (flags
& NN_FLAG
) && size
))
413 if(c
>=size
|| (flags
&N_FLAG
) || m
==0)
424 if(binary
&& !((size
=nv_size(np
)) && nv_isarray(np
) && c
!=size
))
426 if((c
==size
) && np
->nvalue
.cp
&& !nv_isarray(np
))
427 memcpy((char*)np
->nvalue
.cp
,var
,c
);
432 var
= memdup(var
,c
+1);
433 nv_putval(np
,var
,NV_RAW
);
435 if(!nv_isattr(np
,NV_IMPORT
|NV_EXPORT
) && (mp
=(Namval_t
*)np
->nvenv
))
447 else if(cp
= (unsigned char*)sfgetr(iop
,delim
,0))
449 else if(cp
= (unsigned char*)sfgetr(iop
,delim
,-1))
453 if((flags
&S_FLAG
) && !shp
->hist_ptr
)
455 sh_histinit((void*)shp
);
463 if(delim
=='\n' && c
>=2 && cpmax
[-2]=='\r')
465 #endif /* SHOPT_CRNL */
466 if(*(cpmax
-1) != delim
)
469 sfwrite(shp
->hist_ptr
->histfp
,(char*)cp
,c
);
470 c
= shp
->ifstable
[*cp
++];
472 if(!name
&& (flags
&R_FLAG
)) /* special case single argument */
474 /* skip over leading blanks */
476 c
= shp
->ifstable
[*cp
++];
477 /* strip trailing delimiters */
478 if(cpmax
[-1] == '\n')
482 while((c
=shp
->ifstable
[*--cpmax
])==S_DELIM
|| c
==S_SPACE
);
487 if(nv_isattr(np
, NV_RDONLY
))
489 errormsg(SH_DICT
,ERROR_warn(0),e_readonly
, nv_name(np
));
493 nv_putval(np
,(char*)cp
-1,0);
496 #endif /* !SHOPT_MULTIBYTE */
502 /* val==0 at the start of a field */
513 if(sh_strchr(ifs
,(char*)cp
-1)>=0)
515 c
= mbsize((char*)cp
-1);
525 #endif /*SHOPT_MULTIBYTE */
527 /* process escape character */
528 if((c
= shp
->ifstable
[*cp
++]) == S_NL
)
542 /* check for end of buffer */
554 /* eliminate null bytes */
555 c
= shp
->ifstable
[*cp
++];
556 if(!name
&& val
&& (c
==S_SPACE
||c
==S_DELIM
||c
==S_MBYTE
))
563 if(cp
= (unsigned char*)sfgetr(iop
,delim
,0))
565 else if(cp
=(unsigned char*)sfgetr(iop
,delim
,-1))
570 sfwrite(shp
->hist_ptr
->histfp
,(char*)cp
,c
);
572 c
= shp
->ifstable
[*cp
++];
574 if(!name
&& (c
==S_SPACE
|| c
==S_DELIM
|| c
==S_MBYTE
))
583 /* skip over blanks */
584 while((c
=shp
->ifstable
[*cp
++])==S_SPACE
);
590 if(sh_strchr(ifs
,(char*)cp
-1)>=0)
592 if((c
= mbsize((char*)cp
-1))>1)
599 #endif /* SHOPT_MULTIBYTE */
609 /* skip over trailing blanks */
610 while((c
=shp
->ifstable
[*cp
++])==S_SPACE
);
616 if(val
==0 || was_escape
)
621 /* skip over word characters */
625 while((c
=shp
->ifstable
[*cp
++])==0)
630 if(name
|| c
==S_NL
|| c
==S_ESC
|| c
==S_EOF
|| c
==S_MBYTE
)
636 del
= (unsigned char*)"";
641 /* assign value and advance to next variable */
652 /* strip off trailing space delimiters */
653 register unsigned char *vp
= (unsigned char*)val
+ strlen(val
);
654 while(shp
->ifstable
[*--vp
]==S_SPACE
);
657 if(vp
==(unsigned char*)val
)
660 while(shp
->ifstable
[*--vp
]==S_SPACE
);
664 if(nv_isattr(np
, NV_RDONLY
))
666 errormsg(SH_DICT
,ERROR_warn(0),e_readonly
, nv_name(np
));
680 nv_putsub(np
, NIL(char*), array_index
++);
687 if(sh_isoption(SH_ALLEXPORT
)&&!strchr(nv_name(np
),'.') && !nv_isattr(np
,NV_EXPORT
))
689 nv_onattr(np
,NV_EXPORT
);
690 sh_envput(sh
.env
,np
);
695 np
= nv_open(name
,shp
->var_tree
,NV_NOASSIGN
|NV_VARNAME
);
704 if(nv_isattr(np
, NV_RDONLY
))
706 errormsg(SH_DICT
,ERROR_warn(0),e_readonly
, nv_name(np
));
710 nv_putval(np
, "", 0);
714 if(timeout
|| (shp
->fdstatus
[fd
]&(IOTTY
|IONOSEEK
)))
715 sh_popcontext(&buff
);
717 sfset(iop
,SF_WRITE
,1);
719 sfset(iop
,SF_SHARE
,0);
721 if((flags
>>D_FLAG
) && (shp
->fdstatus
[fd
]&IOTTY
))
724 hist_flush(shp
->hist_ptr
);
726 siglongjmp(*shp
->jmplist
,jmpval
);