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