]> arthur.barton.de Git - netatalk.git/blob - bin/megatron/hqx.c
aae9c7231a6452cf9fd3678e389b7b8fe40bbb3c
[netatalk.git] / bin / megatron / hqx.c
1 /*
2  * $Id: hqx.c,v 1.5 2001-05-01 13:21:22 rufustfirefly Exp $
3  */
4
5 #ifdef HAVE_CONFIG_H
6 #include "config.h"
7 #endif
8
9 #include <stdlib.h>
10 #include <sys/types.h>
11 #include <sys/uio.h>
12 #include <sys/time.h>
13 #include <sys/param.h>
14 #ifdef notdef
15 #if BSD >= 199006
16 # include <machine/endian.h>
17 #else
18 # include <netinet/in.h>
19 #endif
20 #endif notdef
21 #include <time.h>
22 #include <fcntl.h>
23 #include <string.h>
24 #include <syslog.h>
25 #include <ctype.h>
26 #include <stdio.h>
27 #include <atalk/adouble.h>
28 #include <netatalk/endian.h>
29 #include "megatron.h"
30
31 #define HEXOUTPUT       0
32
33 /*      String used to indicate standard input instead of a disk
34         file.  Should be a string not normally used for a file
35  */
36 #ifndef STDIN
37 #       define  STDIN   "-"
38 #endif
39
40 /*      Yes and no
41  */
42 #define NOWAY           0
43 #define SURETHANG       1
44
45 /*      Looking for the first or any other line of a binhex file
46  */
47 #define FIRST           0
48 #define OTHER           1
49
50 /*      This is the binhex run length encoding character
51  */
52 #define RUNCHAR         0x90
53
54 /*      These are field sizes in bytes of various pieces of the
55         binhex header
56  */
57 #define BHH_VERSION             1
58 #define BHH_TCSIZ               8
59 #define BHH_FLAGSIZ             2
60 #define BHH_DATASIZ             4
61 #define BHH_RESSIZ              4
62 #define BHH_CRCSIZ              2
63 #define BHH_HEADSIZ             21
64
65 u_short         updcrc();
66
67 #if HEXOUTPUT
68 FILE            *rawhex, *expandhex;
69 #endif
70
71 struct hqx_file_data {
72     u_int32_t           forklen[ NUMFORKS ];
73     u_short             forkcrc[ NUMFORKS ];
74     char                path[ MAXPATHLEN + 1];
75     u_short             headercrc;
76     int                 filed;
77 }               hqx;
78
79 extern char     *forkname[];
80 u_char          hqx7_buf[8192];
81 u_char          *hqx7_first;
82 u_char          *hqx7_last;
83 int             first_flag;
84
85 /* 
86 hqx_open must be called first.  pass it a filename that is supposed
87 to contain a binhqx file.  an hqx struct will be allocated and
88 somewhat initialized; hqx_fd is set.  skip_junk is called from
89 here; skip_junk leaves hqx7_first and hqx7_last set.
90  */
91
92 int hqx_open( hqxfile, flags, fh, options )
93     char                *hqxfile;
94     int                 flags, options;
95     struct FHeader      *fh;
96 {
97     int                 maxlen;
98
99 #if DEBUG
100     fprintf( stderr, "megatron: entering hqx_open\n" );
101 #endif
102     if ( flags == O_RDONLY ) {
103
104 #if HEXOUTPUT
105         rawhex = fopen( "rawhex.unhex", "w" );
106         expandhex = fopen( "expandhex.unhex", "w" );
107 #endif
108
109         first_flag = 0;
110
111         if ( strcmp( hqxfile, STDIN ) == 0 ) {
112             hqx.filed = fileno( stdin );
113         } else if (( hqx.filed = open( hqxfile, O_RDONLY )) < 0 ) {
114             perror( hqxfile );
115             return( -1 );
116         }
117
118         if ( skip_junk( FIRST ) == 0 ) {
119             if ( hqx_header_read( fh ) == 0 ) {
120 #if DEBUG
121                 off_t   pos;
122
123                 pos = lseek( hqx.filed, 0, L_INCR );
124                 fprintf( stderr, "megatron: current position is %ld\n", pos );
125 #endif
126                 return( 0 );
127             }
128         }
129         hqx_close( KEEP );
130         fprintf( stderr, "%s\n", hqxfile );
131         return( -1 );
132     } else {
133         maxlen = sizeof( hqx.path ) -1;
134         strncpy( hqx.path, fh->name, maxlen );
135         strncpy( hqx.path, mtoupath( hqx.path ), maxlen );
136         strncat( hqx.path, ".hqx", maxlen - strlen( hqx.path ));
137         if (( hqx.filed = open( hqx.path, flags, 0666 )) < 0 ) {
138             perror( hqx.path );
139             return( -1 );
140         }
141         if ( hqx_header_write( fh ) != 0 ) {
142             hqx_close( TRASH );
143             fprintf( stderr, "%s\n", hqx.path );
144             return( -1 );
145         }
146         return( 0 );
147     }
148 }
149
150 /* 
151  * hqx_close must be called before a second file can be opened using
152  * hqx_open.  Upon successful completion, a value of 0 is returned.  
153  * Otherwise, a value of -1 is returned.
154  */
155
156 int hqx_close( keepflag )
157     int                 keepflag;
158 {
159     if ( keepflag == KEEP ) {
160         return( close( hqx.filed ));
161     } else if ( keepflag == TRASH ) {
162         if (( strcmp( hqx.path, STDIN ) != 0 ) && ( unlink( hqx.path ) < 0 )) {
163             perror( hqx.path );
164         }
165         return( 0 );
166     } else return( -1 );
167 }
168
169 /*
170  * hqx_read is called until it returns zero for each fork.  when it is
171  * and finds that there is zero left to give, it reads in and compares
172  * the crc with the calculated one, and returns zero if all is well.
173  * it returns negative is the crc was bad or if has been called too many
174  * times for the same fork.  hqx_read must be called enough times to
175  * return zero and no more than that.
176  */
177
178 int hqx_read( fork, buffer, length )
179     int                 fork;
180     char                *buffer;
181     int                 length;
182 {
183     u_short             storedcrc;
184     int                 readlen;
185     int                 cc;
186
187 #if DEBUG >= 3
188     {
189         off_t   pos;
190         pos = lseek( hqx.filed, 0, L_INCR );
191         fprintf( stderr, "hqx_read: current position is %ld\n", pos );
192     }
193     fprintf( stderr, "hqx_read: fork is %s\n", forkname[ fork ] );
194     fprintf( stderr, "hqx_read: remaining length is %d\n", hqx.forklen[fork] );
195 #endif
196
197     if ( hqx.forklen[ fork ] < 0 ) {
198         fprintf( stderr, "This should never happen, dude!\n" );
199         return( hqx.forklen[ fork ] );
200     }
201
202     if ( hqx.forklen[ fork ] == 0 ) {
203         cc = hqx_7tobin( (char *)&storedcrc, sizeof( storedcrc ));
204         if ( cc == sizeof( storedcrc )) {
205             storedcrc = ntohs ( storedcrc );
206 #if DEBUG >= 4
207     fprintf( stderr, "hqx_read: storedcrc\t\t%x\n", storedcrc );
208     fprintf( stderr, "hqx_read: observed crc\t\t%x\n\n", hqx.forkcrc[fork] );
209 #endif
210             if ( storedcrc == hqx.forkcrc[ fork ] ) {
211                 return( 0 );
212             }
213             fprintf( stderr, "hqx_read: Bad %s fork crc, dude\n", 
214                     forkname[ fork ] );
215         }
216         return( -1 );
217     }
218
219     if ( hqx.forklen[ fork ] < length ) {
220         readlen = hqx.forklen[ fork ];
221     } else {
222         readlen = length;
223     }
224 #if DEBUG >= 3
225     fprintf( stderr, "hqx_read: readlen is %d\n", readlen );
226 #endif
227
228     cc = hqx_7tobin( buffer, readlen );
229     if ( cc > 0 ) {
230         hqx.forkcrc[ fork ] = 
231                 updcrc( hqx.forkcrc[ fork ], (u_char *)buffer, cc );
232         hqx.forklen[ fork ] -= cc;
233     }
234 #if DEBUG >= 3
235     fprintf( stderr, "hqx_read: chars read is %d\n", cc );
236 #endif
237     return( cc );
238 }
239
240 /* 
241  * hqx_header_read is called by hqx_open, and before any information can
242  * read from the hqx_header substruct.  it must be called before any
243  * of the bytes of the other two forks can be read, as well.
244  * returns a negative number if it was unable to pull enough information
245  * to fill the hqx_header fields.
246  */
247
248 int hqx_header_read( fh )
249     struct FHeader      *fh;
250 {
251     char                *headerbuf, *headerptr;
252     u_int32_t           time_seconds;
253     u_short             mask;
254     u_short             header_crc;
255     char                namelen;
256
257 #if HEXOUTPUT
258     int         headerfork;
259     headerfork = open( "headerfork", O_WRONLY|O_CREAT, 0622 );
260 #endif
261
262     mask = htons( 0xfcee );
263     hqx.headercrc = 0;
264
265     if ( hqx_7tobin( &namelen, sizeof( namelen )) == 0 ) {
266         fprintf( stderr, "Premature end of file :" );
267         return( -2 );
268     }
269     hqx.headercrc = updcrc( hqx.headercrc, (u_char *)&namelen, 
270             sizeof( namelen ));
271
272 #if HEXOUTPUT
273     write( headerfork, &namelen, sizeof( namelen ));
274 #endif
275
276     if (( headerbuf = 
277             (char *)malloc( (unsigned int)( namelen + BHH_HEADSIZ ))) == 0 ) {
278         return( -1 );
279     }
280     if ( hqx_7tobin( headerbuf, ( namelen + BHH_HEADSIZ )) == 0 ) {
281         free( headerbuf );
282         fprintf( stderr, "Premature end of file :" );
283         return( -2 );
284     }
285     headerptr = headerbuf;
286     hqx.headercrc = updcrc( hqx.headercrc, 
287             (u_char *)headerbuf, ( namelen + BHH_HEADSIZ - BHH_CRCSIZ ));
288
289 #if HEXOUTPUT
290     write( headerfork, headerbuf, ( namelen + BHH_HEADSIZ ));
291 #endif
292
293 /*
294  * stuff from the hqx file header
295  */
296
297     memcpy( fh->name, headerptr, (int)namelen );
298     headerptr += namelen;
299     headerptr += BVERSION;
300     memcpy(&fh->finder_info,  headerptr, TCSIZ );
301     headerptr += TCSIZ;
302     memcpy(&fh->finder_info.fdFlags,  headerptr, FLAGSIZ );
303     fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
304     headerptr += BHH_FLAGSIZ;
305     memcpy(&fh->forklen[ DATA ],  headerptr, BHH_DATASIZ );
306     hqx.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] );
307     headerptr += BHH_DATASIZ;
308     memcpy( &fh->forklen[ RESOURCE ], headerptr, BHH_RESSIZ );
309     hqx.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] );
310     headerptr += BHH_RESSIZ;
311     memcpy(&header_crc,  headerptr, BHH_CRCSIZ );
312     headerptr += BHH_CRCSIZ;
313     header_crc = ntohs( header_crc );
314
315 /*
316  * stuff that should be zero'ed out
317  */
318
319     fh->comment[0] = '\0';
320     fh->finder_info.fdLocation = 0;
321     fh->finder_info.fdFldr = 0;
322
323 #if DEBUG >= 5
324     {
325         short           flags;
326
327         fprintf( stderr, "Values read by hqx_header_read\n" );
328         fprintf( stderr, "name length\t\t%d\n", namelen );
329         fprintf( stderr, "file name\t\t%s\n", fh->name );
330         fprintf( stderr, "get info comment\t%s\n", fh->comment );
331         fprintf( stderr, "type\t\t\t%.*s\n", sizeof( fh->finder_info.fdType ),
332                 &fh->finder_info.fdType );
333         fprintf( stderr, "creator\t\t\t%.*s\n", 
334                 sizeof( fh->finder_info.fdCreator ), 
335                 &fh->finder_info.fdCreator );
336         memcpy( &flags, &fh->finder_info.fdFlags, sizeof( flags ));
337         flags = ntohs( flags );
338         fprintf( stderr, "flags\t\t\t%x\n", flags );
339         fprintf( stderr, "data fork length\t%ld\n", hqx.forklen[DATA] );
340         fprintf( stderr, "resource fork length\t%ld\n", hqx.forklen[RESOURCE] );
341         fprintf( stderr, "header_crc\t\t%x\n", header_crc );
342         fprintf( stderr, "observed crc\t\t%x\n", hqx.headercrc );
343         fprintf( stderr, "\n" );
344     }
345 #endif
346
347 /*
348  * create and modify times are figured from right now
349  */
350
351     time_seconds = AD_DATE_FROM_UNIX(time( NULL ));
352     memcpy( &fh->create_date, &time_seconds, 
353             sizeof( fh->create_date ));
354     memcpy( &fh->mod_date, &time_seconds, 
355             sizeof( fh->mod_date ));
356     fh->backup_date = AD_DATE_START;
357
358 /*
359  * stuff that should be zero'ed out
360  */
361
362     fh->comment[0] = '\0';
363     memset( &fh->finder_info.fdLocation, 0, 
364             sizeof( fh->finder_info.fdLocation ));
365     memset( &fh->finder_info.fdFldr, 0, sizeof( fh->finder_info.fdFldr ));
366
367     hqx.forkcrc[ DATA ] = 0;
368     hqx.forkcrc[ RESOURCE ] = 0;
369
370     free( headerbuf );
371     if ( header_crc != hqx.headercrc ) {
372         fprintf( stderr, "Bad Header crc, dude :" );
373         return( -3 );
374     }
375     return( 0 );
376 }
377
378 /*
379  * hqx_header_write.
380  */
381
382 int hqx_header_write( fh )
383     struct FHeader      *fh;
384 {
385     return( -1 );
386 }
387
388 /*
389  * hqx7_fill is called from skip_junk and hqx_7tobin.  it pulls from the
390  * binhqx file into the hqx7 buffer.  returns number of bytes read
391  * or a zero for end of file.
392  * it sets the pointers to the hqx7 buffer up to point to the valid data.
393  */
394
395 int hqx7_fill( hqx7_ptr )
396     u_char              *hqx7_ptr;
397 {
398     int                 cc;
399     int                 cs;
400
401     cs = hqx7_ptr - hqx7_buf;
402     if ( cs >= sizeof( hqx7_buf )) return( -1 );
403     hqx7_first = hqx7_ptr;
404     cc = read( hqx.filed, (char *)hqx7_first, ( sizeof( hqx7_buf ) - cs ));
405     if ( cc < 0 ) {
406         perror( "" );
407         return( cc );
408     }
409     hqx7_last = ( hqx7_first + cc );
410     return( cc );
411 }
412
413 /*
414 char tr[] = "!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr";
415              0 123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
416              0                1               2               3 
417 Input characters are translated to a number between 0 and 63 by direct
418 array lookup.  0xFF signals a bad character.  0xFE is signals a legal
419 character that should be skipped, namely '\n', '\r'.  0xFD signals ':'.
420 0xFC signals a whitespace character.
421 */
422
423 u_char hqxlookup[] = {
424     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
425     0xFF, 0xFC, 0xFE, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF,
426     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
427     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
428     0xFC, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
429     0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0xFF, 0xFF,
430     0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0xFF,
431     0x14, 0x15, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
432     0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
433     0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0xFF,
434     0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0xFF,
435     0x2C, 0x2D, 0x2E, 0x2F, 0xFF, 0xFF, 0xFF, 0xFF,
436     0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0xFF,
437     0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0xFF, 0xFF,
438     0x3D, 0x3E, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
439     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
440     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
441     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
442     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
443     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
444     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
445     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
446     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
447     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
448     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
449     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
450     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
451     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
452     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
453     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
454     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
455     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
456 };
457
458 /*
459  * skip_junk is called from hqx_open.  it skips over junk in the file until
460  * it comes to a line containing a valid first line of binhqx encoded file.
461  * returns a 0 for success, negative if it never finds good data.
462  * pass a FIRST when looking for the first valid binhex line, a value of 
463  * OTHER when looking for any subsequent line.
464  */
465
466 int skip_junk( line )
467 int                     line;
468 {
469     int                 found = NOWAY;
470     int                 stopflag;
471     int                 nc = 0;
472     u_char              c;
473     u_char              prevchar;
474
475     if ( line == FIRST ) {
476         if ( hqx7_fill( hqx7_buf  ) <= 0 ) {
477             fprintf( stderr, "Premature end of file :" );
478             return( -1 );
479         }
480     }
481
482     while ( found == NOWAY ) {
483         if ( line == FIRST ) {
484             if ( *hqx7_first == ':' ) {
485                 nc = c = 0;
486                 stopflag = NOWAY;
487                 hqx7_first++;
488                 while (( stopflag == NOWAY ) && 
489                         ( nc < ( hqx7_last - hqx7_first ))) {
490                     switch ( c = hqxlookup[ hqx7_first[ nc ]] ) {
491                         case 0xFC :
492                         case 0xFF :
493                         case 0xFE :
494                         case 0xFD :
495                             stopflag = SURETHANG;
496                             break;
497                         default :
498                             nc++;
499                             break;
500                     }
501                 }
502                 if (( nc > 30 ) && ( nc < 64 ) &&
503                         (( c == 0xFE ) || ( c == 0xFD ))) found = SURETHANG;
504             } else {
505                 hqx7_first++;
506             }
507         } else {
508             if (( prevchar = hqxlookup[ *hqx7_first ] ) == 0xFE ) {
509                 nc = c = 0;
510                 stopflag = NOWAY;
511                 hqx7_first++;
512                 while (( stopflag == NOWAY ) && 
513                         ( nc < ( hqx7_last - hqx7_first ))) {
514                     switch ( c = hqxlookup[ hqx7_first[ nc ]] ) {
515                         case 0xFC :
516                         case 0xFE :
517                             if (( prevchar == 0xFC ) || ( prevchar == 0xFE )) {
518                                 nc++;
519                                 break;
520                             }
521                         case 0xFF :
522                         case 0xFD :
523                             stopflag = SURETHANG;
524                             break;
525                         default :
526                             prevchar = c;
527                             nc++;
528                             break;
529                     }
530                 }
531                 if ( c == 0xFD ) {
532                     found = SURETHANG;
533                 } else if (( nc > 30 ) && ( c == 0xFE )) {
534                     found = SURETHANG;
535                 }
536             } else {
537                 hqx7_first++;
538             }
539         }
540
541         if (( hqx7_last - hqx7_first ) == nc ) {
542             if ( line == FIRST ) {
543                 *hqx7_buf = ':';
544             } else *hqx7_buf = '\n';
545             memcpy(hqx7_buf + 1, hqx7_first, nc );
546             hqx7_first = hqx7_buf + ( ++nc );
547             if ( hqx7_fill( hqx7_first ) <= 0 ) {
548                 fprintf( stderr, "Premature end of file :" );
549                 return( -1 );
550             }
551             hqx7_first = hqx7_buf;
552         }
553     }
554
555     return( 0 );
556 }
557
558 /* 
559  * hqx_7tobin is used to read the data, converted to binary.  It is
560  * called by hqx_header_read to get the header information, and must be
561  * called to get the data for each fork, and the crc data for each
562  * fork.  it has the same basic calling structure as unix read.  the
563  * number of valid bytes read is returned.  It does buffering so as to
564  * return the requested length of data every time, unless the end of
565  * file is reached.
566  */
567
568 int hqx_7tobin( outbuf, datalen ) 
569     char                *outbuf;
570     int                 datalen;
571 {
572     static u_char       hqx8[3];
573     static int          hqx8i;
574     static u_char       prev_hqx8;
575     static u_char       prev_out;
576     static u_char       prev_hqx7;
577     static int          eofflag;
578     u_char              hqx7[4];
579     int                 hqx7i = 0;
580     char                *out_first;
581     char                *out_last;
582
583 #if DEBUG
584     fprintf( stderr, "hqx_7tobin: datalen entering %d\n", datalen );
585     fprintf( stderr, "hqx_7tobin: hqx8i entering %d\n", hqx8i );
586 #endif
587
588     if ( first_flag == 0 ) {
589         prev_hqx8 = 0;
590         prev_hqx7 = 0;
591         prev_out = 0;
592         hqx8i = 3;
593         first_flag = 1;
594         eofflag = 0;
595     }
596
597 #if DEBUG
598     fprintf( stderr, "hqx_7tobin: hqx8i entering %d\n", hqx8i );
599 #endif
600
601     out_first = outbuf;
602     out_last = out_first + datalen;
603
604     while (( out_first < out_last ) && ( eofflag == 0 )) {
605
606         if ( hqx7_first == hqx7_last ) {
607             if ( hqx7_fill( hqx7_buf ) == 0 ) {
608                 eofflag = 1;
609                 continue;
610             }
611         }
612
613         if ( hqx8i > 2 ) {
614
615             while (( hqx7i < 4 ) && ( hqx7_first < hqx7_last )) {
616                 hqx7[ hqx7i ] = hqxlookup[ *hqx7_first ];
617                 switch ( hqx7[ hqx7i ] ) {
618                     case 0xFC :
619                         if (( prev_hqx7 == 0xFC ) || ( prev_hqx7 == 0xFE )) {
620                             hqx7_first++;
621                             break;
622                         }
623                     case 0xFD :
624                     case 0xFF :
625                         eofflag = 1;
626                         while ( hqx7i < 4 ) {
627                             hqx7[ hqx7i++ ] = 0;
628                         }
629                         break;
630                     case 0xFE :
631                         prev_hqx7 = hqx7[ hqx7i ];
632                         if ( skip_junk( OTHER ) < 0 ) {
633                             fprintf( stderr, "\n" );
634                             eofflag = 1;
635                             while ( hqx7i < 4 ) {
636                                 hqx7[ hqx7i++ ] = 0; }
637                         }
638                         break;
639                     default :
640                         prev_hqx7 = hqx7[ hqx7i++ ];
641                         hqx7_first++;
642                         break;
643                 }
644             }
645             
646             if ( hqx7i == 4 ) {
647                 hqx8[ 0 ] = (( hqx7[ 0 ] << 2 ) | ( hqx7[ 1 ] >> 4 ));
648                 hqx8[ 1 ] = (( hqx7[ 1 ] << 4 ) | ( hqx7[ 2 ] >> 2 ));
649                 hqx8[ 2 ] = (( hqx7[ 2 ] << 6 ) | ( hqx7[ 3 ] ));
650                 hqx7i = hqx8i = 0;
651             }
652         }
653
654         while (( hqx8i < 3 ) && ( out_first < out_last )) {
655
656 #if HEXOUTPUT
657             putc( hqx8i, rawhex );
658             putc( hqx8[ hqx8i ], rawhex );
659 #endif
660
661             if ( prev_hqx8 == RUNCHAR ) {
662                 if ( hqx8[ hqx8i ] == 0 ) {
663                     *out_first = prev_hqx8;
664 #if HEXOUTPUT
665                     putc( *out_first, expandhex );
666 #endif
667                     prev_out = prev_hqx8;
668                     out_first++;
669                 }
670                 while (( out_first < out_last ) && ( hqx8[ hqx8i ] > 1 )) {
671                     *out_first = prev_out;
672 #if HEXOUTPUT
673                     putc( *out_first, expandhex );
674 #endif
675                     hqx8[ hqx8i ]--;
676                     out_first++;
677                 }
678                 if ( hqx8[ hqx8i ] < 2 ) {
679                     prev_hqx8 = hqx8[ hqx8i ];
680                     hqx8i++;
681                 }
682                 continue;
683             }
684
685             prev_hqx8 = hqx8[ hqx8i ];
686             if ( prev_hqx8 != RUNCHAR ) {
687                 *out_first = prev_hqx8;
688 #if HEXOUTPUT
689                 putc( *out_first, expandhex );
690 #endif
691                 prev_out = prev_hqx8;
692                 out_first++;
693             }
694             hqx8i++;
695
696         }
697
698     }
699     return( out_first - outbuf );
700 }