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