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