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