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