1: #include <stdio.h> 2: 3: # include "../ingres.h" 4: # include "../aux.h" 5: # include "../access.h" 6: # include "../symbol.h" 7: # include "../lock.h" 8: 9: # define BUFSIZE 1024 10: # define WBUFSIZE 512 11: # define MAXMAP 3 * MAXDOM 12: # define DUMMY 'd' 13: # define ESCAPE '\\' 14: 15: 16: struct map 17: { 18: char name[MAXNAME+1]; /* attribute name */ 19: char ftype; /* attfrmt of file domain */ 20: char rtype; /* attfrmt of relation domain */ 21: int flen; /* attfrml of file domain */ 22: int rlen; /* attfrml of relation domain */ 23: int roffset; /* attoff of relation domain */ 24: int used; /* tag for duplicate checking */ 25: char *fdelim; /* pointer to list of file param delims */ 26: char *paramname; /* pointer to original parameter name */ 27: /* used for supplying domain name in case of error */ 28: }; 29: struct map Map[MAXMAP]; /* one entry for each user 30: specified domain in copy statement. */ 31: 32: int Mapcount; /* number of Map entries */ 33: 34: 35: struct descriptor Des; /* descriptor for copied relation */ 36: 37: extern struct out_arg Out_arg; /* user defined formats for numeric output */ 38: 39: FILE *File_iop; /* i/o file pointer */ 40: char *Filename; /* pointer to file name */ 41: 42: int Into; /* into is one if this is a copy into file */ 43: 44: char Inbuf[BUFSIZE]; /* input holder */ 45: char Outbuf[BUFSIZE]; /* output holder */ 46: 47: long Tupcount; /* number of tuples processed */ 48: char *Relname; /* name of relation */ 49: long Duptuple; /* number of duplicate tuples */ 50: long Baddoms; /* number of domains with control chars */ 51: long Truncount; /* number of truncations on a c0 field */ 52: int Piped[2]; /* pipe descriptor for copy communication */ 53: char *Cpdomains[] = /* dummy domain names for copy "into" */ 54: { 55: "nl", "\n", 56: "tab", "\t", 57: "sp", " ", 58: "nul", "\0", 59: "null", "\0", 60: "comma", ",", 61: "colon", ":", 62: "dash", "-", 63: "lparen", "(", 64: "rparen", ")", 65: 0 66: }; 67: 68: char Delimitor[] = ",\n\t"; /* default delims for c0 & d0 */ 69: 70: 71: /* 72: ** COPY -- Performs an ingres COPY. 73: ** 74: ** History: 75: ** 9/12/78 -- (marc) modified to print 76: ** an error message when trying to 77: ** copy into a view. 78: ** For this the error number 5518 79: ** was added and the call on openr was split 80: ** into two. 81: */ 82: 83: 84: copy(pc,pv) 85: int pc; 86: char **pv; 87: { 88: extern char *Usercode; 89: extern int Noupdt; 90: register int i, pid; 91: register char *cp; 92: int stat; 93: int copydone(); 94: int op; 95: 96: # ifdef xZTR1 97: if (tTf(7,1)) 98: printf("entered copy\n"); 99: # endif 100: Duptuple = 0; 101: Truncount = 0; 102: Tupcount = 0; 103: Baddoms = 0; 104: Relname = pv[0]; 105: Into = (pv[pc-2][0] == 'i'); 106: Filename = pv[pc-1]; 107: 108: /* relation must exist and not be a system relation */ 109: /* in addition a copy "from" can't be done if the user */ 110: /* doesn't own the relation */ 111: /* and furthermore it can't have an index */ 112: i = 0; /* assume all is well */ 113: if (op = openr(&Des, 2, Relname)) 114: { 115: if (op == AMOPNVIEW_ERR) 116: i = 5818; 117: else 118: { 119: if (op < 0) 120: syserr("COPY: openr 1 (%.14s) %d", Relname, op); 121: else 122: /* non-existant relation */ 123: i = 5800; 124: } 125: } 126: else 127: { 128: if (Into) 129: { 130: if ((Des.relstat & S_PROTALL) 131: && (Des.relstat & S_PROTRET) 132: && !bequal(Usercode, Des.relowner, 2)) 133: i = 5822; 134: } 135: else 136: { 137: /* extra checking if this is a copy "from" */ 138: 139: /* must be owned by the user */ 140: if (!bequal(Usercode, Des.relowner, 2)) 141: i = 5814; 142: else 143: /* must be updateable */ 144: if ((Des.relstat & S_NOUPDT) && Noupdt) 145: i = 5813; 146: else 147: /* must not be indexed */ 148: if (Des.relindxd > 0) 149: i = 5812; 150: } 151: } 152: if (i) 153: { 154: closer(&Des); 155: return (error(i, Relname, 0)); /* relation doesn't exist for this user */ 156: } 157: 158: /* check that file name begins with a "/" */ 159: cp = Filename; 160: while (*cp == ' ') 161: cp++; 162: if (*cp != '/') 163: { 164: closer(&Des); 165: return (error(5816, Filename, 0)); 166: } 167: 168: /* fill map structures with transfer information */ 169: if (i = mapfill(&pv[1])) 170: { 171: closer(&Des); 172: return (i); /* error in user semantics */ 173: } 174: 175: /* fork a child process which will run as the real user */ 176: /* that child will complete the copy and exit */ 177: if (pipe(Piped)) 178: syserr("copy:can't make pipe"); 179: if ((pid = fork()) < 0) 180: syserr("copy:can't fork"); 181: if (pid) 182: { 183: /* the ingres parent */ 184: close(Piped[1]); 185: ruboff(0); /* interrupts off */ 186: stat = fullwait(pid, "copy"); 187: if (read(Piped[0], &Des.reladds, 4) != 4) 188: syserr("copy:can't read pipe"); 189: close(Piped[0]); 190: closer(&Des); /* close the rel */ 191: rubon(); 192: /* if stat is != 0 then add on 5800 for error */ 193: if (stat) 194: stat += 5800; 195: return (stat); /* done */ 196: } 197: 198: /* the child. change to run as the real user */ 199: if (signal(2, 1) != 1) 200: signal(2, copydone); /* clean up on rubout */ 201: setuid(getuid()); 202: # ifndef xB_UNIX 203: setgid(getgid()); 204: # endif 205: if (Into) /* from relation into file */ 206: { 207: if ((File_iop = fopen(Filename, "w")) == NULL) /* create file for user */ 208: i = error(5806, Filename, 0); /* cant create file */ 209: else 210: { 211: if (Lockrel) /* set a shared lock on relation*/ 212: setrll(A_SLP, Des.reltid, M_SHARE); 213: i = rel_file(); 214: } 215: } 216: else /* from UNIX file into relation */ 217: { 218: if ((File_iop = fopen(Filename, "r")) == NULL) 219: i = error(5805, Filename, 0); /* cant open user file */ 220: else 221: { 222: if (Lockrel) /* set an exclusive lock on relat*/ 223: setrll(A_SLP, Des.reltid, M_EXCL); 224: i = file_rel(); 225: if (Duptuple) 226: error(5819, locv(Duptuple), 0); /* warning only */ 227: if (Baddoms) 228: error(5820, locv(Baddoms), 0); /* warning only */ 229: } 230: } 231: copydone(i); 232: } 233: 234: 235: copydone(i) 236: int i; 237: 238: /* 239: ** Finish up and exit after a copy or interrupt 240: ** 241: ** I is the return code. Since only a byte can be 242: ** returned, only the least significant 2 decimal 243: ** digits are returned. i is either 0 or a number like 58?? 244: */ 245: 246: { 247: if (Lockrel) /* unlock relation */ 248: unlrl(Des.reltid); 249: if (Truncount) 250: error(5821, locv(Truncount), 0); /* warning only */ 251: /* force the updates to be flushed */ 252: cleanrel(&Des); 253: if (File_iop) 254: fclose(File_iop); 255: if (write(Piped[1], &Des.reladds, 4) != 4) 256: syserr("copyc:can't writepipe"); 257: exit (i % 100); 258: } 259: 260: 261: 262: rel_file() 263: { 264: int j; 265: struct tup_id tid, limtid; 266: char *cp, save; 267: register int offset; 268: register int i; 269: register struct map *mp; 270: 271: /* set scan limits to scan the entire relation */ 272: if (find(&Des, NOKEY, &tid, &limtid)) 273: syserr("find error"); 274: 275: while ((i = get(&Des, &tid, &limtid, Inbuf, 1)) == 0) 276: { 277: mp = Map; 278: offset = 0; 279: for (i = 0; i < Mapcount; i++) 280: { 281: /* For cases of char to numeric conversion, 282: there must be a null byte at the end of the 283: string. The character just past the current 284: domain is saved an a null byte inserted */ 285: cp = &Inbuf[mp->roffset + mp->rlen]; /* compute address */ 286: save = *cp; /* get the character */ 287: *cp = '\0'; /* insert a null */ 288: j = transfer(&Inbuf[mp->roffset], mp->rtype, mp->rlen, 289: mp->ftype, mp->flen, offset); 290: if (j) 291: { 292: /* bad ascii to numeric conversion or field length too small */ 293: return (error(j, mp->paramname, &Inbuf[mp->roffset], locv(Tupcount), Relname, Filename, 0)); 294: } 295: *cp = save; /* restore the saved character */ 296: offset += mp->flen; 297: mp++; 298: } 299: Tupcount++; 300: if (fwrite(Outbuf, 1, offset, File_iop) != offset) 301: syserr("copy:cant write to user file %s", Filename); 302: } 303: if (i < 0) 304: syserr("bad get from rel %d", i); 305: return (0); 306: } 307: 308: 309: 310: file_rel() 311: /* 312: ** file_rel is called to transfer tuples from 313: ** the input file and append them to the relation 314: ** 315: ** Char domains are initialized to blank and numeric 316: ** domains are initialized to zero. 317: */ 318: { 319: register int i, j; 320: register struct map *mp; 321: struct tup_id tid; 322: 323: clr_tuple(&Des, Outbuf); 324: 325: /* copy domains until an end of file or an error */ 326: for (;;) 327: { 328: mp = Map; 329: for (i = 0; i < Mapcount; i++) 330: { 331: if ((j = bread(mp)) <= 0) 332: { 333: if (j < 0) 334: { 335: i = 1; /* force an error */ 336: j = 5815; /* unterminated string */ 337: } 338: else 339: j = 5810; /* end of file */ 340: if (i) /* error only if end of file during a tuple or unterminated string */ 341: { 342: i = error(j, mp->paramname, locv(Tupcount), Filename, Relname, 0); 343: } 344: return (i); 345: } 346: j = transfer(Inbuf, mp->ftype, mp->flen, mp->rtype, mp->rlen, mp->roffset); 347: if (j) 348: { 349: /* bad ascii to numeric or field length too small */ 350: return (error(j, mp->paramname, Inbuf, locv(Tupcount), Filename, Relname, 0)); 351: } 352: mp++; 353: } 354: Tupcount++; 355: if ((j = insert(&Des, &tid, Outbuf, 1)) < 0) 356: syserr("insert error %d rel=%s", j, Relname); 357: if (j == 1) 358: Duptuple++; 359: mp++; 360: } 361: return (0); 362: } 363: 364: 365: 366: transfer(in, sf, sl, df, dl, doff) 367: 368: char *in; /* pointer to input chars */ 369: char sf; /* source format */ 370: int sl; /* source length */ 371: char df; /* destination format */ 372: int dl; /* destination length */ 373: int doff; /* destination offset */ 374: 375: /* 376: ** transfer copies data from "*in" to 377: ** Outbuf doing conversions whenever 378: ** necessary 379: */ 380: 381: { 382: double d; 383: float f; 384: register char *inp, *outp; 385: register int i; 386: int j; 387: long l; 388: char temp[MAXFIELD]; /* holds char during conversions to ascii */ 389: 390: 391: outp = &Outbuf[doff]; 392: inp = in; 393: 394: if (sf == DUMMY) 395: /* if source format is a dummy fields then 396: nothing else need be done */ 397: return (0); 398: 399: if (df == DUMMY) 400: { 401: /* fill field with dummy domain character */ 402: i = dl; /* i equals the number of chars */ 403: while (i--) 404: *outp++ = sf; /* sf holds dummy char */ 405: return (0); 406: } 407: 408: if (sf != CHAR) 409: { 410: if (df == CHAR) /* numeric to char conversion */ 411: { 412: switch (sl) 413: { 414: 415: /* int of size 1 or 2 */ 416: case 1: 417: itoa(*inp, temp); 418: break; 419: 420: case 2: 421: bmove(inp, &j, 2); /* copy to an integer */ 422: itoa(j, temp); /* convert to ascii */ 423: break; 424: 425: /* int or float of size 4 */ 426: case 4: 427: if (sf == INT) 428: { 429: bmove(inp, &l, 4); /* copy to a long */ 430: smove(locv(l), temp); /* convert and copy */ 431: } 432: 433: else 434: { 435: bmove(inp, &f, 4); 436: ftoa(f, temp, dl, Out_arg.f4prec, Out_arg.f4style); 437: } 438: break; 439: 440: /* float of size 8 */ 441: case 8: 442: bmove(inp, &d, 8); /* copy to a dbl variable */ 443: ftoa(d, temp, dl, Out_arg.f8prec, Out_arg.f8style); 444: break; 445: 446: /* there is no possible default */ 447: default: 448: syserr("bad domain length %d",sl); 449: } 450: 451: j = length(temp); 452: if ((i = dl - j) < 0) 453: return (5808); /* field won't fit */ 454: 455: /* blank pad from left. Number will be right justified */ 456: while (i--) 457: *outp++ = ' '; 458: 459: bmove(temp, outp, j); 460: return (0); 461: } 462: 463: if (convert(inp, outp, sf, sl, df, dl)) /* numeric to numeric transfer */ 464: return (5808); /* numeric truncation error */ 465: return (0); 466: } 467: 468: /* character to numeric conversion */ 469: /* and character to character conversion */ 470: switch (df) 471: { 472: 473: case CHAR: 474: i = sl; 475: if (!i) 476: { 477: i = length(inp); 478: } 479: if (i > dl) 480: i = dl; 481: if (charmove(inp, outp, i)) 482: Baddoms++; 483: for (outp += i; i<dl; i++) 484: *outp++ = ' '; 485: return (0); 486: 487: case FLOAT: 488: if (atof(inp, &d)) 489: return (5809); /* bad conversion to numeric */ 490: if (dl == 8) 491: bmove(&d, outp, dl); 492: else 493: { 494: f = d; /* f8 to f4 conversion */ 495: bmove(&f, outp, dl); 496: } 497: return (0); 498: 499: case INT: 500: if (dl == 4) 501: { 502: if (atol(inp, &l)) 503: return (5809); 504: bmove(&l, outp, 4); 505: return (0); 506: } 507: if (atoi(inp, &j)) 508: return (5809); 509: if ((dl == 1) && ((j < -128) || (j > 127))) 510: return (5809); 511: bmove(&j, outp, dl); 512: return (0); 513: } 514: } 515: 516: charmove(in, out, length) 517: char *in, *out; 518: int length; 519: 520: /* 521: ** moves a character string from "in" 522: ** to "out" removing any control characters. 523: ** returns true if any control characters were found 524: */ 525: 526: { 527: register char *ip, *op; 528: register int l; 529: int bad; 530: 531: bad = FALSE; 532: ip = in; 533: op = out; 534: l = length; 535: 536: while (l--) 537: if ((*op++ = *ip++) < ' ') 538: { 539: *(op-1) = ' '; 540: bad = TRUE; 541: } 542: return (bad); 543: } 544: 545: 546: 547: mapfill(aptr) 548: /* 549: ** Mapfill fills the Map structure with the list 550: ** of user supplied attributes. It then reads 551: ** the list of relation attributes and checks 552: ** for matching attribute names. 553: ** 554: ** if an error occures then mapfill returns -1 555: ** else it returns 0 556: ** 557: ** Mapfill performs special processing on 558: ** dummy domains. 559: ** 560: ** If no user attributes are given, then "given"=FALSE 561: ** and each attribute in the relation is set up to be 562: ** copied in the formats and order in which they 563: ** exist in the relation 564: */ 565: 566: char **aptr; 567: 568: { 569: register char **ap; 570: register struct map *mp; 571: register int i; 572: char *fp; 573: extern struct descriptor Attdes; 574: struct attribute att; 575: struct tup_id tid, limtid; 576: int given, cnt; 577: char *zcheck(); 578: char *dumvalue(); 579: 580: Mapcount = 0; 581: mp = Map; 582: ap = aptr; 583: 584: /* Gather list of user supplied attributes */ 585: 586: while (**ap) 587: { 588: /* check for overflow */ 589: if (Mapcount == MAXMAP) 590: return (error(5803, 0)); /* more than MAXMAP specifiers */ 591: 592: mp->paramname = *ap; /* save pointer to user supplied name */ 593: pmove(*ap++, mp->name, MAXNAME, ' '); 594: fp = *ap++; /* fp points to format string */ 595: mp->used = 0; 596: mp->rlen = 0; /* zero in case this is a dummy domain */ 597: mp->roffset = 0; 598: mp->fdelim = 0; 599: /* check domain type in *fp */ 600: switch (*fp++) 601: { 602: 603: case 'c': 604: i = CHAR; 605: if ((mp->fdelim = zcheck(fp)) == 0) 606: return (-1); /* bad delimitor */ 607: break; 608: 609: case 'f': 610: i = FLOAT; 611: break; 612: 613: case 'i': 614: i = INT; 615: break; 616: 617: case DUMMY: 618: i = DUMMY; 619: if ((mp->fdelim = zcheck(fp)) == 0) 620: return (-1); 621: break; 622: 623: default: 624: return (error(5811, mp->paramname, --fp, 0)); 625: } 626: mp->ftype = i; 627: 628: 629: /* convert format length to binary */ 630: if (atoi(fp, &mp->flen) || 631: mp->flen < 0 || 632: mp->flen > 511 || 633: (mp->ftype == FLOAT && mp->flen != 4 && mp->flen != 8) || 634: (mp->ftype == INT && mp->flen != 1 && mp->flen != 2 && mp->flen != 4)) 635: { 636: return (error(5804, mp->paramname, --fp, 0)); /* bad length for attribute */ 637: } 638: 639: /* process dummy domain if any */ 640: if (Into && mp->ftype == DUMMY && mp->flen) 641: { 642: if ((fp = dumvalue(mp->paramname)) == 0) 643: return (5807); /* bad dummy name */ 644: mp->rtype = *fp; /* use first char of string */ 645: } 646: 647: /* check for format of type "c0delim" on copy "into" */ 648: if (Into && mp->flen == 0 && mp->fdelim != Delimitor) 649: { 650: fp = mp->fdelim; 651: 652: /* is there room for a dummy domain? */ 653: mp++; 654: if (++Mapcount == MAXMAP) 655: return (error(5803, 0)); /* no room */ 656: 657: /* create a dummy entry */ 658: mp->ftype = DUMMY; 659: mp->flen = 1; 660: mp->rtype = *fp; 661: mp->roffset = mp->rlen = 0; 662: } 663: 664: mp++; 665: Mapcount++; 666: } 667: /* if no atributes were given, set flag */ 668: if (Mapcount) 669: given = TRUE; 670: else 671: given = FALSE; 672: 673: /* open attribute relation and prepare for scan */ 674: opencatalog("attribute", 0); 675: 676: setkey(&Attdes, &att, Des.relid, ATTRELID); 677: setkey(&Attdes, &att, Des.relowner, ATTOWNER); 678: 679: if (find(&Attdes, EXACTKEY, &tid, &limtid, &att)) 680: syserr("find error for att-rel"); 681: 682: /* scan Map for each relation attribute */ 683: while ((i = get(&Attdes, &tid, &limtid, &att, 1)) == 0) 684: { 685: if (!bequal(&Des, &att, MAXNAME+2)) 686: continue; 687: /* if no user attributes were supplied, fake an entry */ 688: if (!given) 689: { 690: Mapcount++; 691: mp = &Map[att.attid -1]; 692: mp->rtype = mp->ftype = att.attfrmt; 693: mp->rlen = mp->flen = att.attfrml & 0377; 694: mp->roffset = att.attoff; 695: mp->used = 1; 696: mp->paramname = mp->name; /* point to name */ 697: bmove(att.attname, mp->name, MAXNAME); /* copy name */ 698: continue; 699: } 700: mp = Map; 701: 702: /* check each user domain for match with relation domain */ 703: for (i = Mapcount; i--; mp++) 704: { 705: if (mp->ftype == DUMMY) 706: continue; /* ignore dummy */ 707: if (!bequal(mp->name, att.attname, 12)) 708: continue; 709: 710: mp->rtype = att.attfrmt; 711: mp->rlen = att.attfrml & 0377; 712: mp->roffset = att.attoff; 713: mp->used++; 714: 715: /* check for special case of C0 in a copy "into" */ 716: if (Into && (mp->flen == 0) && mp->ftype == CHAR) 717: { 718: switch (mp->rtype) 719: { 720: case CHAR: 721: mp->flen = mp->rlen; 722: break; 723: 724: case INT: 725: switch (mp->rlen) 726: { 727: 728: case 1: 729: mp->flen = Out_arg.i1width; 730: break; 731: 732: case 2: 733: mp->flen = Out_arg.i2width; 734: break; 735: 736: case 4: 737: mp->flen = Out_arg.i4width; 738: } 739: break; 740: 741: case FLOAT: 742: if (mp->rlen == 4) 743: mp->flen = Out_arg.f4width; 744: else 745: mp->flen = Out_arg.f8width; 746: } 747: } 748: /* if this is a copy "from" then break 749: otherwise continue. In a copy "into" 750: an attribute might be copied more than once */ 751: if (!Into) 752: break; 753: } 754: } 755: if (i < 0) 756: syserr("bad get from att-rel %d", i); 757: 758: /* check that all user domains have been identified */ 759: cnt = 0; 760: mp = Map; 761: for (i = Mapcount; i--; mp++) 762: { 763: cnt += mp->flen; 764: if (mp->ftype == DUMMY) 765: continue; 766: if (!mp->used) 767: { 768: return (error(5801, mp->paramname, Relname, 0)); /* unrecognizable domain name */ 769: } 770: } 771: /* check that copy into doesn't exceed buffer size */ 772: if (Into && cnt > BUFSIZE) 773: return (error(5817, 0)); /* cnt too large */ 774: return (0); 775: } 776: 777: 778: bread(mp) 779: struct map *mp; 780: { 781: register int count, i; 782: register char *inp; 783: char *dl; 784: int esc; /* escape flag */ 785: 786: count = mp->flen; 787: inp = Inbuf; 788: 789: if (count) 790: { 791: /* block mode. read characters */ 792: i = fread(inp, 1, count, File_iop); 793: 794: /* null terminate */ 795: *(inp + count) = '\0'; 796: 797: return (i == count); /* true -> normal, false ->eof */ 798: } 799: 800: /* string mode read */ 801: /* 802: ** Determine the maximum size the C0 field being read can be. 803: ** In the case where it is being copied into a CHAR field, then 804: ** the size is that of the char field (+1 for the delimitor). 805: ** In the case of a numeric, it is limited only by the size of the 806: ** buffer area. 807: */ 808: count = mp->rtype == CHAR ? mp->rlen + 1 : BUFSIZE; 809: esc = FALSE; 810: 811: for (;;) 812: { 813: if ((i = getc(File_iop)) == EOF) 814: return (inp == Inbuf ? 0 : -1); /* -1 -> unexpected EOF, 0 -> normal EOF */ 815: 816: if (count > 0) 817: { 818: count--; 819: *inp++ = i; 820: } 821: else 822: { 823: if (count == 0) 824: { 825: /* determine type of overflow */ 826: if (mp->rtype == CHAR) 827: { 828: Truncount++; 829: count--; /* read until delim */ 830: } 831: else 832: { 833: return (-1); 834: } 835: } 836: } 837: if (esc) 838: { 839: esc = FALSE; 840: continue; 841: } 842: if (i == ESCAPE) 843: { 844: esc = TRUE; 845: /* 846: ** If esc was stored, back it up. 847: */ 848: if (count >= 0) 849: { 850: inp--; /* remove escape char */ 851: count++; /* restore counter */ 852: } 853: } 854: else 855: { 856: for (dl = mp->fdelim; *dl; dl++) 857: if (*dl == i) 858: { 859: *(inp-1) = '\0'; 860: return (1); 861: } 862: } 863: } 864: } 865: 866: 867: char *zcheck(param) 868: char *param; 869: 870: /* 871: ** Look for the existence of a param of the 872: ** form "0nl" or "00comma" etc. 873: ** 874: ** Returns the correct delim list or 0 875: ** if there was a user error 876: ** 877: ** If successful, a null is inserted at the 878: ** rightmost '0' so the subsequent atoi will work. 879: */ 880: 881: { 882: register char *np, *ret; 883: char *dumvalue(); 884: 885: np = param; 886: ret = Delimitor; /* assume default delimitors */ 887: 888: if (*np++ == '0') 889: { 890: 891: /* we have a starting zero. trim the rest */ 892: while (*np == '0') 893: np++; 894: 895: if (*np > '9' || (*np < '0' && *np >= ' ')) 896: { 897: /* we have a special delim on a 0 width field */ 898: if (ret = dumvalue(np)) 899: *(--np) = '\0'; /* 900: ** end string before delim 901: ** Do not alter delimitor but 902: ** instead destroy last '0'. 903: */ 904: } 905: } 906: return (ret); 907: } 908: 909: 910: char *dumvalue(name) 911: char *name; 912: 913: /* 914: ** Search list of valid dummy names looking 915: ** for 'name'. If 'name' is a single char 916: ** then use just that name else it is 917: ** an error if the name is not found 918: */ 919: 920: { 921: register char **dp, *np, *ret; 922: 923: dp = Cpdomains; /* get list of valid dummy names */ 924: np = name; 925: ret = 0; 926: 927: /* first look for a matching key word */ 928: while (*dp) 929: { 930: if (sequal(np, *dp++)) 931: { 932: ret = *dp; 933: break; 934: } 935: dp++; 936: } 937: 938: /* If single char, use that char */ 939: if (length(np) == 1) 940: ret = np; /* use first char of name */ 941: if (ret == 0) 942: error(5807, np, 0); 943: 944: return (ret); 945: }