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