1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1982-2007 AT&T Intellectual Property * 5 * and is licensed under the * 6 * Common Public License, Version 1.0 * 7 * by AT&T Intellectual Property * 8 * * 9 * A copy of the License is available at * 10 * http://www.opensource.org/licenses/cpl1.0.txt * 11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12 * * 13 * Information and Software Systems Research * 14 * AT&T Research * 15 * Florham Park NJ * 16 * * 17 * David Korn <dgk@research.att.com> * 18 * * 19 ***********************************************************************/ 20 #pragma prototyped 21 22 #include <shell.h> 23 #include <stdio.h> 24 #include <stdbool.h> 25 #include <option.h> 26 #include <stk.h> 27 #include <tm.h> 28 #include "name.h" 29 #undef nv_isnull 30 #ifndef SH_DICT 31 # define SH_DICT "libshell" 32 #endif 33 #include <poll.h> 34 35 #define sh_contexttoshb(context) ((Shbltin_t*)(context)) 36 #define sh_contexttoshell(context) ((context)?(sh_contexttoshb(context)->shp):(NULL)) 37 38 /* 39 * time formatting related 40 */ 41 struct dctime 42 { 43 Namfun_t fun; 44 Namval_t *format; 45 char buff[256]; /* Must be large enougth for |tmfmt()| */ 46 }; 47 48 static char *get_time(Namval_t* np, Namfun_t* nfp) 49 { 50 struct dctime *dp = (struct dctime*)nfp; 51 time_t t = nv_getn(np,nfp); 52 char *format = nv_getval(dp->format); 53 tmfmt(dp->buff,sizeof(dp->buff),format,(time_t*)0); 54 return(dp->buff); 55 } 56 57 static void put_time(Namval_t* np, const char* val, int flag, Namfun_t* nfp) 58 { 59 struct dctime *dp = (struct dctime*)nfp; 60 char *last; 61 if(val) 62 { 63 int32_t t; 64 if(flag&NV_INTEGER) 65 { 66 if(flag&NV_LONG) 67 t = *(Sfdouble_t*)val; 68 else 69 t = *(double*)val; 70 } 71 else 72 { 73 t = tmdate(val, &last, (time_t*)0); 74 if(*last) 75 errormsg(SH_DICT, ERROR_exit(1),"%s: invalid date/time string", val); 76 } 77 nv_putv(np, (char*)&t,NV_INTEGER, nfp); 78 } 79 else 80 { 81 nv_unset(dp->format); 82 free((void*)dp->format); 83 nv_putv(np, val, flag, nfp); 84 } 85 } 86 87 static Namval_t *create_time(Namval_t *np, const char *name, int flags, Namfun_t *nfp) 88 { 89 struct dctime *dp = (struct dctime*)nfp; 90 if(strcmp(name, "format")) 91 return((Namval_t*)0); 92 return(dp->format); 93 } 94 95 static const Namdisc_t timedisc = 96 { 97 sizeof(struct dctime), 98 put_time, 99 get_time, 100 0, 101 0, 102 create_time, 103 }; 104 105 106 static Namval_t *make_time(Namval_t* np) 107 { 108 int offset = stktell(stkstd); 109 char *name = nv_name(np); 110 struct dctime *dp = newof(NULL,struct dctime,1,0); 111 if(!dp) 112 return((Namval_t*)0); 113 sfprintf(stkstd,"%s.format\0",name); 114 sfputc(stkstd,0); 115 dp->format = nv_search(stkptr(stkstd,offset),sh.var_tree,NV_ADD); 116 dp->fun.disc = &timedisc; 117 nv_stack(np,&dp->fun); 118 return(np); 119 } 120 121 /* 122 * mode formatting related 123 */ 124 static char *get_mode(Namval_t* np, Namfun_t* nfp) 125 { 126 mode_t mode = nv_getn(np,nfp); 127 return(fmtperm(mode)); 128 } 129 130 static void put_mode(Namval_t* np, const char* val, int flag, Namfun_t* nfp) 131 { 132 if(val) 133 { 134 int32_t mode; 135 char *last; 136 if(flag&NV_INTEGER) 137 { 138 if(flag&NV_LONG) 139 mode = *(Sfdouble_t*)val; 140 else 141 mode = *(double*)val; 142 } 143 else 144 { 145 mode = strperm(val, &last,0); 146 if(*last) 147 errormsg(SH_DICT, ERROR_exit(1),"%s: invalid mode string", val); 148 } 149 nv_putv(np,(char*)&mode,NV_INTEGER,nfp); 150 } 151 else 152 nv_putv(np,val,flag,nfp); 153 } 154 155 static const Namdisc_t modedisc = 156 { 157 0, 158 put_mode, 159 get_mode, 160 }; 161 162 static Namval_t *make_mode(Namval_t* np) 163 { 164 char *name = nv_name(np); 165 Namfun_t *nfp = newof(NULL,Namfun_t,1,0); 166 if(!nfp) 167 return((Namval_t*)0); 168 nfp->disc = &modedisc; 169 nv_stack(np,nfp); 170 return(np); 171 } 172 173 /* 174 * field related typese and functions 175 */ 176 typedef struct _field_ 177 { 178 char *name; /* field name */ 179 int flags; /* flags */ 180 short offset; /* offset of field into data */ 181 short size; /* size of field */ 182 Namval_t *(*make)(Namval_t*); /* discipline constructor */ 183 } Shfield_t; 184 185 /* 186 * lookup field in field table 187 */ 188 static Shfield_t *sh_findfield(Shfield_t *ftable, int nelem, const char *name) 189 { 190 Shfield_t *fp = ftable; 191 register int i,n; 192 register const char *cp; 193 for(cp=name; *cp; cp++) 194 { 195 if(*cp=='.') 196 break; 197 } 198 n = cp-name; 199 for(i=0; i < nelem; i++,fp++) 200 { 201 if(memcmp(fp->name,name,n)==0 && fp->name[n]==0) 202 return(fp); 203 } 204 return(0); 205 } 206 207 /* 208 * class types and functions 209 */ 210 211 typedef struct _class_ 212 { 213 int nelem; /* number of elements */ 214 int dsize; /* size for data structure */ 215 Shfield_t *fields; /* field description table */ 216 } Shclass_t; 217 218 struct dcclass 219 { 220 Namfun_t fun; 221 Shclass_t sclass; 222 }; 223 224 static Namval_t *sh_newnode(register Shfield_t *fp, Namval_t *np) 225 { 226 char *val = np->nvalue + fp->offset; 227 char *name = nv_name(np); 228 register Namval_t *nq; 229 int offset = stktell(stkstd); 230 sfprintf(stkstd,"%s.%s\0",name,fp->name); 231 sfputc(stkstd,0); 232 nq = nv_search(stkptr(stkstd,offset),sh.var_tree,NV_ADD); 233 if(fp->size<0) 234 val = *(char**)val; 235 nv_putval(nq,val,fp->flags|NV_NOFREE); 236 if(fp->make) 237 (*fp->make)(nq); 238 return(nq); 239 } 240 241 static Namval_t *fieldcreate(Namval_t *np, const char *name, int flags, Namfun_t *nfp) 242 { 243 struct dcclass *dcp = (struct dcclass*)nfp; 244 Shclass_t *sp = &dcp->sclass; 245 Shfield_t *fp = sh_findfield(sp->fields,sp->nelem,name); 246 Namval_t *nq,**nodes = (Namval_t**)(dcp+1); 247 int n = fp-sp->fields; 248 int len = strlen(fp->name); 249 void *data = (void*)np->nvalue; 250 if(!(nq=nodes[n])) 251 { 252 nodes[n] = nq = sh_newnode(fp,np); 253 nfp->last = ""; 254 } 255 if(name[len]==0) 256 return(nq); 257 return(nq); 258 } 259 260 static void genvalue(Sfio_t *out, Shclass_t *sp, int indent, Namval_t *npar) 261 { 262 Shfield_t *fp = sp->fields; 263 Namval_t *np, **nodes= (Namval_t**)(sp+1); 264 register int i,isarray; 265 if(out) 266 { 267 sfwrite(out,"(\n",2); 268 indent++; 269 } 270 for(i=0; i < sp->nelem; i++,fp++) 271 { 272 #if 0 273 /* handle recursive case */ 274 #endif 275 if(!(np=nodes[i]) && out) 276 np = sh_newnode(fp,npar); 277 if(np) 278 { 279 isarray=0; 280 if(nv_isattr(np,NV_ARRAY)) 281 { 282 isarray=1; 283 if(array_elem(nv_arrayptr(np))==0) 284 isarray=2; 285 else 286 nv_putsub(np,(char*)0,ARRAY_SCAN); 287 } 288 sfnputc(out,'\t',indent); 289 sfputr(out,fp->name,(isarray==2?'\n':'=')); 290 if(isarray) 291 { 292 if(isarray==2) 293 continue; 294 sfwrite(out,"(\n",2); 295 sfnputc(out,'\t',++indent); 296 } 297 while(1) 298 { 299 char *fmtq; 300 if(isarray) 301 { 302 sfprintf(out,"[%s]",sh_fmtq(nv_getsub(np))); 303 sfputc(out,'='); 304 } 305 if(!(fmtq=nv_getval(np)) || !(fmtq=sh_fmtq(fmtq))) 306 fmtq = ""; 307 sfputr(out,fmtq,'\n'); 308 if(!nv_nextsub(np)) 309 break; 310 sfnputc(out,'\t',indent); 311 } 312 if(isarray) 313 { 314 sfnputc(out,'\t',--indent); 315 sfwrite(out,")\n",2); 316 } 317 } 318 } 319 if(out) 320 { 321 if(indent>1) 322 sfnputc(out,'\t',indent-1); 323 sfputc(out,')'); 324 } 325 } 326 327 static char *walk_class(register Namval_t *np, int dlete, struct dcclass *dcp) 328 { 329 static Sfio_t *out; 330 Sfio_t *outfile; 331 int savtop = stktell(stkstd); 332 char *savptr = stkfreeze(stkstd,0); 333 if(dlete) 334 outfile = 0; 335 else if(!(outfile=out)) 336 outfile = out = sfnew((Sfio_t*)0,(char*)0,-1,-1,SF_WRITE|SF_STRING); 337 else 338 sfseek(outfile,0L,SEEK_SET); 339 genvalue(outfile,&dcp->sclass,0,np); 340 stkset(stkstd,savptr,savtop); 341 if(!outfile) 342 return((char*)0); 343 sfputc(out,0); 344 return((char*)out->_data); 345 } 346 347 static char *get_classval(Namval_t* np, Namfun_t* nfp) 348 { 349 return(walk_class(np,0,(struct dcclass *)nfp)); 350 } 351 352 static void put_classval(Namval_t* np, const char* val, int flag, Namfun_t* nfp) 353 { 354 walk_class(np,1,(struct dcclass *)nfp); 355 if(nfp = nv_stack(np,(Namfun_t*)0)) 356 { 357 free((void*)nfp); 358 if(np->nvalue && !nv_isattr(np,NV_NOFREE)) 359 free((void*)np->nvalue); 360 } 361 if(val) 362 nv_putval(np,val,flag); 363 } 364 365 static const Namdisc_t classdisc = 366 { 367 sizeof(struct dcclass), 368 put_classval, 369 get_classval, 370 0, 371 0, 372 fieldcreate 373 }; 374 375 static int mkclass(Namval_t *np, Shclass_t *sp) 376 { 377 struct dcclass *tcp = newof(NULL,struct dcclass,1,sp->nelem*sizeof(Namval_t*)); 378 if(!tcp) 379 return(0); 380 memset((void*)(tcp+1),0,sp->nelem*sizeof(Namval_t*)); 381 tcp->fun.disc = &classdisc; 382 tcp->sclass = *sp; 383 np->nvalue = (char*)calloc(sp->dsize,1); 384 nv_stack(np,&tcp->fun); 385 return(1); 386 } 387 388 /* 389 * ====================from here down is file class specific 390 */ 391 static struct stat *Sp; 392 393 struct filedata 394 { 395 struct stat statb; 396 int fd; 397 char *name; 398 }; 399 400 static Shfield_t filefield[] = 401 { 402 { "atime", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_atime), sizeof(Sp->st_atime), make_time}, 403 { "ctime", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_ctime), sizeof(Sp->st_ctime), make_time}, 404 { "dev", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_dev),sizeof(Sp->st_dev)}, 405 { "fd", NV_INTEGER|NV_RDONLY, offsetof(struct filedata,fd), sizeof(int)}, 406 { "gid", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_gid), sizeof(Sp->st_gid)}, 407 { "ino", NV_LONG|NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_ino), sizeof(Sp->st_ino)}, 408 { "mode", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_mode), sizeof(Sp->st_mode), make_mode}, 409 { "mtime", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_mtime), sizeof(Sp->st_mtime), make_time}, 410 { "name", NV_RDONLY, offsetof(struct filedata,name), -1 }, 411 { "nlink", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_nlink), sizeof(Sp->st_nlink)}, 412 { "size", NV_LONG|NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_size), sizeof(Sp->st_size)}, 413 { "uid", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_uid), sizeof(Sp->st_uid)} 414 }; 415 416 static Shclass_t Fileclass = 417 { 418 sizeof(filefield)/sizeof(*filefield), 419 sizeof(struct filedata), 420 filefield 421 }; 422 423 424 #define letterbit(bit) (1<<((bit)-'a')) 425 426 static const char sh_optopen[] = 427 "[-?\n@(#)$Id: open (AT&T Labs Research) 2007-05-07 $\n]" 428 "[-author?David Korn <dgk@research.att.com>]" 429 "[-author?Roland Mainz <roland.mainz@nrubsig.org>]" 430 "[-license?http://www.opensource.org/licenses/cpl1.0.txt]" 431 "[+NAME? open - create a shell variable correspnding to a file]" 432 "[+DESCRIPTION?\bopen\b creates the compound variable \avar\a correspinding " 433 "to the file given by the pathname \afile\a. The elements of \avar\a " 434 "are the names of elements in the \astat\a structure with the \bst_\b " 435 "prefix removed.]" 436 "[+?\afile\a is opened (based on \b-r\b and/or \b-w\b) and the variable " 437 "\avar\a\b.fd\b is the file descriptor.]" 438 "[a:append?Open for append.]" 439 "[b:binary?Open in binary mode" 440 #ifndef O_BINARY 441 " (not supported/ignored on this platform)" 442 #endif 443 ".]" 444 "[t:text?Open in text mode" 445 #ifndef O_TEXT 446 " (not supported/ignored on this platform)" 447 #endif 448 ".]" 449 "[c:create?Open for create.]" 450 "[i:inherit?Open without the close-on-exec bit set.]" 451 "[I:noinherit?Open with the close-on-exec bit set.]" 452 "[r:read?Open with read access.]" 453 "[w:write?Open with write access.]" 454 "[m:mode]:[mode:=rwrwrw?Open with access mode \amode\a.]" 455 "[x:exclusive?Open exclusive.]" 456 457 "[N:nofollow?If the path names a symbolic link, open fails with ELOOP " 458 #ifndef O_NOFOLLOW 459 " (not supported/ignored on this platform)" 460 #endif 461 ".]" 462 "[S:sync?Write I/O operations on the file descriptor complete as " 463 "defined by synchronized I/O file integrity completion" 464 #ifndef O_SYNC 465 " (not supported/ignored on this platform)" 466 #endif 467 ".]" 468 "[T:trunc?If the file exists and is a regular file, and the file " 469 "is successfully opened read/write or write-only, its length is " 470 "truncated to 0 and the mode and owner are unchanged. It " 471 "has no effect on FIFO special files or terminal device " 472 "files. Its effect on other file types is " 473 "implementation-dependent. The result of using -T " 474 "with read-only files is undefined" 475 #ifndef O_TRUNC 476 " (not supported/ignored on this platform)" 477 #endif 478 ".]" 479 "\n" 480 "\nvar file\n" 481 "\n" 482 "[+EXIT STATUS?]{" 483 "[+0?Success.]" 484 "[+>0?An error occurred.]" 485 "}" 486 "[+SEE ALSO?\btmpfile\b(1),\bdup\b(1),\bclose\b(1),\bstat\b(1),\bpoll\b(1),\bstat\b(2)]" 487 ; 488 489 490 extern int b_open(int argc, char *argv[], void *extra) 491 { 492 register Namval_t *np; 493 register int n,oflag=0; 494 Shell_t *shp = sh_contexttoshell(extra); 495 struct filedata *fdp; 496 mode_t mode = 0666; 497 long flags = 0; 498 int fd = -1; 499 char *arg; 500 501 while (n = optget(argv, sh_optopen)) switch (n) 502 { 503 case 'r': 504 case 'w': 505 case 'i': 506 flags |= letterbit(n); 507 break; 508 case 'I': 509 flags &= ~(letterbit('i')); 510 break; 511 case 'b': 512 #ifdef O_BINARY 513 oflag |= O_BINARY; 514 #endif 515 break; 516 case 't': 517 #ifdef O_TEXT 518 oflag |= O_TEXT; 519 #endif 520 break; 521 case 'N': 522 #ifdef O_NOFOLLOW 523 oflag |= O_NOFOLLOW; 524 #endif 525 break; 526 case 'T': 527 #ifdef O_TRUNC 528 oflag |= O_TRUNC; 529 #endif 530 break; 531 case 'x': 532 oflag |= O_EXCL; 533 break; 534 case 'c': 535 oflag |= O_CREAT; 536 break; 537 case 'a': 538 oflag |= O_APPEND; 539 break; 540 case 'S': 541 #ifdef O_SYNC 542 oflag |= O_SYNC; 543 #endif 544 break; 545 case 'm': 546 mode = strperm(arg = opt_info.arg, &opt_info.arg, mode); 547 if (*opt_info.arg) 548 errormsg(SH_DICT, ERROR_system(1), "%s: invalid mode", arg); 549 break; 550 case ':': 551 errormsg(SH_DICT, 2, "%s", opt_info.arg); 552 break; 553 case '?': 554 errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg); 555 break; 556 } 557 argc -= opt_info.index; 558 argv += opt_info.index; 559 if(argc!=2 || !(flags&(letterbit('r')|letterbit('w')))) 560 errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0)); 561 562 if(flags&letterbit('r')) 563 { 564 if(flags&letterbit('w')) 565 oflag |= O_RDWR; 566 else 567 oflag |= O_RDONLY; 568 } 569 else if(flags&letterbit('w')) 570 oflag |= O_WRONLY; 571 572 fd = sh_open(argv[1], oflag, mode); 573 if(fd<0) 574 errormsg(SH_DICT, ERROR_system(1), "%s: open failed", argv[1]); 575 576 if(!(flags&letterbit('i'))) 577 fcntl(fd, F_SETFL, 0); 578 579 np = nv_open(argv[0], shp->var_tree, NV_ARRAY|NV_VARNAME|NV_NOASSIGN); 580 if(!nv_isnull(np)) 581 nv_unset(np); 582 mkclass(np, &Fileclass); 583 fdp = (struct filedata*)np->nvalue; 584 fstat(fd, &fdp->statb); 585 fdp->fd = fd; 586 fdp->name = strdup(argv[1]); 587 return(0); 588 } 589 590 static const char sh_optclose[] = 591 "[-?\n@(#)$Id: close (AT&T Labs Research) 2007-04-21 $\n]" 592 "[-author?Roland Mainz <roland.mainz@nrubsig.org>]" 593 "[-license?http://www.opensource.org/licenses/cpl1.0.txt]" 594 "[+NAME? close - close a file descriptor]" 595 "[+DESCRIPTION?\bclose\b closes the file descriptor specified by fd.]" 596 "\n" 597 "\nfd\n" 598 "\n" 599 "[+EXIT STATUS?]{" 600 "[+0?Success.]" 601 "[+>0?An error occurred.]" 602 "}" 603 "[+SEE ALSO?\bopen\b(1),\bdup\b(1),\btmpfile\b(1),\bpoll\b(1),\bstat\b(1)]" 604 ; 605 606 extern int b_close(int argc, char *argv[], void *extra) 607 { 608 register int n=0; 609 int fd = -1; 610 611 while (n = optget(argv, sh_optclose)) switch (n) 612 { 613 case ':': 614 errormsg(SH_DICT, 2, "%s", opt_info.arg); 615 break; 616 case '?': 617 errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg); 618 break; 619 } 620 argc -= opt_info.index; 621 argv += opt_info.index; 622 if(argc!=1) 623 errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0)); 624 625 errno = 0; 626 fd = strtol(argv[0], (char **)NULL, 0); 627 if (errno != 0 || fd < 0) 628 errormsg(SH_DICT, ERROR_system(1), "%s: invalid descriptor", argv[0]); 629 630 n = sh_close(fd); 631 632 if (n < 0) 633 errormsg(SH_DICT, ERROR_system(1), "%s: close error", argv[0]); 634 635 return(n==0?0:1); 636 } 637 638 639 static const char sh_opttmpfile[] = 640 "[-?\n@(#)$Id: tmpfile (AT&T Labs Research) 2007-05-07 $\n]" 641 "[-author?Roland Mainz <roland.mainz@nrubsig.org>]" 642 "[-license?http://www.opensource.org/licenses/cpl1.0.txt]" 643 "[+NAME? tmpfile - create a shell variable correspnding to a temporary file]" 644 "[+DESCRIPTION?\btmpfile\b creates the compound variable \avar\a correspinding " 645 "to a temporary file. The elements of \avar\a " 646 "are the names of elements in the \astat\a structure with the \bst_\b " 647 "prefix removed.]" 648 "[i:inherit?Open without the close-on-exec bit set.]" 649 "[I:noinherit?Open with the close-on-exec bit set.]" 650 "\n" 651 "\nvar\n" 652 "\n" 653 "[+EXIT STATUS?]{" 654 "[+0?Success.]" 655 "[+>0?An error occurred.]" 656 "}" 657 "[+SEE ALSO?\bopen\b(1),\bdup\b(1),\bclose\b(1),\bstat\b(1),\bstat\b(2)]" 658 ; 659 660 661 extern int b_tmpfile(int argc, char *argv[], void *extra) 662 { 663 register Namval_t *np; 664 register int n; 665 Shell_t *shp = sh_contexttoshell(extra); 666 struct filedata *fdp; 667 bool inherit = false; 668 FILE *file = NULL; 669 int ffd, fd = -1; 670 while (n = optget(argv, sh_opttmpfile)) switch (n) 671 { 672 case 'i': 673 inherit = true; 674 break; 675 case 'I': 676 inherit = false; 677 break; 678 case ':': 679 errormsg(SH_DICT, 2, "%s", opt_info.arg); 680 break; 681 case '?': 682 errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg); 683 break; 684 } 685 argc -= opt_info.index; 686 argv += opt_info.index; 687 if(argc!=1) 688 errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0)); 689 690 file = tmpfile(); 691 if(!file) 692 errormsg(SH_DICT, ERROR_system(1), "%s: tmpfile failed", argv[1]); 693 ffd = fileno(file); 694 fd = sh_dup(ffd); 695 if(fd<0) 696 errormsg(SH_DICT, ERROR_system(1), "%s: tmpfile failed", argv[1]); 697 fclose(file); 698 699 if(!inherit) 700 fcntl(fd, F_SETFL, 0); 701 702 np = nv_open(argv[0], shp->var_tree, NV_ARRAY|NV_VARNAME|NV_NOASSIGN); 703 if(!nv_isnull(np)) 704 nv_unset(np); 705 mkclass(np,&Fileclass); 706 fdp = (struct filedata*)np->nvalue; 707 708 fstat(fd, &fdp->statb); 709 fdp->fd = fd; 710 fdp->name = NULL; 711 return(0); 712 } 713 714 static const char sh_optdup[] = 715 "[-?\n@(#)$Id: dup (AT&T Labs Research) 2007-05-07 $\n]" 716 "[-author?Roland Mainz <roland.mainz@nrubsig.org>]" 717 "[-license?http://www.opensource.org/licenses/cpl1.0.txt]" 718 "[+NAME? dup - duplicate an open file descriptor]" 719 "[+DESCRIPTION?The \bdup\b commands returns a new file descriptor having the " 720 "following in common with the original open file descriptor " 721 "fd: same open file (or pipe), same file pointer (that is, both file descriptors " 722 "share one file pointer) same access mode (read, write or read/write). " 723 "The file descriptor returned is the lowest one available.]" 724 "[i:inherit?Open without the close-on-exec bit set.]" 725 "[I:noinherit?Open with the close-on-exec bit set.]" 726 "\n" 727 "\nvar fd\n" 728 "\n" 729 "[+EXIT STATUS?]{" 730 "[+0?Success.]" 731 "[+>0?An error occurred.]" 732 "}" 733 "[+SEE ALSO?\bopen\b(1),\btmpfile\b(1),\bclose\b(1),\bpoll\b(1),\bstat\b(1)]" 734 ; 735 736 737 extern int b_dup(int argc, char *argv[], void *extra) 738 { 739 register Namval_t *np; 740 register int n; 741 Shell_t *shp = sh_contexttoshell(extra); 742 struct filedata *fdp; 743 bool inherit = false; 744 int ffd, fd = -1; 745 while (n = optget(argv, sh_optdup)) switch (n) 746 { 747 case 'i': 748 inherit = true; 749 break; 750 case 'I': 751 inherit = false; 752 break; 753 case ':': 754 errormsg(SH_DICT, 2, "%s", opt_info.arg); 755 break; 756 case '?': 757 errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg); 758 break; 759 } 760 argc -= opt_info.index; 761 argv += opt_info.index; 762 if(argc!=2) 763 errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0)); 764 765 errno = 0; 766 ffd = strtol(argv[1], (char **)NULL, 0); 767 if (errno != 0 || ffd < 0) 768 errormsg(SH_DICT, ERROR_system(1), "%s: invalid fd", argv[1]); 769 770 fd = sh_dup(ffd); 771 if(fd<0) 772 errormsg(SH_DICT, ERROR_system(1), "%s: dup failed", argv[1]); 773 774 if(!inherit) 775 fcntl(fd,F_SETFL,0); 776 777 np = nv_open(argv[0],shp->var_tree,NV_ARRAY|NV_VARNAME|NV_NOASSIGN); 778 if(!nv_isnull(np)) 779 nv_unset(np); 780 mkclass(np, &Fileclass); 781 fdp = (struct filedata*)np->nvalue; 782 783 fstat(fd, &fdp->statb); 784 fdp->fd = fd; 785 fdp->name = NULL; 786 return(0); 787 } 788 789 static const char sh_optstat[] = 790 "[-?\n@(#)$Id: stat (AT&T Labs Research) 2007-05-07 $\n]" 791 "[-author?David Korn <dgk@research.att.com>]" 792 "[-author?Roland Mainz <roland.mainz@nrubsig.org>]" 793 "[-license?http://www.opensource.org/licenses/cpl1.0.txt]" 794 "[+NAME? stat - get file status]" 795 "[+DESCRIPTION?\bstat\b creates the compound variable \avar\a correspinding " 796 "to the file given by the pathname \afile\a. The elements of \avar\a " 797 "are the names of elements in the \astat\a structure with the \bst_\b " 798 "prefix removed.]" 799 "[l:lstat?If the the named file is a symbolic link returns information about " 800 "the link itself.]" 801 "\n" 802 "\nvar file\n" 803 "\n" 804 "[+EXIT STATUS?]{" 805 "[+0?Success.]" 806 "[+>0?An error occurred.]" 807 "}" 808 "[+SEE ALSO?\bopen\b(1),\btmpfile\b(1),\bdup\b(1),\bclose\b(1),\bpoll\b(1),\bstat\b(2),\blstat\b(2)]" 809 ; 810 811 812 extern int b_stat(int argc, char *argv[], void *extra) 813 { 814 register Namval_t *np; 815 register int n; 816 Shell_t *shp = sh_contexttoshell(extra); 817 struct filedata *fdp; 818 long flags = 0; 819 struct stat statb; 820 while (n = optget(argv, sh_optstat)) switch (n) 821 { 822 case 'l': 823 flags |= letterbit(n); 824 break; 825 case ':': 826 errormsg(SH_DICT, 2, "%s", opt_info.arg); 827 break; 828 case '?': 829 errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg); 830 break; 831 } 832 argc -= opt_info.index; 833 argv += opt_info.index; 834 if(argc!=2) 835 errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0)); 836 837 if(flags&letterbit('l')) 838 { 839 if(lstat(argv[1], &statb) < 0) 840 errormsg(SH_DICT, ERROR_system(1), "%s: stat failed", argv[1]); 841 } 842 else 843 { 844 if(stat(argv[1], &statb) < 0) 845 errormsg(SH_DICT, ERROR_system(1), "%s: stat failed", argv[1]); 846 847 } 848 849 np = nv_open(argv[0],shp->var_tree,NV_ARRAY|NV_VARNAME|NV_NOASSIGN); 850 if(!nv_isnull(np)) 851 nv_unset(np); 852 mkclass(np,&Fileclass); 853 fdp = (struct filedata*)np->nvalue; 854 fdp->statb = statb; 855 fdp->fd = -1; 856 fdp->name = strdup(argv[1]); 857 return(0); 858 } 859 860 static const char sh_optpoll[] = 861 "[-?\n@(#)$Id: poll (AT&T Labs Research) 2007-12-20 $\n]" 862 "[-author?Roland Mainz <roland.mainz@nrubsig.org]" 863 "[-license?http://www.opensource.org/licenses/cpl1.0.txt]" 864 "[+NAME? poll - input/output multiplexing]" 865 "[+DESCRIPTION?The poll command provides applications with a mechanism " 866 "for multiplexing input/output over a set of file descriptors. " 867 "For each member of the array variable \bvar\b, " 868 "poll examines the given file descriptor in the subscript \b.fd\b " 869 "for the event(s) specified in the subscript \b.events\b." 870 "The poll command identifies those file descriptors on which an " 871 "application can read or write data, or on which certain events have " 872 "occurred.]" 873 "[+?The \bvar\b argument specifies the file descriptors to be examined " 874 "and the events of interest for each file descriptor. " 875 "It is a array of structured variables with one member for each open " 876 "file descriptor of interest. The array's members contain the following " 877 "subscripts:]{" 878 "[+?\b.fd\b # file descriptor]" 879 "[+?\b.events\b # requested events]" 880 "[+?\b.revents\b # returned event]" 881 "}" 882 "[+?The \bfd\b variable specifies an open file descriptor and the " 883 "\bevents\b and \brevents\b members are strings constructed from " 884 "a concaternation of the following event flags, seperated by '|':]" 885 "{ " 886 "[+POLLIN?Data other than high priority data may be " 887 "read without blocking. For STREAMS, this " 888 "flag is set in revents even if the message " 889 "is of zero length.]" 890 "[+POLLRDNORM?Normal data (priority band equals 0) may be " 891 "read without blocking. For STREAMS, this " 892 "flag is set in revents even if the message " 893 "is of zero length.]" 894 "[+POLLRDBAND?Data from a non-zero priority band may be " 895 "read without blocking. For STREAMS, this " 896 "flag is set in revents even if the message " 897 "is of zero length.]" 898 "[+POLLPRI?High priority data may be received without " 899 "blocking. For STREAMS, this flag is set in " 900 "revents even if the message is of zero " 901 "length.]" 902 "[+POLLOUT?Normal data (priority band equals 0) may be " 903 "written without blocking.]" 904 "[+POLLWRNORM?The same as POLLOUT.]" 905 "[+POLLWRBAND?Priority data (priority band > 0) may be " 906 "written. This event only examines bands " 907 "that have been written to at least once.]" 908 "[+POLLERR?An error has occurred on the device or " 909 "stream. This flag is only valid in the " 910 "revents bitmask; it is not used in the " 911 "events member.]" 912 "[+POLLHUP?A hangup has occurred on the stream. This " 913 "event and POLLOUT are mutually exclusive; a " 914 "stream can never be writable if a hangup has " 915 "occurred. However, this event and POLLIN, " 916 ", POLLRDBAND, or POLLPRI are not " 917 "mutually exclusive. This flag is only valid " 918 "in the revents bitmask; it is not used in " 919 "the events member.]" 920 "[+POLLNVAL?The specified fd value does not belong to an " 921 "open file. This flag is only valid in the " 922 "revents member; it is not used in the events " 923 "member.]" 924 "}" 925 "]" 926 927 "[+?If the value fd is less than 0, events is ignored and " 928 "revents is set to 0 in that entry on return from poll.]" 929 930 "[+?The results of the poll query are stored in the revents " 931 "member in the \bvar\b structure. POLL*-strings are set in the \brevents\b " 932 "variable to indicate which of the requested events are true. " 933 "If none are true, the \brevents\b will be an empty string when " 934 "the poll command returns. The event flags " 935 "POLLHUP, POLLERR, and POLLNVAL are always set in \brevents\b " 936 "if the conditions they indicate are true; this occurs even " 937 "though these flags were not present in events.]" 938 939 "[+?If none of the defined events have occurred on any selected " 940 "file descriptor, poll waits at least timeout milliseconds " 941 "for an event to occur on any of the selected file descriptors. " 942 "On a computer where millisecond timing accuracy is not " 943 "available, timeout is rounded up to the nearest legal value " 944 "available on that system. If the value timeout is 0, poll " 945 "returns immediately. If the value of timeout is -1, poll " 946 "blocks until a requested event occurs or until the call is " 947 "interrupted.]" 948 949 "[+?The poll function supports regular files, terminal and " 950 "pseudo-terminal devices, STREAMS-based files, FIFOs and " 951 "pipes. The behavior of poll on elements of fds that refer " 952 "to other types of file is unspecified.]" 953 954 "[+?The poll function supports sockets.]" 955 956 "[+?A file descriptor for a socket that is listening for connections " 957 "will indicate that it is ready for reading, once connections " 958 "are available. A file descriptor for a socket that " 959 "is connecting asynchronously will indicate that it is ready " 960 "for writing, once a connection has been established.]" 961 962 "[+?Regular files always poll TRUE for reading and writing.]" 963 964 "[c:fdcount]:[fdcount?Upon successful completion, a non-negative value is " 965 "returned. A positive value indicates the total number of " 966 "file descriptors that has been selected (that is, file " 967 "descriptors for which the revents member is non-zero). A " 968 "value of 0 indicates that the call timed out and no file " 969 "descriptors have been selected. Upon failure, -1 is returned.]" 970 "[t:timeout]:[seconds?Timeout in seconds. If the value timeout is 0, " 971 "poll returns immediately. If the value of timeout is -1, poll " 972 "blocks until a requested event occurs or until the call is " 973 "interrupted.]" 974 "[T:mtimeout]:[milliseconds?Timeout in milliseconds. If the value timeout is 0, " 975 "poll returns immediately. If the value of timeout is -1, poll " 976 "blocks until a requested event occurs or until the call is " 977 "interrupted.]" 978 "\n" 979 "\nvar\n" 980 "\n" 981 "[+EXIT STATUS?]{" 982 "[+0?Success.]" 983 "[+>0?An error occurred.]" 984 "}" 985 "[+SEE ALSO?\bopen\b(1),\btmpfile\b(1),\bdup\b(1),\bclose\b(1),\bpoll\b(2)]" 986 ; 987 988 /* 989 * |mystpcpy| - like |strcpy()| but returns the end of the buffer 990 * 991 * Copy string s2 to s1. s1 must be large enough. 992 * return s1-1 (position of string terminator ('\0') in destnation buffer). 993 */ 994 static 995 char *mystpcpy(char *s1, const char *s2) 996 { 997 while (*s1++ = *s2++) 998 ; 999 return (s1-1); 1000 } 1001 1002 static 1003 Namval_t *nv_open_fmt(Dt_t *dict, int flags, const char *namefmt, ...) 1004 { 1005 char varnamebuff[PATH_MAX]; 1006 va_list ap; 1007 1008 va_start(ap, namefmt); 1009 vsnprintf(varnamebuff, sizeof(varnamebuff), namefmt, ap); 1010 va_end(ap); 1011 1012 return nv_open(varnamebuff, dict, flags); 1013 } 1014 1015 static 1016 int poll_strtoevents(const char *str) 1017 { 1018 int events = 0; 1019 1020 if (strstr(str, "POLLIN")) events |= POLLIN; 1021 if (strstr(str, "POLLRDNORM")) events |= POLLRDNORM; 1022 if (strstr(str, "POLLRDBAND")) events |= POLLRDBAND; 1023 if (strstr(str, "POLLPRI")) events |= POLLPRI; 1024 if (strstr(str, "POLLOUT")) events |= POLLOUT; 1025 if (strstr(str, "POLLWRNORM")) events |= POLLWRNORM; 1026 if (strstr(str, "POLLWRBAND")) events |= POLLWRBAND; 1027 if (strstr(str, "POLLERR")) events |= POLLERR; 1028 if (strstr(str, "POLLHUP")) events |= POLLHUP; 1029 if (strstr(str, "POLLNVAL")) events |= POLLNVAL; 1030 1031 return events; 1032 } 1033 1034 1035 static 1036 void poll_eventstostr(char *s, int events) 1037 { 1038 *s='\0'; 1039 if (!events) 1040 return; 1041 1042 if (events & POLLIN) s=mystpcpy(s, "POLLIN|"); 1043 if (events & POLLRDNORM) s=mystpcpy(s, "POLLRDNORM|"); 1044 if (events & POLLRDBAND) s=mystpcpy(s, "POLLRDBAND|"); 1045 if (events & POLLPRI) s=mystpcpy(s, "POLLPRI|"); 1046 if (events & POLLOUT) s=mystpcpy(s, "POLLOUT|"); 1047 if (events & POLLWRNORM) s=mystpcpy(s, "POLLWRNORM|"); 1048 if (events & POLLWRBAND) s=mystpcpy(s, "POLLWRBAND|"); 1049 if (events & POLLERR) s=mystpcpy(s, "POLLERR|"); 1050 if (events & POLLHUP) s=mystpcpy(s, "POLLHUP|"); 1051 if (events & POLLNVAL) s=mystpcpy(s, "POLLNVAL|"); 1052 1053 /* Remove trailling '|' */ 1054 s--; 1055 if(*s=='|') 1056 *s='\0'; 1057 } 1058 1059 #undef getconf 1060 #define getconf(x) strtol(astconf(x,NiL,NiL),NiL,0) 1061 1062 extern int b_poll(int argc, char *argv[], void *extra) 1063 { 1064 register Namval_t *np; 1065 register int n; 1066 Shell_t *shp = sh_contexttoshell(extra); 1067 char *varname; 1068 int fd; 1069 unsigned int numpollfd = 0; 1070 int i; 1071 char *s; 1072 double timeout = -1.; 1073 char buff[256]; 1074 char *pollfdcountvarname = NULL; 1075 long open_max, 1076 bpoll_max; 1077 1078 if ((open_max = getconf("OPEN_MAX")) <= 0) 1079 open_max = OPEN_MAX; 1080 /* |bpoll_max| needs to be larger than |OPEN_MAX| to make sure we 1081 * can listen to different sets of events per fd. 1082 */ 1083 bpoll_max = open_max*2L; 1084 1085 while (n = optget(argv, sh_optpoll)) switch (n) 1086 { 1087 case 't': 1088 case 'T': 1089 errno = 0; 1090 timeout = strtod(opt_info.arg, (char **)NULL); 1091 if (errno != 0) 1092 errormsg(SH_DICT, ERROR_system(1), "%s: invalid timeout", opt_info.arg); 1093 1094 /* -t uses seconds, -T milliseconds */ 1095 if (n == 't') 1096 timeout *= 1000.; 1097 break; 1098 case 'c': 1099 pollfdcountvarname = opt_info.arg; 1100 break; 1101 case ':': 1102 errormsg(SH_DICT, 2, "%s", opt_info.arg); 1103 break; 1104 case '?': 1105 errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg); 1106 break; 1107 } 1108 argc -= opt_info.index; 1109 argv += opt_info.index; 1110 if(argc!=1) 1111 errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0)); 1112 1113 varname = argv[0]; 1114 1115 #ifdef __GNUC__ 1116 /* 1117 * Allocate one extra array entry to keep ctfconvert+gcc builds 1118 * happy until CR #6379193 is fixed. 1119 */ 1120 struct pollfd pollfd[bpoll_max+1]; 1121 #else 1122 struct pollfd pollfd[bpoll_max]; 1123 #endif 1124 for(i=0 ; i < bpoll_max ; i++) 1125 { 1126 np = nv_open_fmt(shp->var_tree, NV_VARNAME|NV_NOFAIL|NV_NOADD, "%s[%d].fd", varname, i); 1127 if (!np) 1128 break; 1129 fd = (int)nv_getnum(np); 1130 if (fd < 0 || fd > OPEN_MAX) 1131 errormsg(SH_DICT, ERROR_system(1), "invalid pollfd fd"); 1132 nv_close(np); 1133 pollfd[i].fd = fd; 1134 1135 np = nv_open_fmt(shp->var_tree, NV_VARNAME|NV_NOFAIL|NV_NOADD, "%s[%d].events", varname, i); 1136 if (!np) 1137 errormsg(SH_DICT, ERROR_system(1), "missing pollfd events"); 1138 1139 s = nv_getval(np); 1140 if (!s) 1141 errormsg(SH_DICT, ERROR_system(1), "missing pollfd events value"); 1142 pollfd[i].events = poll_strtoevents(s); 1143 nv_close(np); 1144 1145 pollfd[i].revents = 0; 1146 1147 numpollfd++; 1148 } 1149 1150 if (i == bpoll_max) 1151 errormsg(SH_DICT, ERROR_system(1), "cannot handle more than %d entries.", bpoll_max); 1152 1153 n = poll(pollfd, numpollfd, timeout); 1154 /* FixMe: EGAIN and EINTR may require extra handling */ 1155 if (n < 0) 1156 errormsg(SH_DICT, ERROR_system(1), "failure"); 1157 1158 if (pollfdcountvarname) 1159 { 1160 int32_t v = n; 1161 1162 np = nv_open_fmt(shp->var_tree, NV_VARNAME|NV_NOFAIL, "%s", pollfdcountvarname); 1163 if (!np) 1164 errormsg(SH_DICT, ERROR_system(1), "couldn't create poll count variable %s", pollfdcountvarname); 1165 nv_putval(np, (char *)&v, NV_INTEGER); 1166 nv_close(np); 1167 } 1168 1169 for(i=0 ; i < numpollfd ; i++) 1170 { 1171 np = nv_open_fmt(shp->var_tree, NV_VARNAME|NV_NOFAIL, "%s[%d].revents", varname, i); 1172 if (!np) 1173 errormsg(SH_DICT, ERROR_system(1), "couldn't create pollfd %s[%d].revents", varname, i); 1174 1175 poll_eventstostr(buff, pollfd[i].revents); 1176 1177 nv_putval(np, buff, 0); 1178 nv_close(np); 1179 } 1180 1181 return(0); 1182 } 1183 1184 static const char sh_optrewind[] = 1185 "[-?\n@(#)$Id: rewind (AT&T Labs Research) 2007-05-07 $\n]" 1186 "[-author?Roland Mainz <roland.mainz@nrubsig.org>]" 1187 "[-license?http://www.opensource.org/licenses/cpl1.0.txt]" 1188 "[+NAME? rewind - reset file position indicator in a stream]" 1189 "[+DESCRIPTION?The \brewind\b command will move the file pointer of fd to position 0.]" 1190 "\n" 1191 "\nfd\n" 1192 "\n" 1193 "[+EXIT STATUS?]{" 1194 "[+0?Success.]" 1195 "[+>0?An error occurred.]" 1196 "}" 1197 "[+SEE ALSO?\bopen\b(1),\btmpfile\b(1),\bdup\b(1),\bclose\b(1),\bstat\b(1),\bstat\b(2)]" 1198 ; 1199 1200 1201 extern int b_rewind(int argc, char *argv[], void *extra) 1202 { 1203 Shell_t *shp = sh_contexttoshell(extra); 1204 int fd = -1; 1205 register int n; 1206 while (n = optget(argv, sh_optrewind)) switch (n) 1207 { 1208 case ':': 1209 errormsg(SH_DICT, 2, "%s", opt_info.arg); 1210 break; 1211 case '?': 1212 errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg); 1213 break; 1214 } 1215 argc -= opt_info.index; 1216 argv += opt_info.index; 1217 if(argc!=1) 1218 errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0)); 1219 1220 errno = 0; 1221 fd = strtol(argv[0], (char **)NULL, 0); 1222 if (errno != 0 || fd < 0) 1223 errormsg(SH_DICT, ERROR_system(1), "%s: invalid fd", argv[0]); 1224 1225 if (sh_seek(fd, 0, SEEK_SET) == (off_t)-1) 1226 errormsg(SH_DICT, ERROR_system(1), "seek error"); 1227 1228 return(0); 1229 } 1230