Remove all lines containing cvs/svn "$Id$" keyword.
[wxWidgets.git] / src / tiff / libtiff / tif_open.c
1
2 /*
3 * Copyright (c) 1988-1997 Sam Leffler
4 * Copyright (c) 1991-1997 Silicon Graphics, Inc.
5 *
6 * Permission to use, copy, modify, distribute, and sell this software and
7 * its documentation for any purpose is hereby granted without fee, provided
8 * that (i) the above copyright notices and this permission notice appear in
9 * all copies of the software and related documentation, and (ii) the names of
10 * Sam Leffler and Silicon Graphics may not be used in any advertising or
11 * publicity relating to the software without the specific, prior written
12 * permission of Sam Leffler and Silicon Graphics.
13 *
14 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
15 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
16 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
17 *
18 * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
19 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
20 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
21 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
22 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
23 * OF THIS SOFTWARE.
24 */
25
26 /*
27 * TIFF Library.
28 */
29 #include "tiffiop.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 braindead 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 compatibiltiy 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 */