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