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