1 /*
2  * This file is part of uudeview, the simple and friendly multi-part multi-
3  * file uudecoder  program  (c) 1994-2001 by Frank Pilhofer. The author may
4  * be contacted at fp@fpx.de
5  *
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  * certain utilitarian functions that didn't fit anywhere else
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #ifdef SYSTEM_WINDLL
26 #include <windows.h>
27 #endif
28 #ifdef SYSTEM_OS2
29 #include <os2.h>
30 #endif
31 
32 #include <stdio.h>
33 #include <ctype.h>
34 
35 #ifdef STDC_HEADERS
36 #include <stdlib.h>
37 #include <string.h>
38 #endif
39 #ifdef HAVE_MALLOC_H
40 #include <malloc.h>
41 #endif
42 #ifdef HAVE_UNISTD_H
43 #include <unistd.h>
44 #endif
45 #ifdef HAVE_MEMORY_H
46 #include <memory.h>
47 #endif
48 #ifdef HAVE_ERRNO_H
49 #include <errno.h>
50 #endif
51 
52 #include <uudeview.h>
53 #include <uuint.h>
54 #include <fptools.h>
55 #include <uustring.h>
56 
57 char * uuutil_id = "$Id: uuutil.c,v 1.15 2001/06/06 18:21:47 fp Exp $";
58 
59 /*
60  * Parts with different known extensions will not be merged by SPMS.
61  * if first character is '@', it is synonymous to the previous one.
62  */
63 
64 static char *knownexts[] = {
65   "mpg", "@mpeg", "avi", "mov",
66   "gif", "jpg", "@jpeg", "tif",
67   "voc", "wav", "@wave", "au",
68   "zip", "arj", "tar",
69   NULL
70 };
71 
72 /*
73  * forward declarations of local functions
74  */
75 
76 static int	UUSMPKnownExt		_ANSI_ARGS_((char *filename));
77 static uulist *	UU_smparts_r		_ANSI_ARGS_((uulist *, int));
78 
79 /*
80  * mallocable areas
81  */
82 
83 char *uuutil_bhwtmp;
84 
85 /*
86  * free some memory
87  **/
88 
89 void
UUkillfread(fileread * data)90 UUkillfread (fileread *data)
91 {
92   if (data != NULL) {
93     _FP_free (data->subject);
94     _FP_free (data->filename);
95     _FP_free (data->origin);
96     _FP_free (data->mimeid);
97     _FP_free (data->mimetype);
98     _FP_free (data->sfname);
99     _FP_free (data);
100   }
101 }
102 
103 void
UUkillfile(uufile * data)104 UUkillfile (uufile *data)
105 {
106   uufile *next;
107 
108   while (data) {
109     _FP_free    (data->filename);
110     _FP_free    (data->subfname);
111     _FP_free    (data->mimeid);
112     _FP_free    (data->mimetype);
113     UUkillfread (data->data);
114 
115     next = data->NEXT;
116     _FP_free  (data);
117     data = next;
118   }
119 }
120 
121 void
UUkilllist(uulist * data)122 UUkilllist (uulist *data)
123 {
124   uulist *next;
125 
126   while (data) {
127     if (data->binfile != NULL)
128       if (unlink (data->binfile))
129 	UUMessage (uuutil_id, __LINE__, UUMSG_WARNING,
130 		   uustring (S_TMP_NOT_REMOVED),
131 		   data->binfile, strerror (errno));
132 
133     _FP_free   (data->filename);
134     _FP_free   (data->subfname);
135     _FP_free   (data->mimeid);
136     _FP_free   (data->mimetype);
137     _FP_free   (data->binfile);
138     UUkillfile (data->thisfile);
139     _FP_free   (data->haveparts);
140     _FP_free   (data->misparts);
141 
142     next = data->NEXT;
143     _FP_free (data);
144     data = next;
145   }
146 }
147 
148 /*
149  * this kill function is an exception in that it doesn't kill data itself
150  */
151 
152 void
UUkillheaders(headers * data)153 UUkillheaders (headers *data)
154 {
155   if (data != NULL) {
156     _FP_free (data->from);
157     _FP_free (data->subject);
158     _FP_free (data->rcpt);
159     _FP_free (data->date);
160     _FP_free (data->mimevers);
161     _FP_free (data->ctype);
162     _FP_free (data->ctenc);
163     _FP_free (data->fname);
164     _FP_free (data->boundary);
165     _FP_free (data->mimeid);
166     memset   (data, 0, sizeof (headers));
167   }
168 }
169 
170 /*
171  * checks for various well-known extensions. if two parts have different
172  * known extensions, we won't merge them.
173  */
174 
175 static int
UUSMPKnownExt(char * filename)176 UUSMPKnownExt (char *filename)
177 {
178   char **eiter = knownexts, *ptr=_FP_strrchr(filename, '.');
179   int count=0, where=0;
180 
181   if (ptr == NULL)
182     return -1;
183   ptr++;
184 
185   while (*eiter) {
186     if (_FP_stricmp (ptr, (**eiter=='@')?*eiter+1:*eiter) == 0)
187       return where;
188     else
189       eiter++;
190 
191     if (*eiter == NULL)
192       break;
193 
194     if (**eiter=='@')
195       count++;
196     else
197       where = ++count;
198   }
199   return -1;
200 }
201 
202 /*
203  * de-compress a binhex RLE stream
204  * the data read from in is uncompressed, and at most maxcount bytes
205  * (or octets, as they say) are copied to out. Because an uncompression
206  * might not be completed because of this maximum number of bytes. There-
207  * for, the leftover character and repetition count is saved. If a marker
208  * has been read but not the repetition count, *rpc is set to -256.
209  *
210  * the function returns the number of bytes eaten from in. If opc is not
211  * NULL, the total number of characters stored in out is saved there
212  *
213  * with repetition counts, remember that we've already transferred *one*
214  * occurence
215  */
216 
217 int
UUbhdecomp(char * in,char * out,char * last,int * rpc,size_t inc,size_t max,size_t * opc)218 UUbhdecomp (char *in, char *out, char *last, int *rpc,
219 	    size_t inc, size_t max, size_t *opc)
220 {
221   size_t count, used=0, dummy;
222   char marker = '\220' /* '\x90' */;
223 
224   if (opc == NULL)
225     opc = &dummy;
226   else
227     *opc = 0;
228 
229   if (*rpc == -256) {
230     if (inc == 0)
231       return 0;
232     *rpc = (int) (unsigned char) *in++; used++;
233 
234     if (*rpc == 0) {
235       *last = *out++ = marker;
236       max--; *opc+=1;
237     }
238     else
239       *rpc-=1;
240   }
241 
242   if (*rpc) {
243     count = (max > (size_t) *rpc) ? (size_t) *rpc : max;
244 
245     memset (out, *last, count);
246 
247     out  += count;
248     *opc += count;
249     max  -= count;
250     *rpc -= count;
251   }
252 
253   while (used < inc && max) {
254     if (*in == marker) {
255       used++; in++;
256       if (used == inc) {
257 	*rpc = -256;
258 	return used;
259       }
260       *rpc = (int) (unsigned char) *in++; used++;
261 
262       if (*rpc == 0) {
263 	*last = *out++ = marker;
264 	max--; *opc+=1;
265 	continue;
266       }
267       else
268 	*rpc -= 1;
269 
270       count = (max > (size_t) *rpc) ? (size_t) *rpc : max;
271       memset (out, *last, count);
272 
273       out  += count;
274       *opc += count;
275       max  -= count;
276       *rpc -= count;
277     }
278     else {
279       *last = *out++ = *in++;
280       used++; *opc+=1; max--;
281     }
282   }
283 
284   return used;
285 }
286 
287 /*
288  * write to binhex file
289  */
290 
291 size_t
UUbhwrite(char * ptr,size_t sel,size_t nel,FILE * file)292 UUbhwrite (char *ptr, size_t sel, size_t nel, FILE *file)
293 {
294   char *tmpstring=uuutil_bhwtmp;
295   static int rpc = 0;
296   static char lc;
297   int count, tc=0;
298   size_t opc;
299 
300   if (ptr == NULL) { /* init */
301     rpc = 0;
302     return 0;
303   }
304 
305   while (nel || (rpc != 0 && rpc != -256)) {
306     count = UUbhdecomp (ptr, tmpstring, &lc, &rpc,
307 			nel, 256, &opc);
308     if (fwrite (tmpstring, 1, opc, file) != opc)
309       return 0;
310     if (ferror (file))
311       return 0;
312     nel -= count;
313     ptr += count;
314     tc  += count;
315   }
316 
317   return tc;
318 }
319 
320 static uulist *
UU_smparts_r(uulist * addit,int pass)321 UU_smparts_r (uulist *addit, int pass)
322 {
323   uulist *iter = UUGlobalFileList;
324   uufile *fiter, *dest, *temp;
325   int count, flag, a, b;
326 
327   while (iter) {
328     if ((iter->state & UUFILE_OK) || iter->uudet == 0) {
329       iter = iter->NEXT;
330       continue;
331     }
332     if (iter == addit) {
333       iter = iter->NEXT;
334       continue;
335     }
336     if ((iter->begin && addit->begin) || (iter->end && addit->end) ||
337 	(iter->uudet != addit->uudet)) {
338       iter = iter->NEXT;
339       continue;
340     }
341     if ((a = UUSMPKnownExt (addit->subfname)) != -1 &&
342         (b = UUSMPKnownExt (iter->subfname))  != -1)
343       if (a != b) {
344         iter = iter->NEXT;
345         continue;
346       }
347 
348     flag  = count = 0;
349     fiter = iter->thisfile;
350     temp  = addit->thisfile;
351     dest  = NULL;
352 
353     while (temp) {
354       if (!(temp->data->uudet)) {
355 	temp = temp->NEXT;
356 	continue;
357       }
358 
359       while (fiter && fiter->partno < temp->partno) {
360         dest  = fiter;
361         fiter = fiter->NEXT;
362       }
363       if (fiter && fiter->partno == temp->partno) {
364         flag = 0;
365         break;
366       }
367       else {
368 	flag   = 1;
369         count += ((dest)  ? temp->partno - dest->partno - 1 : 0) +
370                  ((fiter) ? fiter->partno - temp->partno - 1 : 0);
371       }
372 
373       temp = temp->NEXT;
374     }
375     if (flag == 0 ||
376         (pass == 0 && count > 0) ||
377         (pass == 1 && count > 5)) {
378       iter = iter->NEXT;
379       continue;
380     }
381 
382     dest  = iter->thisfile;
383     fiter = addit->thisfile;
384 
385     if (iter->filename == NULL && addit->filename != NULL)
386       iter->filename = _FP_strdup (addit->filename);
387 
388     if (addit->begin) iter->begin = 1;
389     if (addit->end)   iter->end   = 1;
390 
391     if (addit->mode != 0 && iter->mode == 0)
392       iter->mode = addit->mode;
393 
394     while (fiter) {
395       flag = 0;
396 
397       if (fiter->partno == iter->thisfile->partno ||
398 	  (dest->NEXT != NULL && fiter->partno == dest->NEXT->partno)) {
399 	temp           = fiter->NEXT;
400 	fiter->NEXT    = NULL;
401 
402 	UUkillfile (fiter);
403 
404 	addit->thisfile= temp;
405 	fiter          = temp;
406 	continue;
407       }
408       if (fiter->partno < iter->thisfile->partno) {
409 	temp           = fiter->NEXT;
410 	fiter->NEXT    = iter->thisfile;
411 	iter->thisfile = fiter;
412 	dest           = fiter;
413 	addit->thisfile= temp;
414 	fiter          = temp;
415       }
416       else if (dest->NEXT == NULL || fiter->partno < dest->NEXT->partno) {
417 	temp           = fiter->NEXT;
418 	fiter->NEXT    = dest->NEXT;
419 	dest->NEXT     = fiter;
420 	addit->thisfile= temp;
421 	fiter          = temp;
422       }
423       else {
424 	dest = dest->NEXT;
425       }
426     }
427     break;
428   }
429   return iter;
430 }
431 
432 int UUEXPORT
UUSmerge(int pass)433 UUSmerge (int pass)
434 {
435   uulist *iter = UUGlobalFileList, *last=NULL, *res, *temp;
436   int flag = 0;
437 
438   while (iter) {
439     if ((iter->state & UUFILE_OK) || iter->uudet == 0) {
440       last = iter;
441       iter = iter->NEXT;
442       continue;
443     }
444     if ((res = UU_smparts_r (iter, pass)) != NULL) {
445       UUMessage (uuutil_id, __LINE__, UUMSG_MESSAGE,
446 		 uustring (S_SMERGE_MERGED),
447 		 (iter->subfname) ? iter->subfname : "",
448 		 (res->subfname)  ? res->subfname  : "", pass);
449 
450       temp       = iter->NEXT;
451       iter->NEXT = NULL;
452       UUkilllist (iter);
453 
454       flag++;
455 
456       if (last == NULL) {
457 	UUGlobalFileList = temp;
458 	iter             = temp;
459       }
460       else {
461 	last->NEXT       = temp;
462 	iter             = temp;
463       }
464 
465       continue;
466     }
467     last = iter;
468     iter = iter->NEXT;
469   }
470 
471   /*
472    * check again
473    */
474 
475   UUCheckGlobalList ();
476 
477   return flag;
478 }
479 
480