• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/timemachine/netatalk-2.2.5/bin/megatron/
1/*
2 * $Id: hqx.c,v 1.18 2010-01-27 21:27:53 didg Exp $
3 */
4
5#ifdef HAVE_CONFIG_H
6#include "config.h"
7#endif /* HAVE_CONFIG_H */
8
9#include <sys/types.h>
10#include <sys/uio.h>
11#include <sys/time.h>
12#include <sys/param.h>
13
14#include <string.h>
15#include <ctype.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <time.h>
19
20#include <unistd.h>
21#ifdef HAVE_FCNTL_H
22#include <fcntl.h>
23#endif /* HAVE_FCNTL_H */
24
25#include <netinet/in.h>
26
27#include <atalk/adouble.h>
28#include <netatalk/endian.h>
29
30#include "megatron.h"
31#include "nad.h"
32#include "hqx.h"
33#include "updcrc.h"
34
35#define HEXOUTPUT	0
36
37/*	String used to indicate standard input instead of a disk
38	file.  Should be a string not normally used for a file
39 */
40#ifndef	STDIN
41#	define	STDIN	"-"
42#endif /* ! STDIN */
43
44/*	Yes and no
45 */
46#define NOWAY		0
47#define SURETHANG	1
48
49/*	Looking for the first or any other line of a binhex file
50 */
51#define	FIRST		0
52#define OTHER		1
53
54/*	This is the binhex run length encoding character
55 */
56#define RUNCHAR		0x90
57
58/*	These are field sizes in bytes of various pieces of the
59	binhex header
60 */
61#define	BHH_VERSION		1
62#define	BHH_TCSIZ		8
63#define	BHH_FLAGSIZ		2
64#define	BHH_DATASIZ		4
65#define	BHH_RESSIZ		4
66#define BHH_CRCSIZ		2
67#define BHH_HEADSIZ		21
68
69#if HEXOUTPUT
70FILE		*rawhex, *expandhex;
71#endif /* HEXOUTPUT */
72
73static struct hqx_file_data {
74    u_int32_t		forklen[ NUMFORKS ];
75    u_short		forkcrc[ NUMFORKS ];
76    char		path[ MAXPATHLEN + 1];
77    u_short		headercrc;
78    int			filed;
79} 		hqx;
80
81extern char	*forkname[];
82static u_char	hqx7_buf[8192];
83static u_char	*hqx7_first;
84static u_char	*hqx7_last;
85static int	first_flag;
86
87/*
88hqx_open must be called first.  pass it a filename that is supposed
89to contain a binhqx file.  an hqx struct will be allocated and
90somewhat initialized; hqx_fd is set.  skip_junk is called from
91here; skip_junk leaves hqx7_first and hqx7_last set.
92 */
93
94int hqx_open(char *hqxfile, int flags, struct FHeader *fh, int options)
95{
96    int			maxlen;
97
98#if DEBUG
99    fprintf( stderr, "megatron: entering hqx_open\n" );
100#endif /* DEBUG */
101    select_charset( options);
102    if ( flags == O_RDONLY ) {
103
104#if HEXOUTPUT
105	rawhex = fopen( "rawhex.unhex", "w" );
106	expandhex = fopen( "expandhex.unhex", "w" );
107#endif /* HEXOUTPUT */
108
109	first_flag = 0;
110
111	if ( strcmp( hqxfile, STDIN ) == 0 ) {
112	    hqx.filed = fileno( stdin );
113	} else if (( hqx.filed = open( hqxfile, O_RDONLY )) < 0 ) {
114	    perror( hqxfile );
115	    return( -1 );
116	}
117
118	if ( skip_junk( FIRST ) == 0 ) {
119	    if ( hqx_header_read( fh ) == 0 ) {
120#if DEBUG
121		off_t	pos;
122
123		pos = lseek( hqx.filed, 0, SEEK_CUR );
124		fprintf( stderr, "megatron: current position is %ld\n", pos );
125#endif /* DEBUG */
126		return( 0 );
127	    }
128	}
129	hqx_close( KEEP );
130	fprintf( stderr, "%s\n", hqxfile );
131	return( -1 );
132    } else {
133	maxlen = sizeof( hqx.path ) -1;
134	strncpy( hqx.path, fh->name, maxlen );
135	strncpy( hqx.path, mtoupath( hqx.path ), maxlen );
136	strncat( hqx.path, ".hqx", maxlen - strlen( hqx.path ));
137	if (( hqx.filed = open( hqx.path, flags, 0666 )) < 0 ) {
138	    perror( hqx.path );
139	    return( -1 );
140	}
141	if ( hqx_header_write( fh ) != 0 ) {
142	    hqx_close( TRASH );
143	    fprintf( stderr, "%s\n", hqx.path );
144	    return( -1 );
145	}
146	return( 0 );
147    }
148}
149
150/*
151 * hqx_close must be called before a second file can be opened using
152 * hqx_open.  Upon successful completion, a value of 0 is returned.
153 * Otherwise, a value of -1 is returned.
154 */
155
156int hqx_close(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
177ssize_t hqx_read(int fork, char *buffer, size_t length)
178{
179    u_short		storedcrc;
180    size_t		readlen;
181    size_t		cc;
182
183#if DEBUG >= 3
184    {
185	off_t	pos;
186	pos = lseek( hqx.filed, 0, SEEK_CUR );
187	fprintf( stderr, "hqx_read: current position is %ld\n", pos );
188    }
189    fprintf( stderr, "hqx_read: fork is %s\n", forkname[ fork ] );
190    fprintf( stderr, "hqx_read: remaining length is %d\n", hqx.forklen[fork] );
191#endif /* DEBUG >= 3 */
192
193    if (hqx.forklen[fork] > 0x7FFFFFFF) {
194	fprintf(stderr, "This should never happen, dude!, fork length == %u\n", hqx.forklen[fork]);
195	return -1;
196    }
197
198    if ( hqx.forklen[ fork ] == 0 ) {
199	cc = hqx_7tobin( (char *)&storedcrc, sizeof( storedcrc ));
200	if ( cc == sizeof( storedcrc )) {
201	    storedcrc = ntohs ( storedcrc );
202#if DEBUG >= 4
203    fprintf( stderr, "hqx_read: storedcrc\t\t%x\n", storedcrc );
204    fprintf( stderr, "hqx_read: observed crc\t\t%x\n\n", hqx.forkcrc[fork] );
205#endif /* DEBUG >= 4 */
206	    if ( storedcrc == hqx.forkcrc[ fork ] ) {
207		return( 0 );
208	    }
209	    fprintf( stderr, "hqx_read: Bad %s fork crc, dude\n",
210		    forkname[ fork ] );
211	}
212	return( -1 );
213    }
214
215    if ( hqx.forklen[ fork ] < length ) {
216	readlen = hqx.forklen[ fork ];
217    } else {
218	readlen = length;
219    }
220#if DEBUG >= 3
221    fprintf( stderr, "hqx_read: readlen is %d\n", readlen );
222#endif /* DEBUG >= 3 */
223
224    cc = hqx_7tobin( buffer, readlen );
225    if ( cc > 0 ) {
226	hqx.forkcrc[ fork ] =
227		updcrc( hqx.forkcrc[ fork ], (u_char *)buffer, cc );
228	hqx.forklen[ fork ] -= cc;
229    }
230#if DEBUG >= 3
231    fprintf( stderr, "hqx_read: chars read is %d\n", cc );
232#endif /* DEBUG >= 3 */
233    return( cc );
234}
235
236/*
237 * hqx_header_read is called by hqx_open, and before any information can
238 * read from the hqx_header substruct.  it must be called before any
239 * of the bytes of the other two forks can be read, as well.
240 * returns a negative number if it was unable to pull enough information
241 * to fill the hqx_header fields.
242 */
243
244int hqx_header_read(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 /* HEXOUTPUT */
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 /* HEXOUTPUT */
270
271    if (( headerbuf =
272	    (char *)malloc( (unsigned int)( namelen + BHH_HEADSIZ ))) == NULL ) {
273	return( -1 );
274    }
275    if ( hqx_7tobin( headerbuf, ( namelen + BHH_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 + BHH_HEADSIZ - BHH_CRCSIZ ));
283
284#if HEXOUTPUT
285    write( headerfork, headerbuf, ( namelen + BHH_HEADSIZ ));
286#endif /* HEXOUTPUT */
287
288/*
289 * stuff from the hqx file header
290 */
291
292    memcpy( fh->name, headerptr, (int)namelen );
293    headerptr += namelen;
294    headerptr += BHH_VERSION;
295    memcpy(&fh->finder_info,  headerptr, BHH_TCSIZ );
296    headerptr += BHH_TCSIZ;
297    memcpy(&fh->finder_info.fdFlags,  headerptr, BHH_FLAGSIZ );
298    fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
299    headerptr += BHH_FLAGSIZ;
300    memcpy(&fh->forklen[ DATA ],  headerptr, BHH_DATASIZ );
301    hqx.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] );
302    headerptr += BHH_DATASIZ;
303    memcpy( &fh->forklen[ RESOURCE ], headerptr, BHH_RESSIZ );
304    hqx.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] );
305    headerptr += BHH_RESSIZ;
306    memcpy(&header_crc,  headerptr, BHH_CRCSIZ );
307    headerptr += BHH_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 /* DEBUG >= 5 */
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
377int hqx_header_write(struct FHeader *fh _U_)
378{
379    return( -1 );
380}
381
382/*
383 * hqx7_fill is called from skip_junk and hqx_7tobin.  it pulls from the
384 * binhqx file into the hqx7 buffer.  returns number of bytes read
385 * or a zero for end of file.
386 * it sets the pointers to the hqx7 buffer up to point to the valid data.
387 */
388
389ssize_t hqx7_fill(u_char *hqx7_ptr)
390{
391    ssize_t		cc;
392    size_t		cs;
393
394    cs = hqx7_ptr - hqx7_buf;
395    if ( cs >= sizeof( hqx7_buf )) return( -1 );
396    hqx7_first = hqx7_ptr;
397    cc = read( hqx.filed, (char *)hqx7_first, ( sizeof( hqx7_buf ) - cs ));
398    if ( cc < 0 ) {
399	perror( "" );
400	return( cc );
401    }
402    hqx7_last = ( hqx7_first + cc );
403    return( cc );
404}
405
406/*
407char tr[] = "!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr";
408	     0 123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
409	     0                1               2               3
410Input characters are translated to a number between 0 and 63 by direct
411array lookup.  0xFF signals a bad character.  0xFE is signals a legal
412character that should be skipped, namely '\n', '\r'.  0xFD signals ':'.
4130xFC signals a whitespace character.
414*/
415
416static const u_char hqxlookup[] = {
417    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
418    0xFF, 0xFC, 0xFE, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF,
419    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
420    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
421    0xFC, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
422    0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0xFF, 0xFF,
423    0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0xFF,
424    0x14, 0x15, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
425    0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
426    0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0xFF,
427    0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0xFF,
428    0x2C, 0x2D, 0x2E, 0x2F, 0xFF, 0xFF, 0xFF, 0xFF,
429    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0xFF,
430    0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0xFF, 0xFF,
431    0x3D, 0x3E, 0x3F, 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    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
449};
450
451/*
452 * skip_junk is called from hqx_open.  it skips over junk in the file until
453 * it comes to a line containing a valid first line of binhqx encoded file.
454 * returns a 0 for success, negative if it never finds good data.
455 * pass a FIRST when looking for the first valid binhex line, a value of
456 * OTHER when looking for any subsequent line.
457 */
458
459int skip_junk(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
560size_t hqx_7tobin( char *outbuf, size_t datalen)
561{
562    static u_char	hqx8[3];
563    static int		hqx8i;
564    static u_char	prev_hqx8;
565    static u_char	prev_out;
566    static u_char	prev_hqx7;
567    static int		eofflag;
568    u_char		hqx7[4];
569    int			hqx7i = 0;
570    char		*out_first;
571    char		*out_last;
572
573#if DEBUG
574    fprintf( stderr, "hqx_7tobin: datalen entering %d\n", datalen );
575    fprintf( stderr, "hqx_7tobin: hqx8i entering %d\n", hqx8i );
576#endif /* DEBUG */
577
578    if ( first_flag == 0 ) {
579	prev_hqx8 = 0;
580	prev_hqx7 = 0;
581	prev_out = 0;
582	hqx8i = 3;
583	first_flag = 1;
584	eofflag = 0;
585    }
586
587#if DEBUG
588    fprintf( stderr, "hqx_7tobin: hqx8i entering %d\n", hqx8i );
589#endif /* DEBUG */
590
591    out_first = outbuf;
592    out_last = out_first + datalen;
593
594    while (( out_first < out_last ) && ( eofflag == 0 )) {
595
596	if ( hqx7_first == hqx7_last ) {
597	    if ( hqx7_fill( hqx7_buf ) == 0 ) {
598		eofflag = 1;
599		continue;
600	    }
601	}
602
603	if ( hqx8i > 2 ) {
604
605	    while (( hqx7i < 4 ) && ( hqx7_first < hqx7_last )) {
606		hqx7[ hqx7i ] = hqxlookup[ *hqx7_first ];
607		switch ( hqx7[ hqx7i ] ) {
608		    case 0xFC :
609			if (( prev_hqx7 == 0xFC ) || ( prev_hqx7 == 0xFE )) {
610			    hqx7_first++;
611			    break;
612			}
613		    case 0xFD :
614		    case 0xFF :
615			eofflag = 1;
616			while ( hqx7i < 4 ) {
617			    hqx7[ hqx7i++ ] = 0;
618			}
619			break;
620		    case 0xFE :
621			prev_hqx7 = hqx7[ hqx7i ];
622			if ( skip_junk( OTHER ) < 0 ) {
623			    fprintf( stderr, "\n" );
624			    eofflag = 1;
625			    while ( hqx7i < 4 ) {
626				hqx7[ hqx7i++ ] = 0; }
627			}
628			break;
629		    default :
630			prev_hqx7 = hqx7[ hqx7i++ ];
631			hqx7_first++;
632			break;
633		}
634	    }
635
636	    if ( hqx7i == 4 ) {
637		hqx8[ 0 ] = (( hqx7[ 0 ] << 2 ) | ( hqx7[ 1 ] >> 4 ));
638		hqx8[ 1 ] = (( hqx7[ 1 ] << 4 ) | ( hqx7[ 2 ] >> 2 ));
639		hqx8[ 2 ] = (( hqx7[ 2 ] << 6 ) | ( hqx7[ 3 ] ));
640		hqx7i = hqx8i = 0;
641	    }
642	}
643
644	while (( hqx8i < 3 ) && ( out_first < out_last )) {
645
646#if HEXOUTPUT
647	    putc( hqx8i, rawhex );
648            putc( hqx8[ hqx8i ], rawhex );
649#endif /* HEXOUTPUT */
650
651	    if ( prev_hqx8 == RUNCHAR ) {
652		if ( hqx8[ hqx8i ] == 0 ) {
653		    *out_first = prev_hqx8;
654#if HEXOUTPUT
655		    putc( *out_first, expandhex );
656#endif /* HEXOUTPUT */
657		    prev_out = prev_hqx8;
658		    out_first++;
659		}
660		while (( out_first < out_last ) && ( hqx8[ hqx8i ] > 1 )) {
661		    *out_first = prev_out;
662#if HEXOUTPUT
663		    putc( *out_first, expandhex );
664#endif /* HEXOUTPUT */
665		    hqx8[ hqx8i ]--;
666		    out_first++;
667		}
668		if ( hqx8[ hqx8i ] < 2 ) {
669		    prev_hqx8 = hqx8[ hqx8i ];
670		    hqx8i++;
671		}
672		continue;
673	    }
674
675	    prev_hqx8 = hqx8[ hqx8i ];
676	    if ( prev_hqx8 != RUNCHAR ) {
677		*out_first = prev_hqx8;
678#if HEXOUTPUT
679		putc( *out_first, expandhex );
680#endif /* HEXOUTPUT */
681		prev_out = prev_hqx8;
682		out_first++;
683	    }
684	    hqx8i++;
685
686	}
687
688    }
689    return( out_first - outbuf );
690}
691