81cca1165649a04f6bc735a69e7583fbc64b27b6
[perl.git] / do / unpack
1 int
2 do_unpack(TARG,gimme,arglast)
3 STR *TARG;
4 int gimme;
5 int *arglast;
6 {
7     STR **st = stack->ary_array;
8     register int sp = arglast[0] + 1;
9     register char *pat = str_get(st[sp++]);
10     register char *s = str_get(st[sp]);
11     char *strend = s + st[sp--]->str_cur;
12     char *strbeg = s;
13     register char *patend = pat + st[sp]->str_cur;
14     int datumtype;
15     register int len;
16     register int bits;
17
18     /* These must not be in registers: */
19     short ashort;
20     int aint;
21     long along;
22 #ifdef QUAD
23     quad aquad;
24 #endif
25     unsigned short aushort;
26     unsigned int auint;
27     unsigned long aulong;
28 #ifdef QUAD
29     unsigned quad auquad;
30 #endif
31     char *aptr;
32     float afloat;
33     double adouble;
34     int checksum = 0;
35     unsigned long culong;
36     double cdouble;
37
38     if (gimme != G_ARRAY) {             /* arrange to do first one only */
39         /*SUPPRESS 530*/
40         for (patend = pat; !isALPHA(*patend) || *patend == 'x'; patend++) ;
41         if (index("aAbBhH", *patend) || *pat == '%') {
42             patend++;
43             while (isDIGIT(*patend) || *patend == '*')
44                 patend++;
45         }
46         else
47             patend++;
48     }
49     sp--;
50     while (pat < patend) {
51       reparse:
52         datumtype = *pat++;
53         if (pat >= patend)
54             len = 1;
55         else if (*pat == '*') {
56             len = strend - strbeg;      /* long enough */
57             pat++;
58         }
59         else if (isDIGIT(*pat)) {
60             len = *pat++ - '0';
61             while (isDIGIT(*pat))
62                 len = (len * 10) + (*pat++ - '0');
63         }
64         else
65             len = (datumtype != '@');
66         switch(datumtype) {
67         default:
68             break;
69         case '%':
70             if (len == 1 && pat[-1] != '1')
71                 len = 16;
72             checksum = len;
73             culong = 0;
74             cdouble = 0;
75             if (pat < patend)
76                 goto reparse;
77             break;
78         case '@':
79             if (len > strend - strbeg)
80                 fatal("@ outside of string");
81             s = strbeg + len;
82             break;
83         case 'X':
84             if (len > s - strbeg)
85                 fatal("X outside of string");
86             s -= len;
87             break;
88         case 'x':
89             if (len > strend - s)
90                 fatal("x outside of string");
91             s += len;
92             break;
93         case 'A':
94         case 'a':
95             if (len > strend - s)
96                 len = strend - s;
97             if (checksum)
98                 goto uchar_checksum;
99             TARG = Str_new(35,len);
100             str_nset(TARG,s,len);
101             s += len;
102             if (datumtype == 'A') {
103                 aptr = s;       /* borrow register */
104                 s = TARG->str_ptr + len - 1;
105                 while (s >= TARG->str_ptr && (!*s || isSPACE(*s)))
106                     s--;
107                 *++s = '\0';
108                 TARG->str_cur = s - TARG->str_ptr;
109                 s = aptr;       /* unborrow register */
110             }
111             (void)astore(stack, ++sp, str_2mortal(TARG));
112             break;
113         case 'B':
114         case 'b':
115             if (pat[-1] == '*' || len > (strend - s) * 8)
116                 len = (strend - s) * 8;
117             TARG = Str_new(35, len + 1);
118             TARG->str_cur = len;
119             TARG->str_pok = 1;
120             aptr = pat;                 /* borrow register */
121             pat = TARG->str_ptr;
122             if (datumtype == 'b') {
123                 aint = len;
124                 for (len = 0; len < aint; len++) {
125                     if (len & 7)                /*SUPPRESS 595*/
126                         bits >>= 1;
127                     else
128                         bits = *s++;
129                     *pat++ = '0' + (bits & 1);
130                 }
131             }
132             else {
133                 aint = len;
134                 for (len = 0; len < aint; len++) {
135                     if (len & 7)
136                         bits <<= 1;
137                     else
138                         bits = *s++;
139                     *pat++ = '0' + ((bits & 128) != 0);
140                 }
141             }
142             *pat = '\0';
143             pat = aptr;                 /* unborrow register */
144             (void)astore(stack, ++sp, str_2mortal(TARG));
145             break;
146         case 'H':
147         case 'h':
148             if (pat[-1] == '*' || len > (strend - s) * 2)
149                 len = (strend - s) * 2;
150             TARG = Str_new(35, len + 1);
151             TARG->str_cur = len;
152             TARG->str_pok = 1;
153             aptr = pat;                 /* borrow register */
154             pat = TARG->str_ptr;
155             if (datumtype == 'h') {
156                 aint = len;
157                 for (len = 0; len < aint; len++) {
158                     if (len & 1)
159                         bits >>= 4;
160                     else
161                         bits = *s++;
162                     *pat++ = hexdigit[bits & 15];
163                 }
164             }
165             else {
166                 aint = len;
167                 for (len = 0; len < aint; len++) {
168                     if (len & 1)
169                         bits <<= 4;
170                     else
171                         bits = *s++;
172                     *pat++ = hexdigit[(bits >> 4) & 15];
173                 }
174             }
175             *pat = '\0';
176             pat = aptr;                 /* unborrow register */
177             (void)astore(stack, ++sp, str_2mortal(TARG));
178             break;
179         case 'c':
180             if (len > strend - s)
181                 len = strend - s;
182             if (checksum) {
183                 while (len-- > 0) {
184                     aint = *s++;
185                     if (aint >= 128)    /* fake up signed chars */
186                         aint -= 256;
187                     culong += aint;
188                 }
189             }
190             else {
191                 while (len-- > 0) {
192                     aint = *s++;
193                     if (aint >= 128)    /* fake up signed chars */
194                         aint -= 256;
195                     TARG = Str_new(36,0);
196                     str_numset(TARG,(double)aint);
197                     (void)astore(stack, ++sp, str_2mortal(TARG));
198                 }
199             }
200             break;
201         case 'C':
202             if (len > strend - s)
203                 len = strend - s;
204             if (checksum) {
205               uchar_checksum:
206                 while (len-- > 0) {
207                     auint = *s++ & 255;
208                     culong += auint;
209                 }
210             }
211             else {
212                 while (len-- > 0) {
213                     auint = *s++ & 255;
214                     TARG = Str_new(37,0);
215                     str_numset(TARG,(double)auint);
216                     (void)astore(stack, ++sp, str_2mortal(TARG));
217                 }
218             }
219             break;
220         case 's':
221             along = (strend - s) / sizeof(short);
222             if (len > along)
223                 len = along;
224             if (checksum) {
225                 while (len-- > 0) {
226                     Copy(s,&ashort,1,short);
227                     s += sizeof(short);
228                     culong += ashort;
229                 }
230             }
231             else {
232                 while (len-- > 0) {
233                     Copy(s,&ashort,1,short);
234                     s += sizeof(short);
235                     TARG = Str_new(38,0);
236                     str_numset(TARG,(double)ashort);
237                     (void)astore(stack, ++sp, str_2mortal(TARG));
238                 }
239             }
240             break;
241         case 'v':
242         case 'n':
243         case 'S':
244             along = (strend - s) / sizeof(unsigned short);
245             if (len > along)
246                 len = along;
247             if (checksum) {
248                 while (len-- > 0) {
249                     Copy(s,&aushort,1,unsigned short);
250                     s += sizeof(unsigned short);
251 #ifdef HAS_NTOHS
252                     if (datumtype == 'n')
253                         aushort = ntohs(aushort);
254 #endif
255 #ifdef HAS_VTOHS
256                     if (datumtype == 'v')
257                         aushort = vtohs(aushort);
258 #endif
259                     culong += aushort;
260                 }
261             }
262             else {
263                 while (len-- > 0) {
264                     Copy(s,&aushort,1,unsigned short);
265                     s += sizeof(unsigned short);
266                     TARG = Str_new(39,0);
267 #ifdef HAS_NTOHS
268                     if (datumtype == 'n')
269                         aushort = ntohs(aushort);
270 #endif
271 #ifdef HAS_VTOHS
272                     if (datumtype == 'v')
273                         aushort = vtohs(aushort);
274 #endif
275                     str_numset(TARG,(double)aushort);
276                     (void)astore(stack, ++sp, str_2mortal(TARG));
277                 }
278             }
279             break;
280         case 'i':
281             along = (strend - s) / sizeof(int);
282             if (len > along)
283                 len = along;
284             if (checksum) {
285                 while (len-- > 0) {
286                     Copy(s,&aint,1,int);
287                     s += sizeof(int);
288                     if (checksum > 32)
289                         cdouble += (double)aint;
290                     else
291                         culong += aint;
292                 }
293             }
294             else {
295                 while (len-- > 0) {
296                     Copy(s,&aint,1,int);
297                     s += sizeof(int);
298                     TARG = Str_new(40,0);
299                     str_numset(TARG,(double)aint);
300                     (void)astore(stack, ++sp, str_2mortal(TARG));
301                 }
302             }
303             break;
304         case 'I':
305             along = (strend - s) / sizeof(unsigned int);
306             if (len > along)
307                 len = along;
308             if (checksum) {
309                 while (len-- > 0) {
310                     Copy(s,&auint,1,unsigned int);
311                     s += sizeof(unsigned int);
312                     if (checksum > 32)
313                         cdouble += (double)auint;
314                     else
315                         culong += auint;
316                 }
317             }
318             else {
319                 while (len-- > 0) {
320                     Copy(s,&auint,1,unsigned int);
321                     s += sizeof(unsigned int);
322                     TARG = Str_new(41,0);
323                     str_numset(TARG,(double)auint);
324                     (void)astore(stack, ++sp, str_2mortal(TARG));
325                 }
326             }
327             break;
328         case 'l':
329             along = (strend - s) / sizeof(long);
330             if (len > along)
331                 len = along;
332             if (checksum) {
333                 while (len-- > 0) {
334                     Copy(s,&along,1,long);
335                     s += sizeof(long);
336                     if (checksum > 32)
337                         cdouble += (double)along;
338                     else
339                         culong += along;
340                 }
341             }
342             else {
343                 while (len-- > 0) {
344                     Copy(s,&along,1,long);
345                     s += sizeof(long);
346                     TARG = Str_new(42,0);
347                     str_numset(TARG,(double)along);
348                     (void)astore(stack, ++sp, str_2mortal(TARG));
349                 }
350             }
351             break;
352         case 'V':
353         case 'N':
354         case 'L':
355             along = (strend - s) / sizeof(unsigned long);
356             if (len > along)
357                 len = along;
358             if (checksum) {
359                 while (len-- > 0) {
360                     Copy(s,&aulong,1,unsigned long);
361                     s += sizeof(unsigned long);
362 #ifdef HAS_NTOHL
363                     if (datumtype == 'N')
364                         aulong = ntohl(aulong);
365 #endif
366 #ifdef HAS_VTOHL
367                     if (datumtype == 'V')
368                         aulong = vtohl(aulong);
369 #endif
370                     if (checksum > 32)
371                         cdouble += (double)aulong;
372                     else
373                         culong += aulong;
374                 }
375             }
376             else {
377                 while (len-- > 0) {
378                     Copy(s,&aulong,1,unsigned long);
379                     s += sizeof(unsigned long);
380                     TARG = Str_new(43,0);
381 #ifdef HAS_NTOHL
382                     if (datumtype == 'N')
383                         aulong = ntohl(aulong);
384 #endif
385 #ifdef HAS_VTOHL
386                     if (datumtype == 'V')
387                         aulong = vtohl(aulong);
388 #endif
389                     str_numset(TARG,(double)aulong);
390                     (void)astore(stack, ++sp, str_2mortal(TARG));
391                 }
392             }
393             break;
394         case 'p':
395             along = (strend - s) / sizeof(char*);
396             if (len > along)
397                 len = along;
398             while (len-- > 0) {
399                 if (sizeof(char*) > strend - s)
400                     break;
401                 else {
402                     Copy(s,&aptr,1,char*);
403                     s += sizeof(char*);
404                 }
405                 TARG = Str_new(44,0);
406                 if (aptr)
407                     str_set(TARG,aptr);
408                 (void)astore(stack, ++sp, str_2mortal(TARG));
409             }
410             break;
411 #ifdef QUAD
412         case 'q':
413             while (len-- > 0) {
414                 if (s + sizeof(quad) > strend)
415                     aquad = 0;
416                 else {
417                     Copy(s,&aquad,1,quad);
418                     s += sizeof(quad);
419                 }
420                 TARG = Str_new(42,0);
421                 str_numset(TARG,(double)aquad);
422                 (void)astore(stack, ++sp, str_2mortal(TARG));
423             }
424             break;
425         case 'Q':
426             while (len-- > 0) {
427                 if (s + sizeof(unsigned quad) > strend)
428                     auquad = 0;
429                 else {
430                     Copy(s,&auquad,1,unsigned quad);
431                     s += sizeof(unsigned quad);
432                 }
433                 TARG = Str_new(43,0);
434                 str_numset(TARG,(double)auquad);
435                 (void)astore(stack, ++sp, str_2mortal(TARG));
436             }
437             break;
438 #endif
439         /* float and double added gnb@melba.bby.oz.au 22/11/89 */
440         case 'f':
441         case 'F':
442             along = (strend - s) / sizeof(float);
443             if (len > along)
444                 len = along;
445             if (checksum) {
446                 while (len-- > 0) {
447                     Copy(s, &afloat,1, float);
448                     s += sizeof(float);
449                     cdouble += afloat;
450                 }
451             }
452             else {
453                 while (len-- > 0) {
454                     Copy(s, &afloat,1, float);
455                     s += sizeof(float);
456                     TARG = Str_new(47, 0);
457                     str_numset(TARG, (double)afloat);
458                     (void)astore(stack, ++sp, str_2mortal(TARG));
459                 }
460             }
461             break;
462         case 'd':
463         case 'D':
464             along = (strend - s) / sizeof(double);
465             if (len > along)
466                 len = along;
467             if (checksum) {
468                 while (len-- > 0) {
469                     Copy(s, &adouble,1, double);
470                     s += sizeof(double);
471                     cdouble += adouble;
472                 }
473             }
474             else {
475                 while (len-- > 0) {
476                     Copy(s, &adouble,1, double);
477                     s += sizeof(double);
478                     TARG = Str_new(48, 0);
479                     str_numset(TARG, (double)adouble);
480                     (void)astore(stack, ++sp, str_2mortal(TARG));
481                 }
482             }
483             break;
484         case 'u':
485             along = (strend - s) * 3 / 4;
486             TARG = Str_new(42,along);
487             while (s < strend && *s > ' ' && *s < 'a') {
488                 int a,b,c,d;
489                 char hunk[4];
490
491                 hunk[3] = '\0';
492                 len = (*s++ - ' ') & 077;
493                 while (len > 0) {
494                     if (s < strend && *s >= ' ')
495                         a = (*s++ - ' ') & 077;
496                     else
497                         a = 0;
498                     if (s < strend && *s >= ' ')
499                         b = (*s++ - ' ') & 077;
500                     else
501                         b = 0;
502                     if (s < strend && *s >= ' ')
503                         c = (*s++ - ' ') & 077;
504                     else
505                         c = 0;
506                     if (s < strend && *s >= ' ')
507                         d = (*s++ - ' ') & 077;
508                     else
509                         d = 0;
510                     hunk[0] = a << 2 | b >> 4;
511                     hunk[1] = b << 4 | c >> 2;
512                     hunk[2] = c << 6 | d;
513                     str_ncat(TARG,hunk, len > 3 ? 3 : len);
514                     len -= 3;
515                 }
516                 if (*s == '\n')
517                     s++;
518                 else if (s[1] == '\n')          /* possible checksum byte */
519                     s += 2;
520             }
521             (void)astore(stack, ++sp, str_2mortal(TARG));
522             break;
523         }
524         if (checksum) {
525             TARG = Str_new(42,0);
526             if (index("fFdD", datumtype) ||
527               (checksum > 32 && index("iIlLN", datumtype)) ) {
528                 double modf();
529                 double trouble;
530
531                 adouble = 1.0;
532                 while (checksum >= 16) {
533                     checksum -= 16;
534                     adouble *= 65536.0;
535                 }
536                 while (checksum >= 4) {
537                     checksum -= 4;
538                     adouble *= 16.0;
539                 }
540                 while (checksum--)
541                     adouble *= 2.0;
542                 along = (1 << checksum) - 1;
543                 while (cdouble < 0.0)
544                     cdouble += adouble;
545                 cdouble = modf(cdouble / adouble, &trouble) * adouble;
546                 str_numset(TARG,cdouble);
547             }
548             else {
549                 if (checksum < 32) {
550                     along = (1 << checksum) - 1;
551                     culong &= (unsigned long)along;
552                 }
553                 str_numset(TARG,(double)culong);
554             }
555             (void)astore(stack, ++sp, str_2mortal(TARG));
556             checksum = 0;
557         }
558     }
559     return sp;
560 }
561