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