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