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