ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Convert-UUlib/uulib/uunconc.c
Revision: 1.3
Committed: Sun Feb 10 22:47:18 2002 UTC (22 years, 3 months ago) by root
Content type: text/plain
Branch: MAIN
Branch point for: UUDEVIEW
Changes since 1.2: +56 -1 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 /*
2     * This file is part of uudeview, the simple and friendly multi-part multi-
3 root 1.2 * file uudecoder program (c) 1994-2001 by Frank Pilhofer. The author may
4     * be contacted at fp@fpx.de
5 root 1.1 *
6     * This program is free software; you can redistribute it and/or modify
7     * it under the terms of the GNU General Public License as published by
8     * the Free Software Foundation; either version 2 of the License, or
9     * (at your option) any later version.
10     *
11     * This program is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     */
16    
17     /*
18     * These are the functions that are responsible for decoding. The
19     * original idea is from a freeware utility called "uunconc", and
20     * few lines of this code may still bear a remote resemblance to
21     * its code. If you are the author or know him, contact me.
22     * This program could only decode one multi-part, uuencoded file
23     * where the parts were in order. Base64, XX and BinHex decoding,
24     * support for multi-files and part-ordering covered by myself.
25     **/
26    
27     #ifdef HAVE_CONFIG_H
28     #include "config.h"
29     #endif
30    
31     #ifdef SYSTEM_WINDLL
32     #include <windows.h>
33     #endif
34     #ifdef SYSTEM_OS2
35     #include <os2.h>
36     #endif
37    
38     #include <stdio.h>
39     #include <ctype.h>
40    
41     #ifdef STDC_HEADERS
42     #include <stdlib.h>
43     #include <string.h>
44     #endif
45     #ifdef HAVE_UNISTD_H
46     #include <unistd.h>
47     #endif
48     #ifdef HAVE_ERRNO_H
49     #include <errno.h>
50     #endif
51    
52 root 1.2 #include <uudeview.h>
53 root 1.1 #include <uuint.h>
54     #include <fptools.h>
55     #include <uustring.h>
56    
57 root 1.3 char * uunconc_id = "$Id: uunconc.c,v 1.2 2001/06/11 20:42:37 root Exp $";
58 root 1.1
59     /* for braindead systems */
60     #ifndef SEEK_SET
61     #ifdef L_BEGIN
62     #define SEEK_SET L_BEGIN
63     #else
64     #define SEEK_SET 0
65     #endif
66     #endif
67    
68     /*
69     * decoder states
70     */
71    
72     #define BEGIN (1)
73     #define DATA (2)
74     #define END (3)
75     #define DONE (4)
76    
77     /*
78     * mallocable areas
79     */
80    
81     char *uunconc_UUxlat;
82     char *uunconc_UUxlen;
83     char *uunconc_B64xlat;
84     char *uunconc_XXxlat;
85     char *uunconc_BHxlat;
86     char *uunconc_save;
87    
88     /*
89     * decoding translation tables and line length table
90     */
91    
92     static int * UUxlen; /* initialized in UUInitConc() */
93     static int * UUxlat; /* from the malloc'ed areas above */
94     static int * B64xlat;
95     static int * XXxlat;
96     static int * BHxlat;
97    
98     /*
99     * buffer for decoding
100     */
101    
102     static char *save[3];
103    
104     /*
105     * mallocable areas
106     */
107    
108     char *uuncdl_fulline;
109     char *uuncdp_oline;
110    
111     /*
112     * Return information for QuickDecode
113     */
114    
115     static int uulboundary;
116    
117     /*
118     * To prevent warnings when using a char as index into an array
119     */
120    
121     #define ACAST(s) ((int)(uchar)(s))
122    
123     /*
124     * Initialize decoding tables
125     */
126    
127     void
128     UUInitConc (void)
129     {
130     int i, j;
131    
132     /*
133     * Update pointers
134     */
135     UUxlen = (int *) uunconc_UUxlen;
136     UUxlat = (int *) uunconc_UUxlat;
137     B64xlat = (int *) uunconc_B64xlat;
138     XXxlat = (int *) uunconc_XXxlat;
139     BHxlat = (int *) uunconc_BHxlat;
140    
141     save[0] = uunconc_save;
142     save[1] = uunconc_save + 256;
143     save[2] = uunconc_save + 512;
144    
145     /* prepare decoding translation table */
146     for(i = 0; i < 256; i++)
147     UUxlat[i] = B64xlat[i] = XXxlat[i] = BHxlat[i] = -1;
148    
149     /*
150     * At some time I received a file which used lowercase characters for
151     * uuencoding. This shouldn't be, but let's accept it. Must take special
152     * care that this doesn't break xxdecoding. This is giving me quite a
153     * headache. If this one file hadn't been a Pocahontas picture, I might
154     * have ignored it for good.
155     */
156    
157     for (i = ' ', j = 0; i < ' ' + 64; i++, j++)
158     UUxlat[i] /* = UUxlat[i+64] */ = j;
159     for (i = '`', j = 0; i < '`' + 32; i++, j++)
160     UUxlat[i] = j;
161    
162     /* add special cases */
163     UUxlat['`'] = UUxlat[' '];
164     UUxlat['~'] = UUxlat['^'];
165    
166     /* prepare line length table */
167     UUxlen[0] = 1;
168 root 1.2 for(i = 1, j = 5; i <= 61; i += 3, j += 4)
169 root 1.1 UUxlen[i] = UUxlen[i+1] = UUxlen[i+2] = j;
170    
171     /* prepare other tables */
172     for (i=0; i<64; i++) {
173     B64xlat[ACAST(B64EncodeTable[i])] = i;
174     XXxlat [ACAST(XXEncodeTable [i])] = i;
175     BHxlat [ACAST(BHEncodeTable [i])] = i;
176     }
177     }
178    
179     /*
180     * Workaround for Netscape
181     */
182    
183     /*
184     * Determines whether Netscape may have broken up a data line (by
185     * inserting a newline). This only seems to happen after <a in a
186     * href statement
187     */
188    
189     int
190     UUBrokenByNetscape (char *string)
191     {
192     char *ptr;
193     int len;
194    
195     if (string==NULL || (len=strlen(string))<3)
196     return 0;
197    
198     if ((ptr = FP_stristr (string, "<a href=")) != NULL) {
199     if (FP_stristr (string, "</a>") > ptr)
200     return 2;
201     }
202    
203     ptr = string + len;
204    
205     while (len && (*(ptr-1)=='\015' || *(ptr-1)=='\012')) {
206     ptr--; len--;
207     }
208     if (len<3) return 0;
209     if (*--ptr == ' ') ptr--;
210     ptr--;
211    
212     if (FP_strnicmp (ptr, "<a", 2) == 0)
213     return 1;
214    
215     return 0;
216     }
217    
218     /*
219     * Try to repair a Netscape-corrupted line of data.
220     * This must only be called on corrupted lines, since non-Netscape
221     * data may even _get_ corrupted by this procedure.
222     *
223     * Some checks are included multiply to speed up the procedure. For
224     * example: (*p1!='<' || strnicmp(p1,"</a>",4)). If the first expression
225     * becomes true, the costly function isn't called :-)
226     *
227     * Since '<', '>', '&' might even be replaced by their html equivalents
228     * in href strings, I'm now using two passes, the first one for &amp; + co,
229     * the second one for hrefs.
230     */
231    
232     int
233     UUNetscapeCollapse (char *string)
234     {
235     char *p1=string, *p2=string;
236     int res = 0;
237    
238     if (string==NULL)
239     return 0;
240    
241     /*
242     * First pass
243     */
244     while (*p1) {
245     if (*p1 == '&') {
246 root 1.2 if (FP_strnicmp (p1, "&amp;", 5) == 0) { p1+=5; *p2++='&'; res=1; }
247     else if (FP_strnicmp (p1, "&lt;", 4) == 0) { p1+=4; *p2++='<'; res=1; }
248     else if (FP_strnicmp (p1, "&gt;", 4) == 0) { p1+=4; *p2++='>'; res=1; }
249 root 1.1 else *p2++ = *p1++;
250     res = 1;
251     }
252     else *p2++ = *p1++;
253     }
254     *p2 = '\0';
255     /*
256     * Second pass
257     */
258     p1 = p2 = string;
259    
260     while (*p1) {
261     if (*p1 == '<') {
262     if ((FP_strnicmp (p1, "<ahref=", 7) == 0 ||
263     FP_strnicmp (p1, "<a href=",8) == 0) &&
264     (FP_strstr (p1, "</a>") != 0 || FP_strstr (p1, "</A>") != 0)) {
265     while (*p1 && *p1!='>') p1++;
266     if (*p1=='\0' || *(p1+1)!='<') return 0;
267     p1++;
268     while (*p1 && (*p1!='<' || FP_strnicmp(p1,"</a>",4)!=0)) {
269     *p2++ = *p1++;
270     }
271     if (FP_strnicmp(p1,"</a>",4) != 0)
272     return 0;
273     p1+=4;
274     res=1;
275     }
276     else
277     *p2++ = *p1++;
278     }
279     else
280     *p2++ = *p1++;
281     }
282     *p2 = '\0';
283    
284     return res;
285     }
286    
287     /*
288     * The second parameter is 0 if we are still searching for encoded data,
289     * otherwise it indicates the encoding we're using right now. If we're
290     * still in the searching stage, we must be a little more strict in
291     * deciding for or against encoding; there's too much plain text looking
292     * like encoded data :-(
293     */
294    
295     int
296     UUValidData (char *ptr, int encoding, int *bhflag)
297     {
298     int i=0, j, len=0, suspicious=0, flag=0;
299     char *s = ptr;
300    
301     if ((s == NULL) || ((schar)*s < '\0')) {
302     return(0); /* bad string */
303     }
304    
305     while (*s && *s!='\012' && *s!='\015') {
306     s++;
307     len++;
308     i++;
309     }
310    
311     if (i == 0)
312     return 0;
313    
314     switch (encoding) {
315     case UU_ENCODED:
316     goto _t_UU;
317     case XX_ENCODED:
318     goto _t_XX;
319     case B64ENCODED:
320     goto _t_B64;
321     case BH_ENCODED:
322     goto _t_Binhex;
323     }
324    
325     _t_Binhex: /* Binhex Test */
326     len = i; s = ptr;
327    
328     /*
329     * bhflag notes the state we're in. Within the data, it's 1. If we're
330     * still looking for the initial :, it's 0
331     */
332     if (*bhflag == 0 && *s != ':') {
333     if (encoding==BH_ENCODED) return 0;
334     goto _t_B64;
335     }
336     else if (*bhflag == 0 /* *s == ':' */) {
337     s++; len--;
338     }
339    
340     while (len && BHxlat[ACAST(*s)] != -1) {
341     len--; s++;
342     }
343    
344     /* allow space characters at the end of the line if we are sure */
345     /* that this is Binhex encoded data or the line was long enough */
346    
347     flag = (*s == ':') ? 0 : 1;
348    
349     if (*s == ':' && len>0) {
350     s++; len--;
351     }
352     if (((i>=60 && len<=10) || encoding) && *s==' ') {
353     while (len && *s==' ') {
354     s++; len--;
355     }
356     }
357    
358     /*
359     * BinHex data shall have exactly 64 characters (except the last
360     * line). We ignore everything with less than 40 characters to
361     * be flexible
362     */
363    
364     if (len != 0 || (flag && i < 40)) {
365     if (encoding==BH_ENCODED) return 0;
366     goto _t_B64;
367     }
368    
369     *bhflag = flag;
370    
371     return BH_ENCODED;
372    
373     _t_B64: /* Base64 Test */
374     len = i; s = ptr;
375    
376     /*
377     * Face it: there _are_ Base64 lines that are not a multiple of four
378     * in length :-(
379     *
380     * if (len%4)
381     * goto _t_UU;
382     */
383    
384     while (len--) {
385     if (*s < 0 || (B64xlat[ACAST(*s)] == -1 && *s != '=')) {
386     /* allow space characters at the end of the line if we are sure */
387     /* that this is Base64 encoded data or the line was long enough */
388     if (((i>=60 && len<=10) || encoding) && *s++==' ') {
389     while (*s==' ' && len) s++;
390     if (len==0) return B64ENCODED;
391     }
392     if (encoding==B64ENCODED) return 0;
393     goto _t_UU;
394     }
395     else if (*s == '=') { /* special case at end */
396     /* if we know this is B64encoded, allow spaces at end of line */
397     s++;
398     if (*s=='=' && len>=1) {
399     len--; s++;
400     }
401     if (encoding && len && *s==' ') {
402     while (len && *s==' ') {
403     s++; len--;
404     }
405     }
406     if (len != 0) {
407     if (encoding==B64ENCODED) return 0;
408     goto _t_UU;
409     }
410     return B64ENCODED;
411     }
412     s++;
413     }
414     return B64ENCODED;
415    
416     _t_UU:
417     len = i; s = ptr;
418    
419     if (UUxlat[ACAST(*s)] == -1) { /* uutest */
420     if (encoding==UU_ENCODED) return 0;
421     goto _t_XX;
422     }
423    
424     j = UUxlen[UUxlat[ACAST(*s)]];
425    
426     if (len-1 == j) /* remove trailing character */
427     len--;
428     if (len != j) {
429     switch (UUxlat[ACAST(*s)]%3) {
430     case 1:
431     if (j-2 == len) j-=2;
432     break;
433     case 2:
434     if (j-1 == len) j-=1;
435     break;
436     }
437     }
438    
439     /*
440     * some encoders are broken with respect to encoding the last line of
441     * a file and produce extraoneous characters beyond the expected EOL
442     * So were not too picky here about the last line, as long as it's longer
443     * than necessary and shorter than the maximum
444     * this tolerance broke the xxdecoding, because xxencoded data was
445     * detected as being uuencoded :( so don't accept 'h' as first character
446     * also, if the first character is lowercase, don't accept the line to
447     * have space characters. the only encoder I've heard of which uses
448     * lowercase characters at least accepts the special case of encoding
449     * 0 as `. The strchr() shouldn't be too expensive here as it's only
450     * evaluated if the first character is lowercase, which really shouldn't
451     * be in uuencoded text.
452     */
453 root 1.2 if (len != j &&
454     ((ptr[0] == '-' && ptr[1] == '-' && strstr(ptr,"part")!=NULL) ||
455     !(*ptr != 'M' && *ptr != 'h' &&
456     len > j && len <= UUxlen[UUxlat['M']]))) {
457 root 1.1 if (encoding==UU_ENCODED) return 0;
458     goto _t_XX; /* bad length */
459     }
460    
461     if (len != j || islower (*ptr)) {
462     /*
463     * if we are not in a 'uuencoded' state, don't allow the line to have
464     * space characters at all. if we know we _are_ decoding uuencoded
465     * data, the rest of the line, beyond the length of encoded data, may
466     * have spaces.
467     */
468     if (encoding != UU_ENCODED)
469     if (strchr (ptr, ' ') != NULL)
470     goto _t_XX;
471    
472     /* suspicious = 1; we're careful here REMOVED 0.4.15 _FP__ */
473     len = j;
474     }
475    
476     while (len--) {
477     if (*s < 0 || UUxlat[ACAST(*s++)] < 0) {
478     if (encoding==UU_ENCODED) return 0;
479     goto _t_XX; /* bad code character */
480     }
481     if (*s == ' ' && suspicious) {
482     if (encoding==UU_ENCODED) return 0;
483     goto _t_XX; /* this line looks _too_ suspicious */
484     }
485     }
486     return UU_ENCODED; /* data is valid */
487    
488     _t_XX: /* XX Test */
489     len = i; s = ptr;
490    
491     if (XXxlat[ACAST(*s)] == -1)
492     return 0;
493    
494     j = UUxlen[XXxlat[ACAST(*s)]]; /* Same line length table as UUencoding */
495    
496     if (len-1 == j) /* remove trailing character */
497     len--;
498     if (len != j)
499     switch (UUxlat[ACAST(*s)]%3) {
500     case 1:
501     if (j-2 == len) j-=2;
502     break;
503     case 2:
504     if (j-1 == len) j-=1;
505     break;
506     }
507     /*
508     * some encoders are broken with respect to encoding the last line of
509     * a file and produce extraoneous characters beyond the expected EOL
510     * So were not too picky here about the last line, as long as it's longer
511     * than necessary and shorter than the maximum
512     */
513     if (len != j && !(*ptr != 'h' && len > j && len <= UUxlen[UUxlat['h']]))
514     return 0; /* bad length */
515    
516     while(len--) {
517     if(*s < 0 || XXxlat[ACAST(*s++)] < 0) {
518     return 0; /* bad code character */
519     }
520     }
521     return XX_ENCODED; /* data is valid */
522     }
523    
524     /*
525     * This function may be called upon a line that does not look like
526     * valid encoding on first sight, but might be erroneously encoded
527     * data from Netscape, Lynx or MS Exchange. We might need to read
528     * a new line from the stream, which is why we need the FILE.
529     * Returns the type of encoded data if successful or 0 otherwise.
530     */
531    
532     int
533     UURepairData (FILE *datei, char *line, int encoding, int *bhflag)
534     {
535     int nflag, vflag=0, safety=42;
536     char *ptr;
537    
538     nflag = UUBrokenByNetscape (line);
539    
540     while (vflag == 0 && nflag && safety--) {
541     if (nflag == 1) { /* need next line to repair */
542     ptr = line + strlen (line);
543     while (ptr>line && (*(ptr-1)=='\015' || *(ptr-1)=='\012'))
544     ptr--;
545     if (FP_fgets (ptr, 255-(ptr-line), datei) == NULL)
546     break;
547     }
548     else { /* don't need next line to repair */
549     }
550     if (UUNetscapeCollapse (line)) {
551     if ((vflag = UUValidData (line, encoding, bhflag)) == 0)
552     nflag = UUBrokenByNetscape (line);
553     }
554     else
555     nflag = 0;
556     }
557     /*
558     * Sometimes, a line is garbled even without it being split into
559     * the next line. Then we try this in our despair
560     */
561     if (vflag == 0) {
562     if (UUNetscapeCollapse (line))
563     vflag = UUValidData (line, encoding, bhflag);
564     }
565    
566     /*
567     * If this line looks uuencoded, but the line is one character short
568     * of a valid line, it was probably broken by MS Exchange. According
569     * to my test cases, there is at most one space character missing;
570     * there are never two spaces together.
571     * If adding a space character helps making this line uuencoded, do
572     * it!
573     */
574    
575     if (vflag == 0) {
576     ptr = line + strlen(line);
577     while (ptr>line && (*(ptr-1)=='\012' || *(ptr-1)=='\015')) {
578     ptr--;
579     }
580     *ptr++ = ' ';
581     *ptr-- = '\0';
582     if ((vflag = UUValidData (line, encoding, bhflag)) != UU_ENCODED) {
583     *ptr = '\0';
584     vflag = 0;
585     }
586     }
587     return vflag;
588     }
589    
590     /*
591     * Decode a single encoded line using method
592     */
593    
594     size_t
595     UUDecodeLine (char *s, char *d, int method)
596     {
597     int i, j, c, cc, count=0, z1, z2, z3, z4;
598     static int leftover=0;
599     int *table;
600    
601     /*
602     * for re-initialization
603     */
604    
605     if (s == NULL || d == NULL) {
606     leftover = 0;
607     return 0;
608     }
609    
610     /*
611     * To shut up gcc -Wall
612     */
613     z1 = z2 = z3 = z4 = 0;
614    
615     if (method == UU_ENCODED || method == XX_ENCODED) {
616     if (method == UU_ENCODED)
617     table = UUxlat;
618     else
619     table = XXxlat;
620    
621     i = table [(uchar)*s++];
622     j = UUxlen[i] - 1;
623    
624     while(j > 0) {
625     c = table[(uchar)*s++] << 2;
626     cc = table[(uchar)*s++];
627     c |= (cc >> 4);
628    
629     if(i-- > 0)
630     d[count++] = c;
631    
632     cc <<= 4;
633     c = table[(uchar)*s++];
634     cc |= (c >> 2);
635    
636     if(i-- > 0)
637     d[count++] = cc;
638    
639     c <<= 6;
640     c |= table[(uchar)*s++];
641    
642     if(i-- > 0)
643     d[count++] = c;
644    
645     j -= 4;
646     }
647     }
648     else if (method == B64ENCODED) {
649     if (leftover) {
650     strcpy (uuncdl_fulline+leftover, s);
651     leftover = 0;
652     s = uuncdl_fulline;
653     }
654    
655     while ((z1 = B64xlat[ACAST(*s)]) != -1) {
656     if ((z2 = B64xlat[ACAST(*(s+1))]) == -1) break;
657     if ((z3 = B64xlat[ACAST(*(s+2))]) == -1) break;
658     if ((z4 = B64xlat[ACAST(*(s+3))]) == -1) break;
659    
660     d[count++] = (z1 << 2) | (z2 >> 4);
661     d[count++] = (z2 << 4) | (z3 >> 2);
662     d[count++] = (z3 << 6) | (z4);
663    
664     s += 4;
665     }
666     if (z1 != -1 && z2 != -1 && *(s+2) == '=') {
667     d[count++] = (z1 << 2) | (z2 >> 4);
668     s+=2;
669     }
670     else if (z1 != -1 && z2 != -1 && z3 != -1 && *(s+3) == '=') {
671     d[count++] = (z1 << 2) | (z2 >> 4);
672     d[count++] = (z2 << 4) | (z3 >> 2);
673     s+=3;
674     }
675     while (B64xlat[ACAST(*s)] != -1)
676     uuncdl_fulline[leftover++] = *s++;
677     }
678     else if (method == BH_ENCODED) {
679     if (leftover) {
680     strcpy (uuncdl_fulline+leftover, s);
681     leftover = 0;
682     s = uuncdl_fulline;
683     }
684     else if (*s == ':')
685     s++;
686    
687     while ((z1 = BHxlat[ACAST(*s)]) != -1) {
688     if ((z2 = BHxlat[ACAST(*(s+1))]) == -1) break;
689     if ((z3 = BHxlat[ACAST(*(s+2))]) == -1) break;
690     if ((z4 = BHxlat[ACAST(*(s+3))]) == -1) break;
691    
692     d[count++] = (z1 << 2) | (z2 >> 4);
693     d[count++] = (z2 << 4) | (z3 >> 2);
694     d[count++] = (z3 << 6) | (z4);
695    
696     s += 4;
697     }
698     if (z1 != -1 && z2 != -1 && *(s+2) == ':') {
699     d[count++] = (z1 << 2) | (z2 >> 4);
700     s+=2;
701     }
702     else if (z1 != -1 && z2 != -1 && z3 != -1 && *(s+3) == ':') {
703     d[count++] = (z1 << 2) | (z2 >> 4);
704     d[count++] = (z2 << 4) | (z3 >> 2);
705     s+=3;
706     }
707     while (BHxlat[ACAST(*s)] != -1)
708     uuncdl_fulline[leftover++] = *s++;
709     }
710    
711     return count;
712     }
713    
714     /*
715     * ``Decode'' Quoted-Printable text
716     */
717    
718     int
719     UUDecodeQP (FILE *datain, FILE *dataout, int *state,
720     long maxpos, int method, int flags,
721     char *boundary)
722     {
723     char *line=uugen_inbuffer, *p1, *p2;
724     int val;
725    
726     uulboundary = -1;
727    
728     while (!feof (datain) &&
729     (ftell(datain)<maxpos || flags&FL_TOEND ||
730     (!(flags&FL_PROPER) && uu_fast_scanning))) {
731     if (FP_fgets (line, 255, datain) == NULL)
732     break;
733     if (ferror (datain)) {
734     UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
735     uustring (S_SOURCE_READ_ERR),
736     strerror (uu_errno = errno));
737     return UURET_IOERR;
738     }
739     line[255] = '\0';
740    
741     if (boundary && line[0]=='-' && line[1]=='-' &&
742     strncmp (line+2, boundary, strlen (boundary)) == 0) {
743     if (line[strlen(boundary)+2]=='-')
744     uulboundary = 1;
745     else
746     uulboundary = 0;
747     return UURET_OK;
748     }
749    
750     if (UUBUSYPOLL(ftell(datain)-progress.foffset,progress.fsize)) {
751     UUMessage (uunconc_id, __LINE__, UUMSG_NOTE,
752     uustring (S_DECODE_CANCEL));
753     return UURET_CANCEL;
754     }
755    
756     p1 = p2 = line;
757    
758     while (*p2) {
759     while (*p2 && *p2 != '=')
760     p2++;
761     if (*p2 == '\0')
762     break;
763     *p2 = '\0';
764     fprintf (dataout, "%s", p1);
765     p1 = ++p2;
766    
767     if (isxdigit (*p2) && isxdigit (*(p2+1))) {
768     val = ((isdigit(*p2)) ? (*p2-'0') : (tolower(*p2)-'a'+10)) << 4;
769     val |= ((isdigit(*(p2+1)))?(*(p2+1)-'0') : (tolower(*(p2+1))-'a'+10));
770    
771     fputc (val, dataout);
772     p2 += 2;
773     p1 = p2;
774     }
775     else if (*p2 == '\012' || *(p2+1) == '\015') {
776     /* soft line break */
777     *p2 = '\0';
778     break;
779     }
780     else {
781     /* huh? */
782     fputc ('=', dataout);
783     }
784     }
785     /*
786     * p2 points to a nullbyte right after the CR/LF/CRLF
787     */
788     val = 0;
789     while (p2>p1 && isspace (*(p2-1))) {
790     if (*(p2-1) == '\012' || *(p2-1) == '\015')
791     val = 1;
792     p2--;
793     }
794     *p2 = '\0';
795    
796     /*
797     * If the part ends directly after this line, the data does not end
798     * with a linebreak. Or, as the docs put it, "the CRLF preceding the
799     * encapsulation line is conceptually attached to the boundary.
800     * So if the part ends here, don't print a line break"
801     */
802     if (val && (!feof (datain) &&
803     (ftell(datain)<maxpos || flags&FL_TOEND ||
804     (!(flags&FL_PROPER) && uu_fast_scanning))))
805     fprintf (dataout, "%s\n", p1);
806     else
807     fprintf (dataout, "%s", p1);
808     }
809     return UURET_OK;
810     }
811    
812     /*
813     * ``Decode'' plain text. Our job is to properly handle the EOL sequence
814     */
815    
816     int
817     UUDecodePT (FILE *datain, FILE *dataout, int *state,
818     long maxpos, int method, int flags,
819     char *boundary)
820     {
821     char *line=uugen_inbuffer, *ptr;
822    
823     uulboundary = -1;
824    
825     while (!feof (datain) &&
826     (ftell(datain)<maxpos || flags&FL_TOEND ||
827     (!(flags&FL_PROPER) && uu_fast_scanning))) {
828     if (FP_fgets (line, 255, datain) == NULL)
829     break;
830     if (ferror (datain)) {
831     UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
832     uustring (S_SOURCE_READ_ERR),
833     strerror (uu_errno = errno));
834     return UURET_IOERR;
835     }
836     line[255] = '\0';
837    
838     if (boundary && line[0]=='-' && line[1]=='-' &&
839     strncmp (line+2, boundary, strlen (boundary)) == 0) {
840     if (line[strlen(boundary)+2]=='-')
841     uulboundary = 1;
842     else
843     uulboundary = 0;
844     return UURET_OK;
845     }
846    
847     if (UUBUSYPOLL(ftell(datain)-progress.foffset,progress.fsize)) {
848     UUMessage (uunconc_id, __LINE__, UUMSG_NOTE,
849     uustring (S_DECODE_CANCEL));
850     return UURET_CANCEL;
851     }
852    
853     ptr = line + strlen (line);
854    
855     while (ptr>line && (*(ptr-1) == '\012' || *(ptr-1) == '\015'))
856     ptr--;
857    
858    
859     /*
860     * If the part ends directly after this line, the data does not end
861     * with a linebreak. Or, as the docs put it, "the CRLF preceding the
862     * encapsulation line is conceptually attached to the boundary.
863     * So if the part ends here, don't print a line break"
864     */
865     if ((*ptr == '\012' || *ptr == '\015') &&
866 root 1.2 (ftell(datain)<maxpos || flags&FL_TOEND || flags&FL_PARTIAL ||
867     !boundary || (!(flags&FL_PROPER) && uu_fast_scanning))) {
868 root 1.1 *ptr = '\0';
869     fprintf (dataout, "%s\n", line);
870     }
871     else {
872     *ptr = '\0';
873     fprintf (dataout, "%s", line);
874     }
875     }
876     return UURET_OK;
877     }
878    
879     int
880     UUDecodePart (FILE *datain, FILE *dataout, int *state,
881     long maxpos, int method, int flags,
882     char *boundary)
883     {
884     char *line=uugen_fnbuffer, *oline=uuncdp_oline;
885     int warning=0, vlc=0, lc[2], hadct=0;
886     int tc=0, tf=0, vflag, haddata=0, haddh=0;
887     static int bhflag=0;
888     size_t count=0;
889    
890     if (datain == NULL || dataout == NULL) {
891     bhflag = 0;
892     return UURET_OK;
893     }
894    
895     /*
896     * Use specialized functions for QP_ENCODED and PT_ENCODED plaintext
897     */
898    
899     if (method == QP_ENCODED)
900     return UUDecodeQP (datain, dataout, state, maxpos,
901     method, flags, boundary);
902     else if (method == PT_ENCODED)
903     return UUDecodePT (datain, dataout, state, maxpos,
904     method, flags, boundary);
905    
906     lc[0] = lc[1] = 0;
907     vflag = 0;
908    
909     uulboundary = -1;
910    
911     while (!feof (datain) && *state != DONE &&
912     (ftell(datain)<maxpos || flags&FL_TOEND || maxpos==-1 ||
913     (!(flags&FL_PROPER) && uu_fast_scanning))) {
914     if (FP_fgets (line, 255, datain) == NULL)
915     break;
916     if (ferror (datain)) {
917     UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
918     uustring (S_SOURCE_READ_ERR),
919     strerror (uu_errno = errno));
920     return UURET_IOERR;
921     }
922     if (line[0]=='\015' || line[0]=='\012') { /* Empty line? */
923     if (*state == DATA &&
924     (method == UU_ENCODED || method == XX_ENCODED))
925     *state = END;
926     /*
927     * if we had a whole block of valid lines before, we reset our
928     * 'valid data' flag, tf. Without this 'if', we'd break decoding
929     * files with interleaved blank lines. The value of 5 is chosen
930     * quite arbitrarly.
931     */
932     if (vlc > 5)
933     tf = tc = 0;
934     vlc = 0;
935     continue;
936     }
937    
938     /*
939     * Busy Polls
940     */
941    
942     if (UUBUSYPOLL(ftell(datain)-progress.foffset,progress.fsize)) {
943     UUMessage (uunconc_id, __LINE__, UUMSG_NOTE,
944     uustring (S_DECODE_CANCEL));
945     return UURET_CANCEL;
946     }
947    
948     /*
949     * try to make sense of data
950     */
951    
952     line[255] = '\0'; /* For Safety of string functions */
953     count = 0;
954    
955     if (boundary && line[0]=='-' && line[1]=='-' &&
956     strncmp (line+2, boundary, strlen (boundary)) == 0) {
957     if (line[strlen(boundary)+2]=='-')
958     uulboundary = 1;
959     else
960     uulboundary = 0;
961     return UURET_OK;
962     }
963    
964     /*
965     * Use this pseudo-handling only if !FL_PROPER
966     */
967    
968     if ((flags&FL_PROPER) == 0) {
969     if (strncmp (line, "BEGIN", 5) == 0 &&
970     FP_strstr (line, "CUT HERE") && !tf) { /* I hate these lines */
971     tc = tf = vlc = 0;
972     continue;
973     }
974     /* MIME body boundary */
975     if (line[0] == '-' && line[1] == '-' && method == B64ENCODED) {
976     if ((haddata || tc) && (haddh || hadct)) {
977     *state = DONE;
978     vlc = 0;
979     lc[0] = lc[1] = 0;
980     continue;
981     }
982     hadct = 0;
983     haddh = 1;
984     continue;
985     }
986     if (FP_strnicmp (line, "Content-Type", 12) == 0)
987     hadct = 1;
988     }
989    
990     if (*state == BEGIN) {
991     if (strncmp (line, "begin ", 6) == 0 ||
992     FP_strnicmp (line, "<pre>begin ", 11) == 0) { /* for LYNX */
993     *state = DATA;
994     continue;
995     }
996     else if (method == BH_ENCODED && line[0] == ':') {
997     if (UUValidData (line, BH_ENCODED, &bhflag) == BH_ENCODED) {
998     bhflag = 0;
999     *state = DATA;
1000     }
1001     else
1002     continue;
1003     }
1004     else
1005     continue;
1006    
1007     tc = tf = vlc = 0;
1008     lc[0] = lc[1] = 0;
1009     }
1010     else if ((*state == END) &&
1011     (method == UU_ENCODED || method == XX_ENCODED)) {
1012     if (strncmp (line, "end", 3) == 0) {
1013     *state = DONE;
1014     break;
1015     }
1016     }
1017     if (*state == DATA || *state == END) {
1018     if (method==B64ENCODED && line[0]=='-' && line[1]=='-' && tc) {
1019     break;
1020     }
1021    
1022     if ((vflag = UUValidData (line, (tf)?method:0, &bhflag)) == 0)
1023     vflag = UURepairData (datain, line, (tf)?method:0, &bhflag);
1024    
1025     /*
1026     * correct XX/UUencoded lines that were declared Base64
1027     */
1028    
1029     if ((method == XX_ENCODED || method == UU_ENCODED) &&
1030     vflag == B64ENCODED) {
1031     if (UUValidData (line, method, &bhflag) == method)
1032     vflag = method;
1033     }
1034    
1035     if (vflag == method) {
1036     if (tf) {
1037     count = UUDecodeLine (line, oline, method);
1038     vlc++; lc[1]++;
1039     }
1040     else if (tc == 3) {
1041     count = UUDecodeLine (save[0], oline, method);
1042     count += UUDecodeLine (save[1], oline + count, method);
1043     count += UUDecodeLine (save[2], oline + count, method);
1044     count += UUDecodeLine (line, oline + count, method);
1045     tf = 1;
1046     tc = 0;
1047    
1048     /*
1049     * complain if we had one or two invalid lines amidst of
1050     * correctly encoded data. This usually means that the
1051     * file is in error
1052     */
1053    
1054     if (lc[1] > 10 && (lc[0] >= 1 && lc[0] <= 2) && !warning) {
1055     UUMessage (uunconc_id, __LINE__, UUMSG_WARNING,
1056     uustring (S_DATA_SUSPICIOUS));
1057     warning=1;
1058     }
1059     lc[0] = 0;
1060     lc[1] = 3;
1061     }
1062     else {
1063     FP_strncpy (save[tc++], line, 256);
1064     }
1065     if (method == UU_ENCODED)
1066     *state = (line[0] == 'M') ? DATA : END;
1067     else if (method == XX_ENCODED)
1068     *state = (line[0] == 'h') ? DATA : END;
1069     else if (method == B64ENCODED)
1070     *state = (strchr (line, '=') == NULL) ? DATA : DONE;
1071     else if (method == BH_ENCODED)
1072     *state = (!line[0] || strchr(line+1,':')==NULL)?DATA:DONE;
1073     }
1074     else {
1075     vlc = tf = tc = 0;
1076     haddh = 0;
1077     lc[0]++;
1078     }
1079     }
1080     else if (*state != DONE) {
1081     return UURET_NOEND;
1082     }
1083     if (count) {
1084     if (method == BH_ENCODED) {
1085     if (UUbhwrite (oline, 1, count, dataout) != count) {
1086     UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
1087     uustring (S_WR_ERR_TEMP),
1088     strerror (uu_errno = errno));
1089     return UURET_IOERR;
1090     }
1091     }
1092     else if (fwrite (oline, 1, count, dataout) != count) {
1093     UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
1094     uustring (S_WR_ERR_TEMP),
1095     strerror (uu_errno = errno));
1096     return UURET_IOERR;
1097     }
1098     haddata++;
1099     count = 0;
1100     }
1101     }
1102    
1103     if (*state == DONE ||
1104     (*state == DATA && method == B64ENCODED &&
1105     vflag == B64ENCODED && (flags&FL_PROPER || haddh))) {
1106     for (tf=0; tf<tc; tf++)
1107     count += UUDecodeLine (save[tf], oline + count, method);
1108     if (count) {
1109     if (method == BH_ENCODED) {
1110     if (UUbhwrite (oline, 1, count, dataout) != count) {
1111     UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
1112     uustring (S_WR_ERR_TEMP),
1113     strerror (uu_errno = errno));
1114     return UURET_IOERR;
1115     }
1116     }
1117     else if (fwrite (oline, 1, count, dataout) != count) {
1118     UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
1119     uustring (S_WR_ERR_TEMP),
1120     strerror (uu_errno = errno));
1121     return UURET_IOERR;
1122     }
1123     }
1124     }
1125     return UURET_OK;
1126     }
1127    
1128     /*
1129     * this function decodes the file into a temporary file
1130     */
1131    
1132     int
1133     UUDecode (uulist *data)
1134     {
1135     int state=BEGIN, part=-1, res=0, hb;
1136     long rsize, dsize, numbytes;
1137     FILE *datain, *dataout;
1138     unsigned char r[8];
1139     char *mode, *ntmp;
1140     uufile *iter;
1141     size_t bytes;
1142 root 1.3 #ifdef HAVE_MKSTEMP
1143     int tmpfd;
1144     const char *tmpprefix = "uuXXXXXX";
1145     char *tmpdir = NULL;
1146     #endif /* HAVE_MKSTEMP */
1147 root 1.1
1148     if (data == NULL || data->thisfile == NULL)
1149     return UURET_ILLVAL;
1150    
1151     if (data->state & UUFILE_TMPFILE)
1152     return UURET_OK;
1153    
1154     if (data->state & UUFILE_NODATA)
1155     return UURET_NODATA;
1156    
1157     if (data->state & UUFILE_NOBEGIN && !uu_desperate)
1158     return UURET_NODATA;
1159    
1160 root 1.2 if (data->uudet == PT_ENCODED)
1161 root 1.1 mode = "wt"; /* open text files in text mode */
1162     else
1163     mode = "wb"; /* otherwise in binary */
1164    
1165 root 1.3 #ifdef HAVE_MKSTEMP
1166     if ((getuid()==geteuid()) && (getgid()==getegid())) {
1167     tmpdir=getenv("TMPDIR");
1168     }
1169    
1170     if (!tmpdir) {
1171     tmpdir = "/tmp";
1172     }
1173     data->binfile = malloc(strlen(tmpdir)+strlen(tmpprefix)+2);
1174    
1175     if (!data->binfile) {
1176     #else
1177 root 1.1 if ((data->binfile = tempnam (NULL, "uu")) == NULL) {
1178 root 1.3 #endif /* HAVE_MKSTEMP */
1179 root 1.1 UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
1180     uustring (S_NO_TEMP_NAME));
1181     return UURET_NOMEM;
1182     }
1183    
1184 root 1.3 #ifdef HAVE_MKSTEMP
1185     strcpy(data->binfile, tmpdir);
1186     strcat(data->binfile, "/");
1187     strcat(data->binfile, tmpprefix);
1188    
1189     if ((tmpfd = mkstemp(data->binfile)) == -1 ||
1190     (dataout = fdopen(tmpfd, mode)) == NULL) {
1191     #else
1192 root 1.1 if ((dataout = fopen (data->binfile, mode)) == NULL) {
1193 root 1.3 #endif /* HAVE_MKSTEMP */
1194 root 1.1 /*
1195     * we couldn't create a temporary file. Usually this means that TMP
1196     * and TEMP aren't set
1197     */
1198     UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
1199     uustring (S_WR_ERR_TARGET),
1200     data->binfile, strerror (uu_errno = errno));
1201 root 1.3 #ifdef HAVE_MKSTEMP
1202     if (tmpfd != -1) {
1203     unlink(data->binfile);
1204     close(tmpfd);
1205     }
1206     #endif /* HAVE_MKSTEMP */
1207 root 1.1 FP_free (data->binfile);
1208     data->binfile = NULL;
1209     uu_errno = errno;
1210     return UURET_IOERR;
1211     }
1212 root 1.3
1213 root 1.1 /*
1214     * we don't have begin lines in Base64 or plain text files.
1215     */
1216     if (data->uudet == B64ENCODED || data->uudet == QP_ENCODED ||
1217     data->uudet == PT_ENCODED)
1218     state = DATA;
1219    
1220     /*
1221     * If we know that the file does not have a begin, we simulate
1222     * it in desperate mode
1223     */
1224    
1225     if ((data->state & UUFILE_NOBEGIN) && uu_desperate)
1226     state = DATA;
1227    
1228     (void) UUDecodeLine (NULL, NULL, 0); /* init */
1229     (void) UUbhwrite (NULL, 0, 0, NULL); /* dito */
1230     (void) UUDecodePart (NULL, NULL, NULL, 0, 0, 0, NULL); /* yep */
1231    
1232     /*
1233     * initialize progress information
1234     */
1235     progress.action = 0;
1236     if (data->filename != NULL) {
1237     FP_strncpy (progress.curfile,
1238     (strlen(data->filename)>255)?
1239     (data->filename+strlen(data->filename)-255):data->filename,
1240     256);
1241     }
1242     else {
1243     FP_strncpy (progress.curfile,
1244     (strlen(data->binfile)>255)?
1245     (data->binfile+strlen(data->binfile)-255):data->binfile,
1246     256);
1247     }
1248     progress.partno = 0;
1249     progress.numparts = 0;
1250     progress.fsize = -1;
1251     progress.percent = 0;
1252     progress.action = UUACT_DECODING;
1253    
1254     iter = data->thisfile;
1255     while (iter) {
1256     progress.numparts = (iter->partno)?iter->partno:1;
1257     iter = iter->NEXT;
1258     }
1259    
1260     /*
1261     * let's rock!
1262     */
1263    
1264     iter = data->thisfile;
1265     while (iter) {
1266     if (part != -1 && iter->partno != part+1)
1267     break;
1268     else
1269     part = iter->partno;
1270    
1271     if (iter->data->sfname == NULL) {
1272     iter = iter->NEXT;
1273     continue;
1274     }
1275    
1276     /*
1277     * call our FileCallback to retrieve the file
1278     */
1279    
1280     if (uu_FileCallback) {
1281     if ((res = (*uu_FileCallback) (uu_FileCBArg, iter->data->sfname,
1282     uugen_fnbuffer, 1)) != UURET_OK)
1283     break;
1284     if ((datain = fopen (uugen_fnbuffer, "rb")) == NULL) {
1285     (*uu_FileCallback) (uu_FileCBArg, iter->data->sfname,
1286     uugen_fnbuffer, 0);
1287     UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
1288     uustring (S_NOT_OPEN_FILE),
1289     uugen_fnbuffer, strerror (uu_errno = errno));
1290     res = UURET_IOERR;
1291     break;
1292     }
1293     }
1294     else {
1295     if ((datain = fopen (iter->data->sfname, "rb")) == NULL) {
1296     UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
1297     uustring (S_NOT_OPEN_FILE),
1298     iter->data->sfname, strerror (uu_errno = errno));
1299     res = UURET_IOERR;
1300     break;
1301     }
1302     FP_strncpy (uugen_fnbuffer, iter->data->sfname, 1024);
1303     }
1304    
1305     progress.partno = part;
1306     progress.fsize = (iter->data->length)?iter->data->length:-1;
1307     progress.percent = 0;
1308     progress.foffset = iter->data->startpos;
1309    
1310     fseek (datain, iter->data->startpos, SEEK_SET);
1311     res = UUDecodePart (datain, dataout, &state,
1312     iter->data->startpos+iter->data->length,
1313     data->uudet, iter->data->flags, NULL);
1314     fclose (datain);
1315    
1316     if (uu_FileCallback)
1317     (*uu_FileCallback) (uu_FileCBArg, iter->data->sfname, uugen_fnbuffer, 0);
1318    
1319     if (state == DONE || res != UURET_OK)
1320     break;
1321    
1322     iter = iter->NEXT;
1323     }
1324    
1325     if (state == DATA &&
1326     (data->uudet == B64ENCODED || data->uudet == QP_ENCODED ||
1327     data->uudet == PT_ENCODED))
1328     state = DONE; /* assume we're done */
1329    
1330     fclose (dataout);
1331    
1332     if (res != UURET_OK || (state != DONE && !uu_desperate)) {
1333     unlink (data->binfile);
1334     FP_free (data->binfile);
1335     data->binfile = NULL;
1336     data->state &= ~UUFILE_TMPFILE;
1337     data->state |= UUFILE_ERROR;
1338    
1339     if (res == UURET_OK && state != DONE)
1340     res = UURET_NOEND;
1341     }
1342     else if (res != UURET_OK) {
1343     data->state &= ~UUFILE_DECODED;
1344     data->state |= UUFILE_ERROR | UUFILE_TMPFILE;
1345     }
1346     else {
1347     data->state &= ~UUFILE_ERROR;
1348     data->state |= UUFILE_TMPFILE;
1349     }
1350    
1351     /*
1352     * If this was a BinHex file, we must extract its data or resource fork
1353     */
1354    
1355     if (data->uudet == BH_ENCODED && data->binfile) {
1356 root 1.3 #ifdef HAVE_MKSTEMP
1357     ntmp = malloc(strlen(tmpdir)+strlen(tmpprefix)+2);
1358    
1359     if (ntmp == NULL) {
1360     #else
1361 root 1.1 if ((ntmp = tempnam (NULL, "uu")) == NULL) {
1362 root 1.3 #endif /* HAVE_MKSTEMP */
1363 root 1.1 UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
1364     uustring (S_NO_TEMP_NAME));
1365     progress.action = 0;
1366     return UURET_NOMEM;
1367     }
1368     if ((datain = fopen (data->binfile, "rb")) == NULL) {
1369     UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
1370     uustring (S_NOT_OPEN_FILE),
1371     data->binfile, strerror (uu_errno = errno));
1372     progress.action = 0;
1373     free (ntmp);
1374     return UURET_IOERR;
1375     }
1376 root 1.3 #ifdef HAVE_MKSTEMP
1377     strcpy(ntmp, tmpdir);
1378     strcat(ntmp, "/");
1379     strcat(ntmp, tmpprefix);
1380     if ((tmpfd = mkstemp(ntmp)) == -1 ||
1381     (dataout = fdopen(tmpfd, "wb")) == NULL) {
1382     #else
1383 root 1.1 if ((dataout = fopen (ntmp, "wb")) == NULL) {
1384 root 1.3 #endif /* HAVE_MKSTEMP */
1385 root 1.1 UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
1386     uustring (S_NOT_OPEN_TARGET),
1387     ntmp, strerror (uu_errno = errno));
1388     progress.action = 0;
1389     fclose (datain);
1390 root 1.3 #ifdef HAVE_MKSTEMP
1391     if (tmpfd != -1) {
1392     unlink(ntmp);
1393     close(tmpfd);
1394     }
1395     #endif /* HAVE_MKSTEMP */
1396 root 1.1 free (ntmp);
1397     return UURET_IOERR;
1398     }
1399 root 1.3
1400 root 1.1 /*
1401     * read fork lengths. remember they're in Motorola format
1402     */
1403     r[0] = fgetc (datain);
1404     hb = (int) r[0] + 22;
1405     fseek (datain, (int) r[0] + 12, SEEK_SET);
1406     fread (r, 1, 8, datain);
1407    
1408     dsize = (((long) 1 << 24) * (long) r[0]) +
1409     (((long) 1 << 16) * (long) r[1]) +
1410     (((long) 1 << 8) * (long) r[2]) +
1411     ( (long) r[3]);
1412     rsize = (((long) 1 << 24) * (long) r[4]) +
1413     (((long) 1 << 16) * (long) r[5]) +
1414     (((long) 1 << 8) * (long) r[6]) +
1415     ( (long) r[7]);
1416    
1417     UUMessage (uunconc_id, __LINE__, UUMSG_MESSAGE,
1418     uustring (S_BINHEX_SIZES),
1419     dsize, rsize);
1420    
1421     if (dsize == 0) {
1422     fseek (datain, dsize + hb + 2, SEEK_SET);
1423     numbytes = rsize;
1424     }
1425     else if (rsize == 0) {
1426     fseek (datain, hb, SEEK_SET);
1427     numbytes = dsize;
1428     }
1429     else {
1430     /* we should let the user have the choice here */
1431     UUMessage (uunconc_id, __LINE__, UUMSG_NOTE,
1432     uustring (S_BINHEX_BOTH));
1433     fseek (datain, hb, SEEK_SET);
1434     numbytes = dsize;
1435     }
1436    
1437     progress.action = 0;
1438     progress.partno = 0;
1439     progress.numparts = 1;
1440     progress.fsize = (numbytes)?numbytes:-1;
1441     progress.foffset = hb;
1442     progress.percent = 0;
1443     progress.action = UUACT_COPYING;
1444    
1445     /*
1446     * copy the chosen fork
1447     */
1448    
1449     while (!feof (datain) && numbytes) {
1450     if (UUBUSYPOLL(ftell(datain)-progress.foffset,progress.fsize)) {
1451     UUMessage (uunconc_id, __LINE__, UUMSG_NOTE,
1452     uustring (S_DECODE_CANCEL));
1453     fclose (datain);
1454     fclose (dataout);
1455     unlink (ntmp);
1456     free (ntmp);
1457     return UURET_CANCEL;
1458     }
1459    
1460     bytes = fread (uugen_inbuffer, 1,
1461     (size_t) ((numbytes>1024)?1024:numbytes), datain);
1462    
1463     if (ferror (datain) || (bytes == 0 && !feof (datain))) {
1464     progress.action = 0;
1465     UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
1466     uustring (S_SOURCE_READ_ERR),
1467     data->binfile, strerror (uu_errno = errno));
1468     fclose (datain);
1469     fclose (dataout);
1470     unlink (ntmp);
1471     free (ntmp);
1472     return UURET_IOERR;
1473     }
1474     if (fwrite (uugen_inbuffer, 1, bytes, dataout) != bytes) {
1475     progress.action = 0;
1476     UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
1477     uustring (S_WR_ERR_TARGET),
1478     ntmp, strerror (uu_errno = errno));
1479     fclose (datain);
1480     fclose (dataout);
1481     unlink (ntmp);
1482     free (ntmp);
1483     return UURET_IOERR;
1484     }
1485     numbytes -= bytes;
1486     }
1487    
1488     if (numbytes) {
1489     UUMessage (uunconc_id, __LINE__, UUMSG_WARNING,
1490     uustring (S_SHORT_BINHEX),
1491     (data->filename)?data->filename:
1492     (data->subfname)?data->subfname:"???",
1493     numbytes);
1494     }
1495    
1496     /*
1497     * replace temp file
1498     */
1499    
1500     fclose (datain);
1501     fclose (dataout);
1502    
1503     if (unlink (data->binfile)) {
1504     UUMessage (uunconc_id, __LINE__, UUMSG_WARNING,
1505     uustring (S_TMP_NOT_REMOVED),
1506     data->binfile, strerror (uu_errno = errno));
1507     }
1508    
1509     free (data->binfile);
1510     data->binfile = ntmp;
1511     }
1512    
1513     progress.action = 0;
1514     return res;
1515     }
1516    
1517     /*
1518     * QuickDecode for proper MIME attachments. We expect the pointer to
1519     * be on the first header line.
1520     */
1521    
1522     int
1523     UUQuickDecode (FILE *datain, FILE *dataout, char *boundary, long maxpos)
1524     {
1525     int state=BEGIN, encoding=-1;
1526     headers myenv;
1527    
1528     /*
1529     * Read header and find out about encoding.
1530     */
1531    
1532     memset (&myenv, 0, sizeof (headers));
1533     UUScanHeader (datain, &myenv);
1534    
1535     if (FP_stristr (myenv.ctenc, "uu") != NULL)
1536     encoding = UU_ENCODED;
1537     else if (FP_stristr (myenv.ctenc, "xx") != NULL)
1538     encoding = XX_ENCODED;
1539     else if (FP_stricmp (myenv.ctenc, "base64") == 0)
1540     encoding = B64ENCODED;
1541     else if (FP_stricmp (myenv.ctenc, "quoted-printable") == 0)
1542     encoding = QP_ENCODED;
1543     else
1544     encoding = PT_ENCODED;
1545    
1546     UUkillheaders (&myenv);
1547    
1548     /*
1549     * okay, so decode this one
1550     */
1551    
1552     (void) UUDecodePart (NULL, NULL, NULL, 0, 0, 0, NULL); /* init */
1553     return UUDecodePart (datain, dataout, &state, maxpos,
1554     encoding, FL_PROPER|FL_TOEND,
1555     boundary);
1556     }