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