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