1 /* 2 * Copyright (c) 1988-1997 Sam Leffler 3 * Copyright (c) 1991-1997 Silicon Graphics, Inc. 4 * 5 * Permission to use, copy, modify, distribute, and sell this software and 6 * its documentation for any purpose is hereby granted without fee, provided 7 * that (i) the above copyright notices and this permission notice appear in 8 * all copies of the software and related documentation, and (ii) the names of 9 * Sam Leffler and Silicon Graphics may not be used in any advertising or 10 * publicity relating to the software without the specific, prior written 11 * permission of Sam Leffler and Silicon Graphics. 12 * 13 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 14 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 15 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 16 * 17 * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR 18 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, 19 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 20 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 21 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 22 * OF THIS SOFTWARE. 23 */ 24 25 /* 26 * TIFF Library. 27 */ 28 29 #include <precomp.h> 30 31 /* 32 * Dummy functions to fill the omitted client procedures. 33 */ 34 static int 35 _tiffDummyMapProc(thandle_t fd, void** pbase, toff_t* psize) 36 { 37 (void) fd; (void) pbase; (void) psize; 38 return (0); 39 } 40 41 static void 42 _tiffDummyUnmapProc(thandle_t fd, void* base, toff_t size) 43 { 44 (void) fd; (void) base; (void) size; 45 } 46 47 int 48 _TIFFgetMode(const char* mode, const char* module) 49 { 50 int m = -1; 51 52 switch (mode[0]) { 53 case 'r': 54 m = O_RDONLY; 55 if (mode[1] == '+') 56 m = O_RDWR; 57 break; 58 case 'w': 59 case 'a': 60 m = O_RDWR|O_CREAT; 61 if (mode[0] == 'w') 62 m |= O_TRUNC; 63 break; 64 default: 65 TIFFErrorExt(0, module, "\"%s\": Bad mode", mode); 66 break; 67 } 68 return (m); 69 } 70 71 TIFF* 72 TIFFClientOpen( 73 const char* name, const char* mode, 74 thandle_t clientdata, 75 TIFFReadWriteProc readproc, 76 TIFFReadWriteProc writeproc, 77 TIFFSeekProc seekproc, 78 TIFFCloseProc closeproc, 79 TIFFSizeProc sizeproc, 80 TIFFMapFileProc mapproc, 81 TIFFUnmapFileProc unmapproc 82 ) 83 { 84 static const char module[] = "TIFFClientOpen"; 85 TIFF *tif; 86 int m; 87 const char* cp; 88 89 /* The following are configuration checks. They should be redundant, but should not 90 * compile to any actual code in an optimised release build anyway. If any of them 91 * fail, (makefile-based or other) configuration is not correct */ 92 assert(sizeof(uint8)==1); 93 assert(sizeof(int8)==1); 94 assert(sizeof(uint16)==2); 95 assert(sizeof(int16)==2); 96 assert(sizeof(uint32)==4); 97 assert(sizeof(int32)==4); 98 assert(sizeof(uint64)==8); 99 assert(sizeof(int64)==8); 100 assert(sizeof(tmsize_t)==sizeof(void*)); 101 { 102 union{ 103 uint8 a8[2]; 104 uint16 a16; 105 } n; 106 n.a8[0]=1; 107 n.a8[1]=0; 108 #ifdef WORDS_BIGENDIAN 109 assert(n.a16==256); 110 #else 111 assert(n.a16==1); 112 #endif 113 } 114 115 m = _TIFFgetMode(mode, module); 116 if (m == -1) 117 goto bad2; 118 tif = (TIFF *)_TIFFmalloc((tmsize_t)(sizeof (TIFF) + strlen(name) + 1)); 119 if (tif == NULL) { 120 TIFFErrorExt(clientdata, module, "%s: Out of memory (TIFF structure)", name); 121 goto bad2; 122 } 123 _TIFFmemset(tif, 0, sizeof (*tif)); 124 tif->tif_name = (char *)tif + sizeof (TIFF); 125 strcpy(tif->tif_name, name); 126 tif->tif_mode = m &~ (O_CREAT|O_TRUNC); 127 tif->tif_curdir = (uint16) -1; /* non-existent directory */ 128 tif->tif_curoff = 0; 129 tif->tif_curstrip = (uint32) -1; /* invalid strip */ 130 tif->tif_row = (uint32) -1; /* read/write pre-increment */ 131 tif->tif_clientdata = clientdata; 132 if (!readproc || !writeproc || !seekproc || !closeproc || !sizeproc) { 133 TIFFErrorExt(clientdata, module, 134 "One of the client procedures is NULL pointer."); 135 goto bad2; 136 } 137 tif->tif_readproc = readproc; 138 tif->tif_writeproc = writeproc; 139 tif->tif_seekproc = seekproc; 140 tif->tif_closeproc = closeproc; 141 tif->tif_sizeproc = sizeproc; 142 if (mapproc) 143 tif->tif_mapproc = mapproc; 144 else 145 tif->tif_mapproc = _tiffDummyMapProc; 146 if (unmapproc) 147 tif->tif_unmapproc = unmapproc; 148 else 149 tif->tif_unmapproc = _tiffDummyUnmapProc; 150 _TIFFSetDefaultCompressionState(tif); /* setup default state */ 151 /* 152 * Default is to return data MSB2LSB and enable the 153 * use of memory-mapped files and strip chopping when 154 * a file is opened read-only. 155 */ 156 tif->tif_flags = FILLORDER_MSB2LSB; 157 if (m == O_RDONLY ) 158 tif->tif_flags |= TIFF_MAPPED; 159 160 #ifdef STRIPCHOP_DEFAULT 161 if (m == O_RDONLY || m == O_RDWR) 162 tif->tif_flags |= STRIPCHOP_DEFAULT; 163 #endif 164 165 /* 166 * Process library-specific flags in the open mode string. 167 * The following flags may be used to control intrinsic library 168 * behaviour that may or may not be desirable (usually for 169 * compatibility with some application that claims to support 170 * TIFF but only supports some brain dead idea of what the 171 * vendor thinks TIFF is): 172 * 173 * 'l' use little-endian byte order for creating a file 174 * 'b' use big-endian byte order for creating a file 175 * 'L' read/write information using LSB2MSB bit order 176 * 'B' read/write information using MSB2LSB bit order 177 * 'H' read/write information using host bit order 178 * 'M' enable use of memory-mapped files when supported 179 * 'm' disable use of memory-mapped files 180 * 'C' enable strip chopping support when reading 181 * 'c' disable strip chopping support 182 * 'h' read TIFF header only, do not load the first IFD 183 * '4' ClassicTIFF for creating a file (default) 184 * '8' BigTIFF for creating a file 185 * 186 * The use of the 'l' and 'b' flags is strongly discouraged. 187 * These flags are provided solely because numerous vendors, 188 * typically on the PC, do not correctly support TIFF; they 189 * only support the Intel little-endian byte order. This 190 * support is not configured by default because it supports 191 * the violation of the TIFF spec that says that readers *MUST* 192 * support both byte orders. It is strongly recommended that 193 * you not use this feature except to deal with busted apps 194 * that write invalid TIFF. And even in those cases you should 195 * bang on the vendors to fix their software. 196 * 197 * The 'L', 'B', and 'H' flags are intended for applications 198 * that can optimize operations on data by using a particular 199 * bit order. By default the library returns data in MSB2LSB 200 * bit order for compatibility with older versions of this 201 * library. Returning data in the bit order of the native CPU 202 * makes the most sense but also requires applications to check 203 * the value of the FillOrder tag; something they probably do 204 * not do right now. 205 * 206 * The 'M' and 'm' flags are provided because some virtual memory 207 * systems exhibit poor behaviour when large images are mapped. 208 * These options permit clients to control the use of memory-mapped 209 * files on a per-file basis. 210 * 211 * The 'C' and 'c' flags are provided because the library support 212 * for chopping up large strips into multiple smaller strips is not 213 * application-transparent and as such can cause problems. The 'c' 214 * option permits applications that only want to look at the tags, 215 * for example, to get the unadulterated TIFF tag information. 216 */ 217 for (cp = mode; *cp; cp++) 218 switch (*cp) { 219 case 'b': 220 #ifndef WORDS_BIGENDIAN 221 if (m&O_CREAT) 222 tif->tif_flags |= TIFF_SWAB; 223 #endif 224 break; 225 case 'l': 226 #ifdef WORDS_BIGENDIAN 227 if ((m&O_CREAT)) 228 tif->tif_flags |= TIFF_SWAB; 229 #endif 230 break; 231 case 'B': 232 tif->tif_flags = (tif->tif_flags &~ TIFF_FILLORDER) | 233 FILLORDER_MSB2LSB; 234 break; 235 case 'L': 236 tif->tif_flags = (tif->tif_flags &~ TIFF_FILLORDER) | 237 FILLORDER_LSB2MSB; 238 break; 239 case 'H': 240 tif->tif_flags = (tif->tif_flags &~ TIFF_FILLORDER) | 241 HOST_FILLORDER; 242 break; 243 case 'M': 244 if (m == O_RDONLY) 245 tif->tif_flags |= TIFF_MAPPED; 246 break; 247 case 'm': 248 if (m == O_RDONLY) 249 tif->tif_flags &= ~TIFF_MAPPED; 250 break; 251 case 'C': 252 if (m == O_RDONLY) 253 tif->tif_flags |= TIFF_STRIPCHOP; 254 break; 255 case 'c': 256 if (m == O_RDONLY) 257 tif->tif_flags &= ~TIFF_STRIPCHOP; 258 break; 259 case 'h': 260 tif->tif_flags |= TIFF_HEADERONLY; 261 break; 262 case '8': 263 if (m&O_CREAT) 264 tif->tif_flags |= TIFF_BIGTIFF; 265 break; 266 } 267 /* 268 * Read in TIFF header. 269 */ 270 if ((m & O_TRUNC) || 271 !ReadOK(tif, &tif->tif_header, sizeof (TIFFHeaderClassic))) { 272 if (tif->tif_mode == O_RDONLY) { 273 TIFFErrorExt(tif->tif_clientdata, name, 274 "Cannot read TIFF header"); 275 goto bad; 276 } 277 /* 278 * Setup header and write. 279 */ 280 #ifdef WORDS_BIGENDIAN 281 tif->tif_header.common.tiff_magic = (tif->tif_flags & TIFF_SWAB) 282 ? TIFF_LITTLEENDIAN : TIFF_BIGENDIAN; 283 #else 284 tif->tif_header.common.tiff_magic = (tif->tif_flags & TIFF_SWAB) 285 ? TIFF_BIGENDIAN : TIFF_LITTLEENDIAN; 286 #endif 287 if (!(tif->tif_flags&TIFF_BIGTIFF)) 288 { 289 tif->tif_header.common.tiff_version = TIFF_VERSION_CLASSIC; 290 tif->tif_header.classic.tiff_diroff = 0; 291 if (tif->tif_flags & TIFF_SWAB) 292 TIFFSwabShort(&tif->tif_header.common.tiff_version); 293 tif->tif_header_size = sizeof(TIFFHeaderClassic); 294 } 295 else 296 { 297 tif->tif_header.common.tiff_version = TIFF_VERSION_BIG; 298 tif->tif_header.big.tiff_offsetsize = 8; 299 tif->tif_header.big.tiff_unused = 0; 300 tif->tif_header.big.tiff_diroff = 0; 301 if (tif->tif_flags & TIFF_SWAB) 302 { 303 TIFFSwabShort(&tif->tif_header.common.tiff_version); 304 TIFFSwabShort(&tif->tif_header.big.tiff_offsetsize); 305 } 306 tif->tif_header_size = sizeof (TIFFHeaderBig); 307 } 308 /* 309 * The doc for "fopen" for some STD_C_LIBs says that if you 310 * open a file for modify ("+"), then you must fseek (or 311 * fflush?) between any freads and fwrites. This is not 312 * necessary on most systems, but has been shown to be needed 313 * on Solaris. 314 */ 315 TIFFSeekFile( tif, 0, SEEK_SET ); 316 if (!WriteOK(tif, &tif->tif_header, (tmsize_t)(tif->tif_header_size))) { 317 TIFFErrorExt(tif->tif_clientdata, name, 318 "Error writing TIFF header"); 319 goto bad; 320 } 321 /* 322 * Setup the byte order handling. 323 */ 324 if (tif->tif_header.common.tiff_magic == TIFF_BIGENDIAN) { 325 #ifndef WORDS_BIGENDIAN 326 tif->tif_flags |= TIFF_SWAB; 327 #endif 328 } else { 329 #ifdef WORDS_BIGENDIAN 330 tif->tif_flags |= TIFF_SWAB; 331 #endif 332 } 333 /* 334 * Setup default directory. 335 */ 336 if (!TIFFDefaultDirectory(tif)) 337 goto bad; 338 tif->tif_diroff = 0; 339 tif->tif_dirlist = NULL; 340 tif->tif_dirlistsize = 0; 341 tif->tif_dirnumber = 0; 342 return (tif); 343 } 344 /* 345 * Setup the byte order handling. 346 */ 347 if (tif->tif_header.common.tiff_magic != TIFF_BIGENDIAN && 348 tif->tif_header.common.tiff_magic != TIFF_LITTLEENDIAN 349 #if MDI_SUPPORT 350 && 351 #if HOST_BIGENDIAN 352 tif->tif_header.common.tiff_magic != MDI_BIGENDIAN 353 #else 354 tif->tif_header.common.tiff_magic != MDI_LITTLEENDIAN 355 #endif 356 ) { 357 TIFFErrorExt(tif->tif_clientdata, name, 358 "Not a TIFF or MDI file, bad magic number %d (0x%x)", 359 #else 360 ) { 361 TIFFErrorExt(tif->tif_clientdata, name, 362 "Not a TIFF file, bad magic number %d (0x%x)", 363 #endif 364 tif->tif_header.common.tiff_magic, 365 tif->tif_header.common.tiff_magic); 366 goto bad; 367 } 368 if (tif->tif_header.common.tiff_magic == TIFF_BIGENDIAN) { 369 #ifndef WORDS_BIGENDIAN 370 tif->tif_flags |= TIFF_SWAB; 371 #endif 372 } else { 373 #ifdef WORDS_BIGENDIAN 374 tif->tif_flags |= TIFF_SWAB; 375 #endif 376 } 377 if (tif->tif_flags & TIFF_SWAB) 378 TIFFSwabShort(&tif->tif_header.common.tiff_version); 379 if ((tif->tif_header.common.tiff_version != TIFF_VERSION_CLASSIC)&& 380 (tif->tif_header.common.tiff_version != TIFF_VERSION_BIG)) { 381 TIFFErrorExt(tif->tif_clientdata, name, 382 "Not a TIFF file, bad version number %d (0x%x)", 383 tif->tif_header.common.tiff_version, 384 tif->tif_header.common.tiff_version); 385 goto bad; 386 } 387 if (tif->tif_header.common.tiff_version == TIFF_VERSION_CLASSIC) 388 { 389 if (tif->tif_flags & TIFF_SWAB) 390 TIFFSwabLong(&tif->tif_header.classic.tiff_diroff); 391 tif->tif_header_size = sizeof(TIFFHeaderClassic); 392 } 393 else 394 { 395 if (!ReadOK(tif, ((uint8*)(&tif->tif_header) + sizeof(TIFFHeaderClassic)), (sizeof(TIFFHeaderBig)-sizeof(TIFFHeaderClassic)))) 396 { 397 TIFFErrorExt(tif->tif_clientdata, name, 398 "Cannot read TIFF header"); 399 goto bad; 400 } 401 if (tif->tif_flags & TIFF_SWAB) 402 { 403 TIFFSwabShort(&tif->tif_header.big.tiff_offsetsize); 404 TIFFSwabLong8(&tif->tif_header.big.tiff_diroff); 405 } 406 if (tif->tif_header.big.tiff_offsetsize != 8) 407 { 408 TIFFErrorExt(tif->tif_clientdata, name, 409 "Not a TIFF file, bad BigTIFF offsetsize %d (0x%x)", 410 tif->tif_header.big.tiff_offsetsize, 411 tif->tif_header.big.tiff_offsetsize); 412 goto bad; 413 } 414 if (tif->tif_header.big.tiff_unused != 0) 415 { 416 TIFFErrorExt(tif->tif_clientdata, name, 417 "Not a TIFF file, bad BigTIFF unused %d (0x%x)", 418 tif->tif_header.big.tiff_unused, 419 tif->tif_header.big.tiff_unused); 420 goto bad; 421 } 422 tif->tif_header_size = sizeof(TIFFHeaderBig); 423 tif->tif_flags |= TIFF_BIGTIFF; 424 } 425 tif->tif_flags |= TIFF_MYBUFFER; 426 tif->tif_rawcp = tif->tif_rawdata = 0; 427 tif->tif_rawdatasize = 0; 428 tif->tif_rawdataoff = 0; 429 tif->tif_rawdataloaded = 0; 430 431 switch (mode[0]) { 432 case 'r': 433 if (!(tif->tif_flags&TIFF_BIGTIFF)) 434 tif->tif_nextdiroff = tif->tif_header.classic.tiff_diroff; 435 else 436 tif->tif_nextdiroff = tif->tif_header.big.tiff_diroff; 437 /* 438 * Try to use a memory-mapped file if the client 439 * has not explicitly suppressed usage with the 440 * 'm' flag in the open mode (see above). 441 */ 442 if (tif->tif_flags & TIFF_MAPPED) 443 { 444 toff_t n; 445 if (TIFFMapFileContents(tif,(void**)(&tif->tif_base),&n)) 446 { 447 tif->tif_size=(tmsize_t)n; 448 assert((toff_t)tif->tif_size==n); 449 } 450 else 451 tif->tif_flags &= ~TIFF_MAPPED; 452 } 453 /* 454 * Sometimes we do not want to read the first directory (for example, 455 * it may be broken) and want to proceed to other directories. I this 456 * case we use the TIFF_HEADERONLY flag to open file and return 457 * immediately after reading TIFF header. 458 */ 459 if (tif->tif_flags & TIFF_HEADERONLY) 460 return (tif); 461 462 /* 463 * Setup initial directory. 464 */ 465 if (TIFFReadDirectory(tif)) { 466 tif->tif_rawcc = (tmsize_t)-1; 467 tif->tif_flags |= TIFF_BUFFERSETUP; 468 return (tif); 469 } 470 break; 471 case 'a': 472 /* 473 * New directories are automatically append 474 * to the end of the directory chain when they 475 * are written out (see TIFFWriteDirectory). 476 */ 477 if (!TIFFDefaultDirectory(tif)) 478 goto bad; 479 return (tif); 480 } 481 bad: 482 tif->tif_mode = O_RDONLY; /* XXX avoid flush */ 483 TIFFCleanup(tif); 484 bad2: 485 return ((TIFF*)0); 486 } 487 488 /* 489 * Query functions to access private data. 490 */ 491 492 /* 493 * Return open file's name. 494 */ 495 const char * 496 TIFFFileName(TIFF* tif) 497 { 498 return (tif->tif_name); 499 } 500 501 /* 502 * Set the file name. 503 */ 504 const char * 505 TIFFSetFileName(TIFF* tif, const char *name) 506 { 507 const char* old_name = tif->tif_name; 508 tif->tif_name = (char *)name; 509 return (old_name); 510 } 511 512 /* 513 * Return open file's I/O descriptor. 514 */ 515 int 516 TIFFFileno(TIFF* tif) 517 { 518 return (tif->tif_fd); 519 } 520 521 /* 522 * Set open file's I/O descriptor, and return previous value. 523 */ 524 int 525 TIFFSetFileno(TIFF* tif, int fd) 526 { 527 int old_fd = tif->tif_fd; 528 tif->tif_fd = fd; 529 return old_fd; 530 } 531 532 /* 533 * Return open file's clientdata. 534 */ 535 thandle_t 536 TIFFClientdata(TIFF* tif) 537 { 538 return (tif->tif_clientdata); 539 } 540 541 /* 542 * Set open file's clientdata, and return previous value. 543 */ 544 thandle_t 545 TIFFSetClientdata(TIFF* tif, thandle_t newvalue) 546 { 547 thandle_t m = tif->tif_clientdata; 548 tif->tif_clientdata = newvalue; 549 return m; 550 } 551 552 /* 553 * Return read/write mode. 554 */ 555 int 556 TIFFGetMode(TIFF* tif) 557 { 558 return (tif->tif_mode); 559 } 560 561 /* 562 * Return read/write mode. 563 */ 564 int 565 TIFFSetMode(TIFF* tif, int mode) 566 { 567 int old_mode = tif->tif_mode; 568 tif->tif_mode = mode; 569 return (old_mode); 570 } 571 572 /* 573 * Return nonzero if file is organized in 574 * tiles; zero if organized as strips. 575 */ 576 int 577 TIFFIsTiled(TIFF* tif) 578 { 579 return (isTiled(tif)); 580 } 581 582 /* 583 * Return current row being read/written. 584 */ 585 uint32 586 TIFFCurrentRow(TIFF* tif) 587 { 588 return (tif->tif_row); 589 } 590 591 /* 592 * Return index of the current directory. 593 */ 594 uint16 595 TIFFCurrentDirectory(TIFF* tif) 596 { 597 return (tif->tif_curdir); 598 } 599 600 /* 601 * Return current strip. 602 */ 603 uint32 604 TIFFCurrentStrip(TIFF* tif) 605 { 606 return (tif->tif_curstrip); 607 } 608 609 /* 610 * Return current tile. 611 */ 612 uint32 613 TIFFCurrentTile(TIFF* tif) 614 { 615 return (tif->tif_curtile); 616 } 617 618 /* 619 * Return nonzero if the file has byte-swapped data. 620 */ 621 int 622 TIFFIsByteSwapped(TIFF* tif) 623 { 624 return ((tif->tif_flags & TIFF_SWAB) != 0); 625 } 626 627 /* 628 * Return nonzero if the data is returned up-sampled. 629 */ 630 int 631 TIFFIsUpSampled(TIFF* tif) 632 { 633 return (isUpSampled(tif)); 634 } 635 636 /* 637 * Return nonzero if the data is returned in MSB-to-LSB bit order. 638 */ 639 int 640 TIFFIsMSB2LSB(TIFF* tif) 641 { 642 return (isFillOrder(tif, FILLORDER_MSB2LSB)); 643 } 644 645 /* 646 * Return nonzero if given file was written in big-endian order. 647 */ 648 int 649 TIFFIsBigEndian(TIFF* tif) 650 { 651 return (tif->tif_header.common.tiff_magic == TIFF_BIGENDIAN); 652 } 653 654 /* 655 * Return pointer to file read method. 656 */ 657 TIFFReadWriteProc 658 TIFFGetReadProc(TIFF* tif) 659 { 660 return (tif->tif_readproc); 661 } 662 663 /* 664 * Return pointer to file write method. 665 */ 666 TIFFReadWriteProc 667 TIFFGetWriteProc(TIFF* tif) 668 { 669 return (tif->tif_writeproc); 670 } 671 672 /* 673 * Return pointer to file seek method. 674 */ 675 TIFFSeekProc 676 TIFFGetSeekProc(TIFF* tif) 677 { 678 return (tif->tif_seekproc); 679 } 680 681 /* 682 * Return pointer to file close method. 683 */ 684 TIFFCloseProc 685 TIFFGetCloseProc(TIFF* tif) 686 { 687 return (tif->tif_closeproc); 688 } 689 690 /* 691 * Return pointer to file size requesting method. 692 */ 693 TIFFSizeProc 694 TIFFGetSizeProc(TIFF* tif) 695 { 696 return (tif->tif_sizeproc); 697 } 698 699 /* 700 * Return pointer to memory mapping method. 701 */ 702 TIFFMapFileProc 703 TIFFGetMapFileProc(TIFF* tif) 704 { 705 return (tif->tif_mapproc); 706 } 707 708 /* 709 * Return pointer to memory unmapping method. 710 */ 711 TIFFUnmapFileProc 712 TIFFGetUnmapFileProc(TIFF* tif) 713 { 714 return (tif->tif_unmapproc); 715 } 716 717 /* vim: set ts=8 sts=8 sw=8 noet: */ 718 /* 719 * Local Variables: 720 * mode: c 721 * c-basic-offset: 8 722 * fill-column: 78 723 * End: 724 */ 725