diff options
Diffstat (limited to 'do/unpack')
-rw-r--r-- | do/unpack | 561 |
1 files changed, 561 insertions, 0 deletions
diff --git a/do/unpack b/do/unpack new file mode 100644 index 0000000000..81cca11656 --- /dev/null +++ b/do/unpack @@ -0,0 +1,561 @@ +int +do_unpack(TARG,gimme,arglast) +STR *TARG; +int gimme; +int *arglast; +{ + STR **st = stack->ary_array; + register int sp = arglast[0] + 1; + register char *pat = str_get(st[sp++]); + register char *s = str_get(st[sp]); + char *strend = s + st[sp--]->str_cur; + char *strbeg = s; + register char *patend = pat + st[sp]->str_cur; + int datumtype; + register int len; + register int bits; + + /* These must not be in registers: */ + short ashort; + int aint; + long along; +#ifdef QUAD + quad aquad; +#endif + unsigned short aushort; + unsigned int auint; + unsigned long aulong; +#ifdef QUAD + unsigned quad auquad; +#endif + char *aptr; + float afloat; + double adouble; + int checksum = 0; + unsigned long culong; + double cdouble; + + if (gimme != G_ARRAY) { /* arrange to do first one only */ + /*SUPPRESS 530*/ + for (patend = pat; !isALPHA(*patend) || *patend == 'x'; patend++) ; + if (index("aAbBhH", *patend) || *pat == '%') { + patend++; + while (isDIGIT(*patend) || *patend == '*') + patend++; + } + else + patend++; + } + sp--; + while (pat < patend) { + reparse: + datumtype = *pat++; + if (pat >= patend) + len = 1; + else if (*pat == '*') { + len = strend - strbeg; /* long enough */ + pat++; + } + else if (isDIGIT(*pat)) { + len = *pat++ - '0'; + while (isDIGIT(*pat)) + len = (len * 10) + (*pat++ - '0'); + } + else + len = (datumtype != '@'); + switch(datumtype) { + default: + break; + case '%': + if (len == 1 && pat[-1] != '1') + len = 16; + checksum = len; + culong = 0; + cdouble = 0; + if (pat < patend) + goto reparse; + break; + case '@': + if (len > strend - strbeg) + fatal("@ outside of string"); + s = strbeg + len; + break; + case 'X': + if (len > s - strbeg) + fatal("X outside of string"); + s -= len; + break; + case 'x': + if (len > strend - s) + fatal("x outside of string"); + s += len; + break; + case 'A': + case 'a': + if (len > strend - s) + len = strend - s; + if (checksum) + goto uchar_checksum; + TARG = Str_new(35,len); + str_nset(TARG,s,len); + s += len; + if (datumtype == 'A') { + aptr = s; /* borrow register */ + s = TARG->str_ptr + len - 1; + while (s >= TARG->str_ptr && (!*s || isSPACE(*s))) + s--; + *++s = '\0'; + TARG->str_cur = s - TARG->str_ptr; + s = aptr; /* unborrow register */ + } + (void)astore(stack, ++sp, str_2mortal(TARG)); + break; + case 'B': + case 'b': + if (pat[-1] == '*' || len > (strend - s) * 8) + len = (strend - s) * 8; + TARG = Str_new(35, len + 1); + TARG->str_cur = len; + TARG->str_pok = 1; + aptr = pat; /* borrow register */ + pat = TARG->str_ptr; + if (datumtype == 'b') { + aint = len; + for (len = 0; len < aint; len++) { + if (len & 7) /*SUPPRESS 595*/ + bits >>= 1; + else + bits = *s++; + *pat++ = '0' + (bits & 1); + } + } + else { + aint = len; + for (len = 0; len < aint; len++) { + if (len & 7) + bits <<= 1; + else + bits = *s++; + *pat++ = '0' + ((bits & 128) != 0); + } + } + *pat = '\0'; + pat = aptr; /* unborrow register */ + (void)astore(stack, ++sp, str_2mortal(TARG)); + break; + case 'H': + case 'h': + if (pat[-1] == '*' || len > (strend - s) * 2) + len = (strend - s) * 2; + TARG = Str_new(35, len + 1); + TARG->str_cur = len; + TARG->str_pok = 1; + aptr = pat; /* borrow register */ + pat = TARG->str_ptr; + if (datumtype == 'h') { + aint = len; + for (len = 0; len < aint; len++) { + if (len & 1) + bits >>= 4; + else + bits = *s++; + *pat++ = hexdigit[bits & 15]; + } + } + else { + aint = len; + for (len = 0; len < aint; len++) { + if (len & 1) + bits <<= 4; + else + bits = *s++; + *pat++ = hexdigit[(bits >> 4) & 15]; + } + } + *pat = '\0'; + pat = aptr; /* unborrow register */ + (void)astore(stack, ++sp, str_2mortal(TARG)); + break; + case 'c': + if (len > strend - s) + len = strend - s; + if (checksum) { + while (len-- > 0) { + aint = *s++; + if (aint >= 128) /* fake up signed chars */ + aint -= 256; + culong += aint; + } + } + else { + while (len-- > 0) { + aint = *s++; + if (aint >= 128) /* fake up signed chars */ + aint -= 256; + TARG = Str_new(36,0); + str_numset(TARG,(double)aint); + (void)astore(stack, ++sp, str_2mortal(TARG)); + } + } + break; + case 'C': + if (len > strend - s) + len = strend - s; + if (checksum) { + uchar_checksum: + while (len-- > 0) { + auint = *s++ & 255; + culong += auint; + } + } + else { + while (len-- > 0) { + auint = *s++ & 255; + TARG = Str_new(37,0); + str_numset(TARG,(double)auint); + (void)astore(stack, ++sp, str_2mortal(TARG)); + } + } + break; + case 's': + along = (strend - s) / sizeof(short); + if (len > along) + len = along; + if (checksum) { + while (len-- > 0) { + Copy(s,&ashort,1,short); + s += sizeof(short); + culong += ashort; + } + } + else { + while (len-- > 0) { + Copy(s,&ashort,1,short); + s += sizeof(short); + TARG = Str_new(38,0); + str_numset(TARG,(double)ashort); + (void)astore(stack, ++sp, str_2mortal(TARG)); + } + } + break; + case 'v': + case 'n': + case 'S': + along = (strend - s) / sizeof(unsigned short); + if (len > along) + len = along; + if (checksum) { + while (len-- > 0) { + Copy(s,&aushort,1,unsigned short); + s += sizeof(unsigned short); +#ifdef HAS_NTOHS + if (datumtype == 'n') + aushort = ntohs(aushort); +#endif +#ifdef HAS_VTOHS + if (datumtype == 'v') + aushort = vtohs(aushort); +#endif + culong += aushort; + } + } + else { + while (len-- > 0) { + Copy(s,&aushort,1,unsigned short); + s += sizeof(unsigned short); + TARG = Str_new(39,0); +#ifdef HAS_NTOHS + if (datumtype == 'n') + aushort = ntohs(aushort); +#endif +#ifdef HAS_VTOHS + if (datumtype == 'v') + aushort = vtohs(aushort); +#endif + str_numset(TARG,(double)aushort); + (void)astore(stack, ++sp, str_2mortal(TARG)); + } + } + break; + case 'i': + along = (strend - s) / sizeof(int); + if (len > along) + len = along; + if (checksum) { + while (len-- > 0) { + Copy(s,&aint,1,int); + s += sizeof(int); + if (checksum > 32) + cdouble += (double)aint; + else + culong += aint; + } + } + else { + while (len-- > 0) { + Copy(s,&aint,1,int); + s += sizeof(int); + TARG = Str_new(40,0); + str_numset(TARG,(double)aint); + (void)astore(stack, ++sp, str_2mortal(TARG)); + } + } + break; + case 'I': + along = (strend - s) / sizeof(unsigned int); + if (len > along) + len = along; + if (checksum) { + while (len-- > 0) { + Copy(s,&auint,1,unsigned int); + s += sizeof(unsigned int); + if (checksum > 32) + cdouble += (double)auint; + else + culong += auint; + } + } + else { + while (len-- > 0) { + Copy(s,&auint,1,unsigned int); + s += sizeof(unsigned int); + TARG = Str_new(41,0); + str_numset(TARG,(double)auint); + (void)astore(stack, ++sp, str_2mortal(TARG)); + } + } + break; + case 'l': + along = (strend - s) / sizeof(long); + if (len > along) + len = along; + if (checksum) { + while (len-- > 0) { + Copy(s,&along,1,long); + s += sizeof(long); + if (checksum > 32) + cdouble += (double)along; + else + culong += along; + } + } + else { + while (len-- > 0) { + Copy(s,&along,1,long); + s += sizeof(long); + TARG = Str_new(42,0); + str_numset(TARG,(double)along); + (void)astore(stack, ++sp, str_2mortal(TARG)); + } + } + break; + case 'V': + case 'N': + case 'L': + along = (strend - s) / sizeof(unsigned long); + if (len > along) + len = along; + if (checksum) { + while (len-- > 0) { + Copy(s,&aulong,1,unsigned long); + s += sizeof(unsigned long); +#ifdef HAS_NTOHL + if (datumtype == 'N') + aulong = ntohl(aulong); +#endif +#ifdef HAS_VTOHL + if (datumtype == 'V') + aulong = vtohl(aulong); +#endif + if (checksum > 32) + cdouble += (double)aulong; + else + culong += aulong; + } + } + else { + while (len-- > 0) { + Copy(s,&aulong,1,unsigned long); + s += sizeof(unsigned long); + TARG = Str_new(43,0); +#ifdef HAS_NTOHL + if (datumtype == 'N') + aulong = ntohl(aulong); +#endif +#ifdef HAS_VTOHL + if (datumtype == 'V') + aulong = vtohl(aulong); +#endif + str_numset(TARG,(double)aulong); + (void)astore(stack, ++sp, str_2mortal(TARG)); + } + } + break; + case 'p': + along = (strend - s) / sizeof(char*); + if (len > along) + len = along; + while (len-- > 0) { + if (sizeof(char*) > strend - s) + break; + else { + Copy(s,&aptr,1,char*); + s += sizeof(char*); + } + TARG = Str_new(44,0); + if (aptr) + str_set(TARG,aptr); + (void)astore(stack, ++sp, str_2mortal(TARG)); + } + break; +#ifdef QUAD + case 'q': + while (len-- > 0) { + if (s + sizeof(quad) > strend) + aquad = 0; + else { + Copy(s,&aquad,1,quad); + s += sizeof(quad); + } + TARG = Str_new(42,0); + str_numset(TARG,(double)aquad); + (void)astore(stack, ++sp, str_2mortal(TARG)); + } + break; + case 'Q': + while (len-- > 0) { + if (s + sizeof(unsigned quad) > strend) + auquad = 0; + else { + Copy(s,&auquad,1,unsigned quad); + s += sizeof(unsigned quad); + } + TARG = Str_new(43,0); + str_numset(TARG,(double)auquad); + (void)astore(stack, ++sp, str_2mortal(TARG)); + } + break; +#endif + /* float and double added gnb@melba.bby.oz.au 22/11/89 */ + case 'f': + case 'F': + along = (strend - s) / sizeof(float); + if (len > along) + len = along; + if (checksum) { + while (len-- > 0) { + Copy(s, &afloat,1, float); + s += sizeof(float); + cdouble += afloat; + } + } + else { + while (len-- > 0) { + Copy(s, &afloat,1, float); + s += sizeof(float); + TARG = Str_new(47, 0); + str_numset(TARG, (double)afloat); + (void)astore(stack, ++sp, str_2mortal(TARG)); + } + } + break; + case 'd': + case 'D': + along = (strend - s) / sizeof(double); + if (len > along) + len = along; + if (checksum) { + while (len-- > 0) { + Copy(s, &adouble,1, double); + s += sizeof(double); + cdouble += adouble; + } + } + else { + while (len-- > 0) { + Copy(s, &adouble,1, double); + s += sizeof(double); + TARG = Str_new(48, 0); + str_numset(TARG, (double)adouble); + (void)astore(stack, ++sp, str_2mortal(TARG)); + } + } + break; + case 'u': + along = (strend - s) * 3 / 4; + TARG = Str_new(42,along); + while (s < strend && *s > ' ' && *s < 'a') { + int a,b,c,d; + char hunk[4]; + + hunk[3] = '\0'; + len = (*s++ - ' ') & 077; + while (len > 0) { + if (s < strend && *s >= ' ') + a = (*s++ - ' ') & 077; + else + a = 0; + if (s < strend && *s >= ' ') + b = (*s++ - ' ') & 077; + else + b = 0; + if (s < strend && *s >= ' ') + c = (*s++ - ' ') & 077; + else + c = 0; + if (s < strend && *s >= ' ') + d = (*s++ - ' ') & 077; + else + d = 0; + hunk[0] = a << 2 | b >> 4; + hunk[1] = b << 4 | c >> 2; + hunk[2] = c << 6 | d; + str_ncat(TARG,hunk, len > 3 ? 3 : len); + len -= 3; + } + if (*s == '\n') + s++; + else if (s[1] == '\n') /* possible checksum byte */ + s += 2; + } + (void)astore(stack, ++sp, str_2mortal(TARG)); + break; + } + if (checksum) { + TARG = Str_new(42,0); + if (index("fFdD", datumtype) || + (checksum > 32 && index("iIlLN", datumtype)) ) { + double modf(); + double trouble; + + adouble = 1.0; + while (checksum >= 16) { + checksum -= 16; + adouble *= 65536.0; + } + while (checksum >= 4) { + checksum -= 4; + adouble *= 16.0; + } + while (checksum--) + adouble *= 2.0; + along = (1 << checksum) - 1; + while (cdouble < 0.0) + cdouble += adouble; + cdouble = modf(cdouble / adouble, &trouble) * adouble; + str_numset(TARG,cdouble); + } + else { + if (checksum < 32) { + along = (1 << checksum) - 1; + culong &= (unsigned long)along; + } + str_numset(TARG,(double)culong); + } + (void)astore(stack, ++sp, str_2mortal(TARG)); + checksum = 0; + } + } + return sp; +} + |