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; }