1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1982-2008 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 * Array processing routines 23 * 24 * David Korn 25 * AT&T Labs 26 * dgk@research.att.com 27 * 28 */ 29 30 #include "defs.h" 31 #include <stak.h> 32 #include "name.h" 33 34 #define NUMSIZE (4+(ARRAY_MAX>999)+(ARRAY_MAX>9999)+(ARRAY_MAX>99999)) 35 #define is_associative(ap) array_assoc((Namarr_t*)(ap)) 36 #define array_setbit(cp, n, b) (cp[n] |= (b)) 37 #define array_clrbit(cp, n, b) (cp[n] &= ~(b)) 38 #define array_isbit(cp, n, b) (cp[n] & (b)) 39 #define NV_CHILD NV_EXPORT 40 #define ARRAY_CHILD 1 41 #define ARRAY_NOFREE 2 42 43 struct index_array 44 { 45 Namarr_t header; 46 void *xp; /* if set, subscripts will be converted */ 47 int cur; /* index of current element */ 48 int maxi; /* maximum index for array */ 49 unsigned char *bits; /* bit array for child subscripts */ 50 union Value val[1]; /* array of value holders */ 51 }; 52 53 struct assoc_array 54 { 55 Namarr_t header; 56 Namval_t *pos; 57 Namval_t *nextpos; 58 Namval_t *cur; 59 }; 60 61 static Namarr_t *array_scope(Namval_t *np, Namarr_t *ap, int flags) 62 { 63 Namarr_t *aq; 64 struct index_array *ar; 65 size_t size = ap->hdr.dsize; 66 if(size==0) 67 size = ap->hdr.disc->dsize; 68 if(!(aq=newof(NIL(Namarr_t*),Namarr_t,1,size-sizeof(Namarr_t)))) 69 return(0); 70 memcpy(aq,ap,size); 71 aq->hdr.nofree &= ~1; 72 aq->hdr.nofree |= (flags&NV_RDONLY)?1:0; 73 if(is_associative(aq)) 74 { 75 aq->scope = (void*)dtopen(&_Nvdisc,Dtoset); 76 dtview((Dt_t*)aq->scope,aq->table); 77 aq->table = (Dt_t*)aq->scope; 78 return(aq); 79 } 80 aq->scope = (void*)ap; 81 ar = (struct index_array*)aq; 82 memset(ar->val, 0, ar->maxi*sizeof(char*)); 83 return(aq); 84 } 85 86 static int array_unscope(Namval_t *np,Namarr_t *ap) 87 { 88 Namfun_t *fp; 89 if(!ap->scope) 90 return(0); 91 if(is_associative(ap)) 92 (*ap->fun)(np, NIL(char*), NV_AFREE); 93 if((fp = nv_disc(np,(Namfun_t*)ap,NV_POP)) && !(fp->nofree&1)) 94 free((void*)fp); 95 nv_delete(np,(Dt_t*)0,0); 96 return(1); 97 } 98 99 static void array_syncsub(Namarr_t *ap, Namarr_t *aq) 100 { 101 ((struct index_array*)ap)->cur = ((struct index_array*)aq)->cur; 102 } 103 104 static int array_covered(Namval_t *np, struct index_array *ap) 105 { 106 struct index_array *aq = (struct index_array*)ap->header.scope; 107 if(!ap->header.fun && aq) 108 return ((ap->cur<aq->maxi) && aq->val[ap->cur].cp); 109 return(0); 110 } 111 112 /* 113 * replace discipline with new one 114 */ 115 static void array_setptr(register Namval_t *np, struct index_array *old, struct index_array *new) 116 { 117 register Namfun_t **fp = &np->nvfun; 118 while(*fp && *fp!= &old->header.hdr) 119 fp = &((*fp)->next); 120 if(*fp) 121 { 122 new->header.hdr.next = (*fp)->next; 123 *fp = &new->header.hdr; 124 } 125 else sfprintf(sfstderr,"discipline not replaced\n"); 126 } 127 128 /* 129 * Calculate the amount of space to be allocated to hold an 130 * indexed array into which <maxi> is a legal index. The number of 131 * elements that will actually fit into the array (> <maxi> 132 * but <= ARRAY_MAX) is returned. 133 * 134 */ 135 static int arsize(struct index_array *ap, register int maxi) 136 { 137 if(ap && maxi < 2*ap->maxi) 138 maxi = 2*ap->maxi; 139 maxi = roundof(maxi,ARRAY_INCR); 140 return (maxi>ARRAY_MAX?ARRAY_MAX:maxi); 141 } 142 143 static struct index_array *array_grow(Namval_t*, struct index_array*,int); 144 145 /* return index of highest element of an array */ 146 int array_maxindex(Namval_t *np) 147 { 148 register struct index_array *ap = (struct index_array*)nv_arrayptr(np); 149 register int i = ap->maxi; 150 if(is_associative(ap)) 151 return(-1); 152 while(i>0 && ap->val[--i].cp==0); 153 return(i+1); 154 } 155 156 static union Value *array_getup(Namval_t *np, Namarr_t *arp, int update) 157 { 158 register struct index_array *ap = (struct index_array*)arp; 159 register union Value *up; 160 int nofree; 161 if(!arp) 162 return(&np->nvalue); 163 if(is_associative(ap)) 164 { 165 Namval_t *mp; 166 mp = (Namval_t*)((*arp->fun)(np,NIL(char*),NV_ACURRENT)); 167 if(mp) 168 { 169 nofree = nv_isattr(mp,NV_NOFREE); 170 up = &mp->nvalue; 171 } 172 else 173 return((union Value*)((*arp->fun)(np,NIL(char*),0))); 174 } 175 else 176 { 177 if(ap->cur >= ap->maxi) 178 errormsg(SH_DICT,ERROR_exit(1),e_subscript,nv_name(np)); 179 up = &(ap->val[ap->cur]); 180 nofree = array_isbit(ap->bits,ap->cur,ARRAY_NOFREE); 181 } 182 if(update) 183 { 184 if(nofree) 185 nv_onattr(np,NV_NOFREE); 186 else 187 nv_offattr(np,NV_NOFREE); 188 } 189 return(up); 190 } 191 192 /* 193 * Get the Value pointer for an array. 194 * Delete space as necessary if flag is ARRAY_DELETE 195 * After the lookup is done the last @ or * subscript is incremented 196 */ 197 static Namval_t *array_find(Namval_t *np,Namarr_t *arp, int flag) 198 { 199 register struct index_array *ap = (struct index_array*)arp; 200 register union Value *up; 201 Namval_t *mp; 202 int wasundef; 203 if(flag&ARRAY_LOOKUP) 204 ap->header.nelem &= ~ARRAY_NOSCOPE; 205 else 206 ap->header.nelem |= ARRAY_NOSCOPE; 207 if(wasundef = ap->header.nelem&ARRAY_UNDEF) 208 { 209 ap->header.nelem &= ~ARRAY_UNDEF; 210 /* delete array is the same as delete array[@] */ 211 if(flag&ARRAY_DELETE) 212 { 213 nv_putsub(np, NIL(char*), ARRAY_SCAN|ARRAY_NOSCOPE); 214 ap->header.nelem |= ARRAY_SCAN; 215 } 216 else /* same as array[0] */ 217 { 218 if(is_associative(ap)) 219 (*ap->header.fun)(np,"0",flag==ARRAY_ASSIGN?NV_AADD:0); 220 else 221 ap->cur = 0; 222 } 223 } 224 if(is_associative(ap)) 225 { 226 mp = (Namval_t*)((*arp->fun)(np,NIL(char*),NV_ACURRENT)); 227 if(!mp) 228 up = (union Value*)∓ 229 else if(nv_isarray(mp)) 230 { 231 if(wasundef) 232 nv_putsub(mp,NIL(char*),ARRAY_UNDEF); 233 return(mp); 234 } 235 else 236 { 237 up = &mp->nvalue; 238 if(nv_isvtree(mp)) 239 { 240 if(!up->cp && flag==ARRAY_ASSIGN) 241 { 242 nv_arraychild(np,mp,0); 243 ap->header.nelem++; 244 } 245 return(mp); 246 } 247 } 248 } 249 else 250 { 251 if(!(ap->header.nelem&ARRAY_SCAN) && ap->cur >= ap->maxi) 252 ap = array_grow(np, ap, (int)ap->cur); 253 if(ap->cur>=ap->maxi) 254 errormsg(SH_DICT,ERROR_exit(1),e_subscript,nv_name(np)); 255 up = &(ap->val[ap->cur]); 256 if((!up->cp||up->cp==Empty) && nv_type(np) && nv_isvtree(np)) 257 { 258 char *cp; 259 if(!ap->header.table) 260 ap->header.table = dtopen(&_Nvdisc,Dtoset); 261 sfprintf(sh.strbuf,"%d",ap->cur); 262 cp = sfstruse(sh.strbuf); 263 mp = nv_search(cp, ap->header.table, NV_ADD); 264 mp->nvenv = (char*)np; 265 nv_arraychild(np,mp,0); 266 } 267 if(up->np && array_isbit(ap->bits,ap->cur,ARRAY_CHILD)) 268 { 269 if(wasundef && nv_isarray(up->np)) 270 nv_putsub(up->np,NIL(char*),ARRAY_UNDEF); 271 return(up->np); 272 } 273 } 274 np->nvalue.cp = up->cp; 275 if(!up->cp) 276 { 277 if(flag!=ARRAY_ASSIGN) 278 return(0); 279 if(!array_covered(np,ap)) 280 ap->header.nelem++; 281 } 282 return(np); 283 } 284 285 #if SHOPT_TYPEDEF 286 int nv_arraysettype(Namval_t *np, Namval_t *tp, const char *sub, int flags) 287 { 288 Namval_t *nq; 289 char *av[2]; 290 int rdonly = nv_isattr(np,NV_RDONLY); 291 int xtrace = sh_isoption(SH_XTRACE); 292 Namarr_t *ap = nv_arrayptr(np); 293 av[1] = 0; 294 sh.last_table = 0; 295 if(!ap->table) 296 ap->table = dtopen(&_Nvdisc,Dtoset); 297 if(nq = nv_search(sub, ap->table, NV_ADD)) 298 { 299 if(!nq->nvfun && nq->nvalue.cp && *nq->nvalue.cp==0) 300 _nv_unset(nq,NV_RDONLY); 301 nv_arraychild(np,nq,0); 302 if(!nv_isattr(tp,NV_BINARY)) 303 { 304 sfprintf(sh.strbuf,"%s=%s",nv_name(nq),nv_getval(np)); 305 av[0] = strdup(sfstruse(sh.strbuf)); 306 } 307 if(!nv_clone(tp,nq,flags|NV_NOFREE)) 308 return(0); 309 ap->nelem |= ARRAY_SCAN; 310 if(!rdonly) 311 nv_offattr(nq,NV_RDONLY); 312 if(!nv_isattr(tp,NV_BINARY)) 313 { 314 if(xtrace) 315 sh_offoption(SH_XTRACE); 316 ap->nelem &= ~ARRAY_SCAN; 317 sh_eval(sh_sfeval(av),0); 318 ap->nelem |= ARRAY_SCAN; 319 free((void*)av[0]); 320 if(xtrace) 321 sh_onoption(SH_XTRACE); 322 } 323 return(1); 324 } 325 return(0); 326 } 327 #endif /* SHOPT_TYPEDEF */ 328 329 330 static Namfun_t *array_clone(Namval_t *np, Namval_t *mp, int flags, Namfun_t *fp) 331 { 332 Namarr_t *ap = (Namarr_t*)fp; 333 Namval_t *nq, *mq; 334 char *name, *sub=0; 335 int nelem, skipped=0; 336 Dt_t *otable=ap->table; 337 struct index_array *aq = (struct index_array*)ap, *ar; 338 Shell_t *shp = sh_getinterp(); 339 if(flags&NV_MOVE) 340 { 341 if((flags&NV_COMVAR) && nv_putsub(np,NIL(char*),ARRAY_SCAN)) 342 { 343 do 344 { 345 if(nq=nv_opensub(np)) 346 nq->nvenv = (void*)mp; 347 } 348 while(nv_nextsub(np)); 349 } 350 return(fp); 351 } 352 nelem = ap->nelem; 353 if(nelem&ARRAY_NOCLONE) 354 return(0); 355 if((flags&NV_TYPE) && !ap->scope) 356 { 357 ap = array_scope(np,ap,flags); 358 return(&ap->hdr); 359 } 360 ap = (Namarr_t*)nv_clone_disc(fp,0); 361 if(flags&NV_COMVAR) 362 { 363 ap->scope = 0; 364 ap->nelem = 0; 365 sh.prev_table = sh.last_table; 366 sh.prev_root = sh.last_root; 367 } 368 if(ap->table) 369 { 370 ap->table = dtopen(&_Nvdisc,Dtoset); 371 if(ap->scope && !(flags&NV_COMVAR)) 372 { 373 ap->scope = ap->table; 374 dtview(ap->table, otable->view); 375 } 376 } 377 mp->nvfun = (Namfun_t*)ap; 378 mp->nvflag &= NV_MINIMAL; 379 mp->nvflag |= (np->nvflag&~(NV_MINIMAL|NV_NOFREE)); 380 if(!(nelem&(ARRAY_SCAN|ARRAY_UNDEF)) && (sub=nv_getsub(np))) 381 sub = strdup(sub); 382 ar = (struct index_array*)ap; 383 if(!is_associative(ap)) 384 ar->bits = (unsigned char*)&ar->val[ar->maxi]; 385 if(!nv_putsub(np,NIL(char*),ARRAY_SCAN|((flags&NV_COMVAR)?0:ARRAY_NOSCOPE))) 386 { 387 if(ap->fun) 388 (*ap->fun)(np,(char*)np,0); 389 skipped=1; 390 goto skip; 391 } 392 do 393 { 394 name = nv_getsub(np); 395 nv_putsub(mp,name,ARRAY_ADD|ARRAY_NOSCOPE); 396 mq = 0; 397 if(nq=nv_opensub(np)) 398 mq = nv_search(name,ap->table,NV_ADD); 399 if(nq && (flags&NV_COMVAR) && nv_isvtree(nq)) 400 { 401 mq->nvalue.cp = 0; 402 if(!is_associative(ap)) 403 ar->val[ar->cur].np = mq; 404 nv_clone(nq,mq,flags); 405 } 406 else if(flags&NV_ARRAY) 407 { 408 if((flags&NV_NOFREE) && !is_associative(ap)) 409 array_setbit(aq->bits,aq->cur,ARRAY_NOFREE); 410 else if(nq && (flags&NV_NOFREE)) 411 { 412 mq->nvalue = nq->nvalue; 413 nv_onattr(nq,NV_NOFREE); 414 } 415 } 416 else if(nv_isattr(np,NV_INTEGER)) 417 { 418 Sfdouble_t d= nv_getnum(np); 419 if(!is_associative(ap)) 420 ar->val[ar->cur].cp = 0; 421 nv_putval(mp,(char*)&d,NV_LDOUBLE); 422 } 423 else 424 { 425 if(!is_associative(ap)) 426 ar->val[ar->cur].cp = 0; 427 nv_putval(mp,nv_getval(np),NV_RDONLY); 428 } 429 aq->header.nelem |= ARRAY_NOSCOPE; 430 } 431 while(nv_nextsub(np)); 432 skip: 433 if(sub) 434 { 435 if(!skipped) 436 nv_putsub(np,sub,0L); 437 free((void*)sub); 438 } 439 aq->header.nelem = ap->nelem = nelem; 440 return(&ap->hdr); 441 } 442 443 static char *array_getval(Namval_t *np, Namfun_t *disc) 444 { 445 register Namarr_t *aq,*ap = (Namarr_t*)disc; 446 register Namval_t *mp; 447 if((mp=array_find(np,ap,ARRAY_LOOKUP))!=np) 448 { 449 if(!mp && !is_associative(ap) && (aq=(Namarr_t*)ap->scope)) 450 { 451 array_syncsub(aq,ap); 452 if((mp=array_find(np,aq,ARRAY_LOOKUP))==np) 453 return(nv_getv(np,&aq->hdr)); 454 } 455 return(mp?nv_getval(mp):0); 456 } 457 return(nv_getv(np,&ap->hdr)); 458 } 459 460 static Sfdouble_t array_getnum(Namval_t *np, Namfun_t *disc) 461 { 462 register Namarr_t *aq,*ap = (Namarr_t*)disc; 463 register Namval_t *mp; 464 if((mp=array_find(np,ap,ARRAY_LOOKUP))!=np) 465 { 466 if(!mp && !is_associative(ap) && (aq=(Namarr_t*)ap->scope)) 467 { 468 array_syncsub(aq,ap); 469 if((mp=array_find(np,aq,ARRAY_LOOKUP))==np) 470 return(nv_getn(np,&aq->hdr)); 471 } 472 return(mp?nv_getnum(mp):0); 473 } 474 return(nv_getn(np,&ap->hdr)); 475 } 476 477 static void array_putval(Namval_t *np, const char *string, int flags, Namfun_t *dp) 478 { 479 register Namarr_t *ap = (Namarr_t*)dp; 480 register union Value *up; 481 register Namval_t *mp; 482 register struct index_array *aq = (struct index_array*)ap; 483 int scan,nofree = nv_isattr(np,NV_NOFREE); 484 do 485 { 486 mp = array_find(np,ap,string?ARRAY_ASSIGN:ARRAY_DELETE); 487 scan = ap->nelem&ARRAY_SCAN; 488 if(mp && mp!=np) 489 { 490 if(!is_associative(ap) && string && !nv_type(np) && nv_isvtree(mp)) 491 { 492 if(!nv_isattr(np,NV_NOFREE)) 493 _nv_unset(mp,flags&NV_RDONLY); 494 array_clrbit(aq->bits,aq->cur,ARRAY_CHILD); 495 aq->val[aq->cur].cp = 0; 496 if(!nv_isattr(mp,NV_NOFREE)) 497 nv_delete(mp,ap->table,0); 498 goto skip; 499 } 500 nv_putval(mp, string, flags); 501 if(string) 502 { 503 #if SHOPT_TYPEDEF 504 if(ap->hdr.type && ap->hdr.type!=nv_type(mp)) 505 nv_arraysettype(np,ap->hdr.type,nv_getsub(np),0); 506 #endif /* SHOPT_TYPEDEF */ 507 continue; 508 } 509 ap->nelem |= scan; 510 } 511 if(!string) 512 { 513 if(mp) 514 { 515 if(is_associative(ap)) 516 { 517 (*ap->fun)(np,NIL(char*),NV_ADELETE); 518 np->nvalue.cp = 0; 519 } 520 else 521 { 522 if(mp!=np) 523 { 524 array_clrbit(aq->bits,aq->cur,ARRAY_CHILD); 525 aq->val[aq->cur].cp = 0; 526 nv_delete(mp,ap->table,0); 527 } 528 if(!array_covered(np,(struct index_array*)ap)) 529 ap->nelem--; 530 } 531 } 532 if(array_elem(ap)==0 && ((ap->nelem&ARRAY_SCAN) || !is_associative(ap))) 533 { 534 if(is_associative(ap)) 535 (*ap->fun)(np, NIL(char*), NV_AFREE); 536 else if(ap->table) 537 dtclose(ap->table); 538 nv_offattr(np,NV_ARRAY); 539 } 540 if(!mp || mp!=np || is_associative(ap)) 541 continue; 542 } 543 skip: 544 /* prevent empty string from being deleted */ 545 up = array_getup(np,ap,!nofree); 546 if(up->cp == Empty) 547 up->cp = 0; 548 if(nv_isarray(np)) 549 np->nvalue.up = up; 550 nv_putv(np,string,flags,&ap->hdr); 551 if(!is_associative(ap)) 552 { 553 if(string) 554 array_clrbit(aq->bits,aq->cur,ARRAY_NOFREE); 555 else if(mp==np) 556 aq->val[aq->cur].cp = 0; 557 } 558 #if SHOPT_TYPEDEF 559 if(string && ap->hdr.type && nv_isvtree(np)) 560 nv_arraysettype(np,ap->hdr.type,nv_getsub(np),0); 561 #endif /* SHOPT_TYPEDEF */ 562 } 563 while(!string && nv_nextsub(np)); 564 if(ap) 565 ap->nelem &= ~ARRAY_NOSCOPE; 566 if(nofree) 567 nv_onattr(np,NV_NOFREE); 568 else 569 nv_offattr(np,NV_NOFREE); 570 if(!string && !nv_isattr(np,NV_ARRAY)) 571 { 572 Namfun_t *nfp; 573 if(!is_associative(ap) && aq->xp) 574 { 575 _nv_unset(nv_namptr(aq->xp,0),NV_RDONLY); 576 free((void*)aq->xp); 577 } 578 if((nfp = nv_disc(np,(Namfun_t*)ap,NV_POP)) && !(nfp->nofree&1)) 579 free((void*)nfp); 580 if(!nv_isnull(np)) 581 { 582 nv_onattr(np,NV_NOFREE); 583 _nv_unset(np,flags); 584 } 585 if(np->nvalue.cp==Empty) 586 np->nvalue.cp = 0; 587 } 588 if(!string && (flags&NV_TYPE)) 589 array_unscope(np,ap); 590 } 591 592 static const Namdisc_t array_disc = 593 { 594 sizeof(Namarr_t), 595 array_putval, 596 array_getval, 597 array_getnum, 598 0, 599 0, 600 array_clone 601 }; 602 603 static void array_copytree(Namval_t *np, Namval_t *mp) 604 { 605 char *val; 606 Namfun_t *fp = nv_disc(np,NULL,NV_POP); 607 nv_offattr(np,NV_ARRAY); 608 nv_clone(np,mp,0); 609 np->nvalue.up = &mp->nvalue; 610 val = sfstruse(sh.strbuf); 611 fp->nofree &= ~1; 612 nv_disc(np,(Namfun_t*)fp, NV_FIRST); 613 fp->nofree |= 1; 614 nv_onattr(np,NV_ARRAY); 615 mp->nvenv = (char*)np; 616 } 617 618 /* 619 * Increase the size of the indexed array of elements in <arp> 620 * so that <maxi> is a legal index. If <arp> is 0, an array 621 * of the required size is allocated. A pointer to the 622 * allocated Namarr_t structure is returned. 623 * <maxi> becomes the current index of the array. 624 */ 625 static struct index_array *array_grow(Namval_t *np, register struct index_array *arp,int maxi) 626 { 627 register struct index_array *ap; 628 register int i; 629 register int newsize = arsize(arp,maxi+1); 630 if (maxi >= ARRAY_MAX) 631 errormsg(SH_DICT,ERROR_exit(1),e_subscript, fmtbase((long)maxi,10,0)); 632 i = (newsize-1)*sizeof(union Value*)+newsize; 633 ap = new_of(struct index_array,i); 634 memset((void*)ap,0,sizeof(*ap)+i); 635 ap->maxi = newsize; 636 ap->cur = maxi; 637 ap->bits = (unsigned char*)&ap->val[newsize]; 638 memset(ap->bits, 0, newsize); 639 if(arp) 640 { 641 ap->header = arp->header; 642 ap->header.hdr.dsize = sizeof(*ap) + i; 643 for(i=0;i < arp->maxi;i++) 644 ap->val[i].cp = arp->val[i].cp; 645 memcpy(ap->bits, arp->bits, arp->maxi); 646 array_setptr(np,arp,ap); 647 free((void*)arp); 648 } 649 else 650 { 651 Namval_t *mp=0; 652 ap->header.hdr.dsize = sizeof(*ap) + i; 653 i = 0; 654 ap->header.fun = 0; 655 if(nv_isnull(np) && nv_isattr(np,NV_NOFREE)) 656 { 657 i = ARRAY_TREE; 658 nv_offattr(np,NV_NOFREE); 659 } 660 if(np->nvalue.cp==Empty) 661 np->nvalue.cp=0; 662 if(nv_hasdisc(np,&array_disc) || nv_isvtree(np)) 663 { 664 ap->header.table = dtopen(&_Nvdisc,Dtoset); 665 mp = nv_search("0", ap->header.table, 0); 666 667 if(mp && nv_isnull(mp)) 668 { 669 Namfun_t *fp; 670 ap->val[0].np = mp; 671 array_setbit(ap->bits,0,ARRAY_CHILD); 672 for(fp=np->nvfun; fp && !fp->disc->readf; fp=fp->next); 673 if(fp) 674 (*fp->disc->readf)(mp,(Sfio_t*)0,0,fp); 675 i++; 676 } 677 } 678 else if((ap->val[0].cp=np->nvalue.cp)) 679 i++; 680 else if(nv_isattr(np,NV_INTEGER)) 681 { 682 Sfdouble_t d= nv_getnum(np); 683 i++; 684 } 685 ap->header.nelem = i; 686 ap->header.hdr.disc = &array_disc; 687 nv_disc(np,(Namfun_t*)ap, NV_FIRST); 688 nv_onattr(np,NV_ARRAY); 689 if(mp) 690 { 691 array_copytree(np,mp); 692 ap->header.hdr.nofree &= ~1; 693 } 694 } 695 for(;i < newsize;i++) 696 ap->val[i].cp = 0; 697 return(ap); 698 } 699 700 int nv_atypeindex(Namval_t *np, const char *tname) 701 { 702 Namval_t *tp; 703 int offset = staktell(); 704 int n = strlen(tname)-1; 705 sfprintf(stkstd,"%s.%.*s%c",NV_CLASS,n,tname,0); 706 tp = nv_open(stakptr(offset), sh.var_tree, NV_NOADD|NV_VARNAME); 707 stakseek(offset); 708 if(tp) 709 { 710 struct index_array *ap = (struct index_array*)nv_arrayptr(np); 711 if(!nv_hasdisc(tp,&ENUM_disc)) 712 errormsg(SH_DICT,ERROR_exit(1),e_notenum,tp->nvname); 713 if(!ap) 714 ap = array_grow(np,ap,1); 715 ap->xp = calloc(NV_MINSZ,1); 716 np = nv_namptr(ap->xp,0); 717 np->nvname = tp->nvname; 718 nv_onattr(np,NV_MINIMAL); 719 nv_clone(tp,np,NV_NOFREE); 720 nv_offattr(np,NV_RDONLY); 721 return(1); 722 } 723 errormsg(SH_DICT,ERROR_exit(1),e_unknowntype, n,tname); 724 return(0); 725 } 726 727 Namarr_t *nv_arrayptr(register Namval_t *np) 728 { 729 if(nv_isattr(np,NV_ARRAY)) 730 return((Namarr_t*)nv_hasdisc(np, &array_disc)); 731 return(0); 732 } 733 734 /* 735 * Verify that argument is an indexed array and convert to associative, 736 * freeing relevant storage 737 */ 738 static Namarr_t *nv_changearray(Namval_t *np, void *(*fun)(Namval_t*,const char*,int)) 739 { 740 register Namarr_t *ap; 741 char numbuff[NUMSIZE+1]; 742 unsigned dot, digit, n; 743 union Value *up; 744 struct index_array *save_ap; 745 register char *string_index=&numbuff[NUMSIZE]; 746 numbuff[NUMSIZE]='\0'; 747 748 if(!fun || !(ap = nv_arrayptr(np)) || is_associative(ap)) 749 return(NIL(Namarr_t*)); 750 751 nv_stack(np,&ap->hdr); 752 save_ap = (struct index_array*)nv_stack(np,0); 753 ap = (Namarr_t*)((*fun)(np, NIL(char*), NV_AINIT)); 754 ap->nelem = 0; 755 ap->fun = fun; 756 nv_onattr(np,NV_ARRAY); 757 758 for(dot = 0; dot < (unsigned)save_ap->maxi; dot++) 759 { 760 if(save_ap->val[dot].cp) 761 { 762 if ((digit = dot)== 0) 763 *--string_index = '0'; 764 else while( n = digit ) 765 { 766 digit /= 10; 767 *--string_index = '0' + (n-10*digit); 768 } 769 nv_putsub(np, string_index, ARRAY_ADD); 770 up = (union Value*)((*ap->fun)(np,NIL(char*),0)); 771 up->cp = save_ap->val[dot].cp; 772 save_ap->val[dot].cp = 0; 773 } 774 string_index = &numbuff[NUMSIZE]; 775 } 776 free((void*)save_ap); 777 return(ap); 778 } 779 780 /* 781 * set the associative array processing method for node <np> to <fun> 782 * The array pointer is returned if sucessful. 783 */ 784 Namarr_t *nv_setarray(Namval_t *np, void *(*fun)(Namval_t*,const char*,int)) 785 { 786 register Namarr_t *ap; 787 char *value=0; 788 Namfun_t *fp; 789 int nelem = 0; 790 if(fun && (ap = nv_arrayptr(np))) 791 { 792 /* 793 * if it's already an indexed array, convert to 794 * associative structure 795 */ 796 if(!is_associative(ap)) 797 ap = nv_changearray(np, fun); 798 return(ap); 799 } 800 if(nv_isnull(np) && nv_isattr(np,NV_NOFREE)) 801 { 802 nelem = ARRAY_TREE; 803 nv_offattr(np,NV_NOFREE); 804 } 805 if(!(fp=nv_isvtree(np))) 806 value = nv_getval(np); 807 if(fun && !ap && (ap = (Namarr_t*)((*fun)(np, NIL(char*), NV_AINIT)))) 808 { 809 /* check for preexisting initialization and save */ 810 ap->nelem = nelem; 811 ap->fun = fun; 812 nv_onattr(np,NV_ARRAY); 813 if(fp || value) 814 { 815 nv_putsub(np, "0", ARRAY_ADD); 816 if(value) 817 nv_putval(np, value, 0); 818 else 819 { 820 Namval_t *mp = (Namval_t*)((*fun)(np,NIL(char*),NV_ACURRENT)); 821 array_copytree(np,mp); 822 } 823 } 824 return(ap); 825 } 826 return(NIL(Namarr_t*)); 827 } 828 829 /* 830 * move parent subscript into child 831 */ 832 Namval_t *nv_arraychild(Namval_t *np, Namval_t *nq, int c) 833 { 834 Namfun_t *fp; 835 register Namarr_t *ap = nv_arrayptr(np); 836 union Value *up; 837 Namval_t *tp; 838 if(!nq) 839 return(ap?array_find(np,ap, ARRAY_LOOKUP):0); 840 if(!ap) 841 { 842 nv_putsub(np, NIL(char*), ARRAY_FILL); 843 ap = nv_arrayptr(np); 844 } 845 if(!(up = array_getup(np,ap,0))) 846 return((Namval_t*)0); 847 np->nvalue.cp = up->cp; 848 if((tp=nv_type(np)) || c) 849 { 850 ap->nelem |= ARRAY_NOCLONE; 851 nq->nvenv = (char*)np; 852 if(c=='t') 853 nv_clone(tp,nq, 0); 854 else 855 nv_clone(np, nq, NV_NODISC); 856 nv_offattr(nq,NV_ARRAY); 857 ap->nelem &= ~ARRAY_NOCLONE; 858 } 859 nq->nvenv = (char*)np; 860 if((fp=nq->nvfun) && fp->disc && fp->disc->setdisc && (fp = nv_disc(nq,fp,NV_POP))) 861 free((void*)fp); 862 if(!ap->fun) 863 { 864 struct index_array *aq = (struct index_array*)ap; 865 array_setbit(aq->bits,aq->cur,ARRAY_CHILD); 866 up->np = nq; 867 } 868 if(c=='.') 869 nv_setvtree(nq); 870 return(nq); 871 } 872 873 /* 874 * This routine sets subscript of <np> to the next element, if any. 875 * The return value is zero, if there are no more elements 876 * Otherwise, 1 is returned. 877 */ 878 int nv_nextsub(Namval_t *np) 879 { 880 register struct index_array *ap = (struct index_array*)nv_arrayptr(np); 881 register unsigned dot; 882 struct index_array *aq=0, *ar=0; 883 if(!ap || !(ap->header.nelem&ARRAY_SCAN)) 884 return(0); 885 if(is_associative(ap)) 886 { 887 Namval_t *nq; 888 if(nq=(*ap->header.fun)(np,NIL(char*),NV_ANEXT)) 889 { 890 if(nv_isattr(nq,NV_CHILD)) 891 nv_putsub(nq->nvalue.np,NIL(char*),ARRAY_UNDEF); 892 return(1); 893 } 894 ap->header.nelem &= ~(ARRAY_SCAN|ARRAY_NOCHILD); 895 return(0); 896 } 897 if(!(ap->header.nelem&ARRAY_NOSCOPE)) 898 ar = (struct index_array*)ap->header.scope; 899 for(dot=ap->cur+1; dot < (unsigned)ap->maxi; dot++) 900 { 901 aq = ap; 902 if(!ap->val[dot].cp && !(ap->header.nelem&ARRAY_NOSCOPE)) 903 { 904 if(!(aq=ar) || dot>=(unsigned)aq->maxi) 905 continue; 906 } 907 if(aq->val[dot].cp) 908 { 909 ap->cur = dot; 910 if(array_isbit(aq->bits, dot,ARRAY_CHILD)) 911 { 912 Namval_t *mp = aq->val[dot].np; 913 if((aq->header.nelem&ARRAY_NOCHILD) && nv_isvtree(mp)) 914 continue; 915 nv_putsub(mp,NIL(char*),ARRAY_UNDEF); 916 } 917 return(1); 918 } 919 } 920 ap->header.nelem &= ~(ARRAY_SCAN|ARRAY_NOCHILD); 921 ap->cur = 0; 922 return(0); 923 } 924 925 /* 926 * Set an array subscript for node <np> given the subscript <sp> 927 * An array is created if necessary. 928 * <mode> can be a number, plus or more of symbolic constants 929 * ARRAY_SCAN, ARRAY_UNDEF, ARRAY_ADD 930 * The node pointer is returned which can be NULL if <np> is 931 * not already array and the ARRAY_ADD bit of <mode> is not set. 932 * ARRAY_FILL sets the specified subscript to the empty string when 933 * ARRAY_ADD is specified and there is no value or sets all 934 * the elements up to the number specified if ARRAY_ADD is not specified 935 */ 936 Namval_t *nv_putsub(Namval_t *np,register char *sp,register long mode) 937 { 938 register struct index_array *ap = (struct index_array*)nv_arrayptr(np); 939 register int size = (mode&ARRAY_MASK); 940 if(!ap || !ap->header.fun) 941 { 942 if(sp) 943 { 944 if(ap && ap->xp && !strmatch(sp,"+([0-9])")) 945 { 946 Namval_t *mp = nv_namptr(ap->xp,0); 947 nv_putval(mp, sp,0); 948 size = nv_getnum(mp); 949 } 950 else 951 size = (int)sh_arith((char*)sp); 952 } 953 if(size <0 && ap) 954 size += array_maxindex(np); 955 if(size >= ARRAY_MAX || (size < 0)) 956 { 957 errormsg(SH_DICT,ERROR_exit(1),e_subscript, nv_name(np)); 958 return(NIL(Namval_t*)); 959 } 960 if(!ap || size>=ap->maxi) 961 { 962 if(size==0 && !(mode&ARRAY_FILL)) 963 return(NIL(Namval_t*)); 964 if(sh.subshell) 965 np = sh_assignok(np,1); 966 ap = array_grow(np, ap,size); 967 } 968 ap->header.nelem &= ~ARRAY_UNDEF; 969 ap->header.nelem |= (mode&(ARRAY_SCAN|ARRAY_NOCHILD|ARRAY_UNDEF|ARRAY_NOSCOPE)); 970 #if 0 971 if(array_isbit(ap->bits,oldsize,ARRAY_CHILD)) 972 mp = ap->val[oldsize].np; 973 if(size != oldsize && mp->nvalue.cp) 974 { 975 Namfun_t *nfp; 976 for(nfp=np->nvfun; nfp; nfp=nfp->next) 977 { 978 if(nfp->disc && nfp->disc->readf) 979 { 980 (*nfp->disc->readf)(mp,(Sfio_t*)0,0,nfp); 981 break; 982 } 983 } 984 } 985 #endif 986 ap->cur = size; 987 if((mode&ARRAY_SCAN) && (ap->cur--,!nv_nextsub(np))) 988 np = 0; 989 if(mode&(ARRAY_FILL|ARRAY_ADD)) 990 { 991 if(!(mode&ARRAY_ADD)) 992 { 993 int n; 994 for(n=0; n <= size; n++) 995 { 996 if(!ap->val[n].cp) 997 { 998 ap->val[n].cp = Empty; 999 if(!array_covered(np,ap)) 1000 ap->header.nelem++; 1001 } 1002 } 1003 if(n=ap->maxi-ap->maxi) 1004 memset(&ap->val[size],0,n*sizeof(union Value)); 1005 } 1006 else if(!ap->val[size].cp) 1007 { 1008 if(sh.subshell) 1009 np = sh_assignok(np,1); 1010 ap->val[size].cp = Empty; 1011 if(!array_covered(np,ap)) 1012 ap->header.nelem++; 1013 } 1014 } 1015 else if(!(mode&ARRAY_SCAN)) 1016 { 1017 ap->header.nelem &= ~ARRAY_SCAN; 1018 if(array_isbit(ap->bits,size,ARRAY_CHILD)) 1019 nv_putsub(ap->val[size].np,NIL(char*),ARRAY_UNDEF); 1020 if(sp && !(mode&ARRAY_ADD) && !ap->val[size].cp) 1021 np = 0; 1022 } 1023 return((Namval_t*)np); 1024 } 1025 ap->header.nelem &= ~ARRAY_UNDEF; 1026 if(!(mode&ARRAY_FILL)) 1027 ap->header.nelem &= ~ARRAY_SCAN; 1028 ap->header.nelem |= (mode&(ARRAY_SCAN|ARRAY_NOCHILD|ARRAY_UNDEF|ARRAY_NOSCOPE)); 1029 if(sp) 1030 { 1031 if(mode&ARRAY_SETSUB) 1032 { 1033 (*ap->header.fun)(np, sp, NV_ASETSUB); 1034 return(np); 1035 } 1036 (*ap->header.fun)(np, sp, (mode&ARRAY_ADD)?NV_AADD:0); 1037 if(!(mode&(ARRAY_SCAN|ARRAY_ADD)) && !(*ap->header.fun)(np,NIL(char*),NV_ACURRENT)) 1038 np = 0; 1039 } 1040 else if(mode&ARRAY_SCAN) 1041 (*ap->header.fun)(np,(char*)np,0); 1042 else if(mode&ARRAY_UNDEF) 1043 (*ap->header.fun)(np, "",0); 1044 if((mode&ARRAY_SCAN) && !nv_nextsub(np)) 1045 np = 0; 1046 return(np); 1047 } 1048 1049 /* 1050 * process an array subscript for node <np> given the subscript <cp> 1051 * returns pointer to character after the subscript 1052 */ 1053 char *nv_endsubscript(Namval_t *np, register char *cp, int mode) 1054 { 1055 register int count=1, quoted=0, c; 1056 register char *sp = cp+1; 1057 /* first find matching ']' */ 1058 while(count>0 && (c= *++cp)) 1059 { 1060 if(c=='\\' && (!(mode&NV_SUBQUOTE) || (c=cp[1])=='[' || c==']' || c=='\\' || c=='*' || c=='@')) 1061 { 1062 quoted=1; 1063 cp++; 1064 } 1065 else if(c=='[') 1066 count++; 1067 else if(c==']') 1068 count--; 1069 } 1070 *cp = 0; 1071 if(quoted) 1072 { 1073 /* strip escape characters */ 1074 count = staktell(); 1075 stakwrite(sp,1+cp-sp); 1076 sh_trim(sp=stakptr(count)); 1077 } 1078 if(mode && np) 1079 { 1080 if((mode&NV_ASSIGN) && (cp[1]=='=' || cp[1]=='+')) 1081 mode |= NV_ADD; 1082 nv_putsub(np, sp, ((mode&NV_ADD)?ARRAY_ADD:0)|(cp[1]&&(mode&NV_ADD)?ARRAY_FILL:mode&ARRAY_FILL)); 1083 } 1084 if(quoted) 1085 stakseek(count); 1086 *cp++ = c; 1087 return(cp); 1088 } 1089 1090 1091 Namval_t *nv_opensub(Namval_t* np) 1092 { 1093 register struct index_array *ap = (struct index_array*)nv_arrayptr(np); 1094 if(ap) 1095 { 1096 if(is_associative(ap)) 1097 return((Namval_t*)((*ap->header.fun)(np,NIL(char*),NV_ACURRENT))); 1098 else if(array_isbit(ap->bits,ap->cur,ARRAY_CHILD)) 1099 return(ap->val[ap->cur].np); 1100 } 1101 return(NIL(Namval_t*)); 1102 } 1103 1104 char *nv_getsub(Namval_t* np) 1105 { 1106 static char numbuff[NUMSIZE]; 1107 register struct index_array *ap; 1108 register unsigned dot, n; 1109 register char *cp = &numbuff[NUMSIZE]; 1110 if(!np || !(ap = (struct index_array*)nv_arrayptr(np))) 1111 return(NIL(char*)); 1112 if(is_associative(ap)) 1113 return((char*)((*ap->header.fun)(np,NIL(char*),NV_ANAME))); 1114 if(ap->xp) 1115 { 1116 np = nv_namptr(ap->xp,0); 1117 np->nvalue.s = ap->cur; 1118 return(nv_getval(np)); 1119 } 1120 if((dot = ap->cur)==0) 1121 *--cp = '0'; 1122 else while(n=dot) 1123 { 1124 dot /= 10; 1125 *--cp = '0' + (n-10*dot); 1126 } 1127 return(cp); 1128 } 1129 1130 /* 1131 * If <np> is an indexed array node, the current subscript index 1132 * returned, otherwise returns -1 1133 */ 1134 int nv_aindex(register Namval_t* np) 1135 { 1136 Namarr_t *ap = nv_arrayptr(np); 1137 if(!ap) 1138 return(0); 1139 else if(is_associative(ap)) 1140 return(-1); 1141 return(((struct index_array*)(ap))->cur&ARRAY_MASK); 1142 } 1143 1144 int nv_arraynsub(register Namarr_t* ap) 1145 { 1146 return(array_elem(ap)); 1147 } 1148 1149 int nv_aimax(register Namval_t* np) 1150 { 1151 struct index_array *ap = (struct index_array*)nv_arrayptr(np); 1152 int sub = -1; 1153 if(!ap || is_associative(&ap->header)) 1154 return(-1); 1155 sub = ap->maxi; 1156 while(--sub>0 && ap->val[sub].cp==0); 1157 return(sub); 1158 } 1159 1160 /* 1161 * This is the default implementation for associative arrays 1162 */ 1163 void *nv_associative(register Namval_t *np,const char *sp,int mode) 1164 { 1165 register struct assoc_array *ap = (struct assoc_array*)nv_arrayptr(np); 1166 register int type; 1167 switch(mode) 1168 { 1169 case NV_AINIT: 1170 if(ap = (struct assoc_array*)calloc(1,sizeof(struct assoc_array))) 1171 { 1172 ap->header.table = dtopen(&_Nvdisc,Dtoset); 1173 ap->cur = 0; 1174 ap->pos = 0; 1175 ap->header.hdr.disc = &array_disc; 1176 nv_disc(np,(Namfun_t*)ap, NV_FIRST); 1177 ap->header.hdr.dsize = sizeof(struct assoc_array); 1178 ap->header.hdr.nofree &= ~1; 1179 } 1180 return((void*)ap); 1181 case NV_ADELETE: 1182 if(ap->cur) 1183 { 1184 if(!ap->header.scope || (Dt_t*)ap->header.scope==ap->header.table || !nv_search(ap->cur->nvname,(Dt_t*)ap->header.scope,0)) 1185 ap->header.nelem--; 1186 _nv_unset(ap->cur,NV_RDONLY); 1187 nv_delete(ap->cur,ap->header.table,0); 1188 ap->cur = 0; 1189 } 1190 return((void*)ap); 1191 case NV_AFREE: 1192 ap->pos = 0; 1193 if(ap->header.scope) 1194 { 1195 ap->header.table = dtview(ap->header.table,(Dt_t*)0); 1196 dtclose(ap->header.scope); 1197 ap->header.scope = 0; 1198 } 1199 else 1200 dtclose(ap->header.table); 1201 return((void*)ap); 1202 case NV_ANEXT: 1203 if(!ap->pos) 1204 { 1205 if((ap->header.nelem&ARRAY_NOSCOPE) && ap->header.scope && dtvnext(ap->header.table)) 1206 { 1207 ap->header.scope = dtvnext(ap->header.table); 1208 ap->header.table->view = 0; 1209 } 1210 if(!(ap->pos=ap->cur)) 1211 ap->pos = (Namval_t*)dtfirst(ap->header.table); 1212 } 1213 else 1214 ap->pos = ap->nextpos; 1215 for(;ap->cur=ap->pos; ap->pos=ap->nextpos) 1216 { 1217 ap->nextpos = (Namval_t*)dtnext(ap->header.table,ap->pos); 1218 if(ap->cur->nvalue.cp) 1219 { 1220 if((ap->header.nelem&ARRAY_NOCHILD) && nv_isattr(ap->cur,NV_CHILD)) 1221 continue; 1222 return((void*)ap); 1223 } 1224 } 1225 if((ap->header.nelem&ARRAY_NOSCOPE) && ap->header.scope && !dtvnext(ap->header.table)) 1226 { 1227 ap->header.table->view = (Dt_t*)ap->header.scope; 1228 ap->header.scope = ap->header.table; 1229 } 1230 return(NIL(void*)); 1231 case NV_ASETSUB: 1232 ap->cur = (Namval_t*)sp; 1233 return((void*)ap->cur); 1234 case NV_ACURRENT: 1235 if(ap->cur) 1236 ap->cur->nvenv = (char*)np; 1237 return((void*)ap->cur); 1238 case NV_ANAME: 1239 if(ap->cur) 1240 return((void*)ap->cur->nvname); 1241 return(NIL(void*)); 1242 default: 1243 if(sp) 1244 { 1245 Namval_t *mp=0; 1246 ap->cur = 0; 1247 if(sp==(char*)np) 1248 return(0); 1249 type = nv_isattr(np,NV_PUBLIC&~(NV_ARRAY|NV_CHILD|NV_MINIMAL)); 1250 if(mode) 1251 mode = NV_ADD|HASH_NOSCOPE; 1252 else if(ap->header.nelem&ARRAY_NOSCOPE) 1253 mode = HASH_NOSCOPE; 1254 if(*sp==0 && (mode&NV_ADD)) 1255 sfprintf(sfstderr,"adding empty subscript\n"); 1256 if(sh.subshell && (mp=nv_search(sp,ap->header.table,0)) && nv_isnull(mp)) 1257 ap->cur = mp; 1258 if((mp || (mp=nv_search(sp,ap->header.table,mode))) && nv_isnull(mp) && (mode&NV_ADD)) 1259 { 1260 nv_onattr(mp,type); 1261 mp->nvenv = (char*)np; 1262 if((mode&NV_ADD) && nv_type(np)) 1263 nv_arraychild(np,mp,0); 1264 if(sh.subshell) 1265 np = sh_assignok(np,1); 1266 if(!ap->header.scope || !nv_search(sp,dtvnext(ap->header.table),0)) 1267 ap->header.nelem++; 1268 if(nv_isnull(mp)) 1269 { 1270 if(ap->header.nelem&ARRAY_TREE) 1271 nv_setvtree(mp); 1272 mp->nvalue.cp = Empty; 1273 } 1274 } 1275 else if(ap->header.nelem&ARRAY_SCAN) 1276 { 1277 Namval_t fake; 1278 fake.nvname = (char*)sp; 1279 ap->pos = mp = (Namval_t*)dtprev(ap->header.table,&fake); 1280 ap->nextpos = (Namval_t*)dtnext(ap->header.table,mp); 1281 } 1282 np = mp; 1283 if(ap->pos != np && !(ap->header.nelem&ARRAY_SCAN)) 1284 ap->pos = 0; 1285 ap->cur = np; 1286 } 1287 if(ap->cur) 1288 return((void*)(&ap->cur->nvalue)); 1289 else 1290 return((void*)(&ap->cur)); 1291 } 1292 } 1293 1294 /* 1295 * Assign values to an array 1296 */ 1297 void nv_setvec(register Namval_t *np,int append,register int argc,register char *argv[]) 1298 { 1299 int arg0=0; 1300 struct index_array *ap=0,*aq; 1301 if(nv_isarray(np)) 1302 { 1303 ap = (struct index_array*)nv_arrayptr(np); 1304 if(ap && is_associative(ap)) 1305 errormsg(SH_DICT,ERROR_exit(1),"cannot append index array to associative array %s",nv_name(np)); 1306 } 1307 if(append) 1308 { 1309 if(ap) 1310 { 1311 if(!(aq = (struct index_array*)ap->header.scope)) 1312 aq = ap; 1313 arg0 = ap->maxi; 1314 while(--arg0>0 && ap->val[arg0].cp==0 && aq->val[arg0].cp==0); 1315 arg0++; 1316 } 1317 else if(!nv_isnull(np)) 1318 arg0=1; 1319 } 1320 while(--argc >= 0) 1321 { 1322 nv_putsub(np,NIL(char*),(long)argc+arg0|ARRAY_FILL|ARRAY_ADD); 1323 nv_putval(np,argv[argc],0); 1324 } 1325 } 1326 1327