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