-/* $Id$ */
/*
* Copyright (c) 1988-1997 Sam Leffler
*/
#include "tiffiop.h"
-static const long typemask[13] = {
- (long)0L, /* TIFF_NOTYPE */
- (long)0x000000ffL, /* TIFF_BYTE */
- (long)0xffffffffL, /* TIFF_ASCII */
- (long)0x0000ffffL, /* TIFF_SHORT */
- (long)0xffffffffL, /* TIFF_LONG */
- (long)0xffffffffL, /* TIFF_RATIONAL */
- (long)0x000000ffL, /* TIFF_SBYTE */
- (long)0x000000ffL, /* TIFF_UNDEFINED */
- (long)0x0000ffffL, /* TIFF_SSHORT */
- (long)0xffffffffL, /* TIFF_SLONG */
- (long)0xffffffffL, /* TIFF_SRATIONAL */
- (long)0xffffffffL, /* TIFF_FLOAT */
- (long)0xffffffffL, /* TIFF_DOUBLE */
-};
-static const int bigTypeshift[13] = {
- 0, /* TIFF_NOTYPE */
- 24, /* TIFF_BYTE */
- 0, /* TIFF_ASCII */
- 16, /* TIFF_SHORT */
- 0, /* TIFF_LONG */
- 0, /* TIFF_RATIONAL */
- 24, /* TIFF_SBYTE */
- 24, /* TIFF_UNDEFINED */
- 16, /* TIFF_SSHORT */
- 0, /* TIFF_SLONG */
- 0, /* TIFF_SRATIONAL */
- 0, /* TIFF_FLOAT */
- 0, /* TIFF_DOUBLE */
-};
-static const int litTypeshift[13] = {
- 0, /* TIFF_NOTYPE */
- 0, /* TIFF_BYTE */
- 0, /* TIFF_ASCII */
- 0, /* TIFF_SHORT */
- 0, /* TIFF_LONG */
- 0, /* TIFF_RATIONAL */
- 0, /* TIFF_SBYTE */
- 0, /* TIFF_UNDEFINED */
- 0, /* TIFF_SSHORT */
- 0, /* TIFF_SLONG */
- 0, /* TIFF_SRATIONAL */
- 0, /* TIFF_FLOAT */
- 0, /* TIFF_DOUBLE */
-};
-
/*
* Dummy functions to fill the omitted client procedures.
*/
static int
-_tiffDummyMapProc(thandle_t fd, tdata_t* pbase, toff_t* psize)
+_tiffDummyMapProc(thandle_t fd, void** pbase, toff_t* psize)
{
(void) fd; (void) pbase; (void) psize;
return (0);
}
static void
-_tiffDummyUnmapProc(thandle_t fd, tdata_t base, toff_t size)
+_tiffDummyUnmapProc(thandle_t fd, void* base, toff_t size)
{
(void) fd; (void) base; (void) size;
}
-/*
- * Initialize the shift & mask tables, and the
- * byte swapping state according to the file
- * contents and the machine architecture.
- */
-static void
-TIFFInitOrder(TIFF* tif, int magic)
-{
- tif->tif_typemask = typemask;
- if (magic == TIFF_BIGENDIAN) {
- tif->tif_typeshift = bigTypeshift;
-#ifndef WORDS_BIGENDIAN
- tif->tif_flags |= TIFF_SWAB;
-#endif
- } else {
- tif->tif_typeshift = litTypeshift;
-#ifdef WORDS_BIGENDIAN
- tif->tif_flags |= TIFF_SWAB;
-#endif
- }
-}
-
int
_TIFFgetMode(const char* mode, const char* module)
{
int m;
const char* cp;
+ /* The following are configuration checks. They should be redundant, but should not
+ * compile to any actual code in an optimised release build anyway. If any of them
+ * fail, (makefile-based or other) configuration is not correct */
+ assert(sizeof(uint8)==1);
+ assert(sizeof(int8)==1);
+ assert(sizeof(uint16)==2);
+ assert(sizeof(int16)==2);
+ assert(sizeof(uint32)==4);
+ assert(sizeof(int32)==4);
+ assert(sizeof(uint64)==8);
+ assert(sizeof(int64)==8);
+ assert(sizeof(tmsize_t)==sizeof(void*));
+ {
+ union{
+ uint8 a8[2];
+ uint16 a16;
+ } n;
+ n.a8[0]=1;
+ n.a8[1]=0;
+ #ifdef WORDS_BIGENDIAN
+ assert(n.a16==256);
+ #else
+ assert(n.a16==1);
+ #endif
+ }
+
m = _TIFFgetMode(mode, module);
if (m == -1)
goto bad2;
- tif = (TIFF *)_TIFFmalloc(sizeof (TIFF) + strlen(name) + 1);
+ tif = (TIFF *)_TIFFmalloc((tmsize_t)(sizeof (TIFF) + strlen(name) + 1));
if (tif == NULL) {
TIFFErrorExt(clientdata, module, "%s: Out of memory (TIFF structure)", name);
goto bad2;
tif->tif_name = (char *)tif + sizeof (TIFF);
strcpy(tif->tif_name, name);
tif->tif_mode = m &~ (O_CREAT|O_TRUNC);
- tif->tif_curdir = (tdir_t) -1; /* non-existent directory */
+ tif->tif_curdir = (uint16) -1; /* non-existent directory */
tif->tif_curoff = 0;
- tif->tif_curstrip = (tstrip_t) -1; /* invalid strip */
+ tif->tif_curstrip = (uint32) -1; /* invalid strip */
tif->tif_row = (uint32) -1; /* read/write pre-increment */
tif->tif_clientdata = clientdata;
if (!readproc || !writeproc || !seekproc || !closeproc || !sizeproc) {
TIFFErrorExt(clientdata, module,
- "One of the client procedures is NULL pointer.");
+ "One of the client procedures is NULL pointer.");
goto bad2;
}
tif->tif_readproc = readproc;
tif->tif_seekproc = seekproc;
tif->tif_closeproc = closeproc;
tif->tif_sizeproc = sizeproc;
- if (mapproc)
+ if (mapproc)
tif->tif_mapproc = mapproc;
else
tif->tif_mapproc = _tiffDummyMapProc;
tif->tif_unmapproc = unmapproc;
else
tif->tif_unmapproc = _tiffDummyUnmapProc;
- _TIFFSetDefaultCompressionState(tif); /* setup default state */
+ _TIFFSetDefaultCompressionState(tif); /* setup default state */
/*
* Default is to return data MSB2LSB and enable the
* use of memory-mapped files and strip chopping when
*/
tif->tif_flags = FILLORDER_MSB2LSB;
if (m == O_RDONLY )
- tif->tif_flags |= TIFF_MAPPED;
+ tif->tif_flags |= TIFF_MAPPED;
-#ifdef STRIPCHOP_DEFAULT
+ #ifdef STRIPCHOP_DEFAULT
if (m == O_RDONLY || m == O_RDWR)
tif->tif_flags |= STRIPCHOP_DEFAULT;
-#endif
+ #endif
/*
* Process library-specific flags in the open mode string.
* TIFF but only supports some braindead idea of what the
* vendor thinks TIFF is):
*
- * 'l' use little-endian byte order for creating a file
- * 'b' use big-endian byte order for creating a file
- * 'L' read/write information using LSB2MSB bit order
- * 'B' read/write information using MSB2LSB bit order
- * 'H' read/write information using host bit order
- * 'M' enable use of memory-mapped files when supported
- * 'm' disable use of memory-mapped files
- * 'C' enable strip chopping support when reading
- * 'c' disable strip chopping support
- * 'h' read TIFF header only, do not load the first IFD
+ * 'l' use little-endian byte order for creating a file
+ * 'b' use big-endian byte order for creating a file
+ * 'L' read/write information using LSB2MSB bit order
+ * 'B' read/write information using MSB2LSB bit order
+ * 'H' read/write information using host bit order
+ * 'M' enable use of memory-mapped files when supported
+ * 'm' disable use of memory-mapped files
+ * 'C' enable strip chopping support when reading
+ * 'c' disable strip chopping support
+ * 'h' read TIFF header only, do not load the first IFD
+ * '4' ClassicTIFF for creating a file (default)
+ * '8' BigTIFF for creating a file
*
* The use of the 'l' and 'b' flags is strongly discouraged.
* These flags are provided solely because numerous vendors,
*/
for (cp = mode; *cp; cp++)
switch (*cp) {
- case 'b':
-#ifndef WORDS_BIGENDIAN
- if (m&O_CREAT)
- tif->tif_flags |= TIFF_SWAB;
-#endif
- break;
- case 'l':
-#ifdef WORDS_BIGENDIAN
- if ((m&O_CREAT))
- tif->tif_flags |= TIFF_SWAB;
-#endif
- break;
- case 'B':
- tif->tif_flags = (tif->tif_flags &~ TIFF_FILLORDER) |
- FILLORDER_MSB2LSB;
- break;
- case 'L':
- tif->tif_flags = (tif->tif_flags &~ TIFF_FILLORDER) |
- FILLORDER_LSB2MSB;
- break;
- case 'H':
- tif->tif_flags = (tif->tif_flags &~ TIFF_FILLORDER) |
- HOST_FILLORDER;
- break;
- case 'M':
- if (m == O_RDONLY)
- tif->tif_flags |= TIFF_MAPPED;
- break;
- case 'm':
- if (m == O_RDONLY)
- tif->tif_flags &= ~TIFF_MAPPED;
- break;
- case 'C':
- if (m == O_RDONLY)
- tif->tif_flags |= TIFF_STRIPCHOP;
- break;
- case 'c':
- if (m == O_RDONLY)
- tif->tif_flags &= ~TIFF_STRIPCHOP;
- break;
- case 'h':
- tif->tif_flags |= TIFF_HEADERONLY;
- break;
+ case 'b':
+ #ifndef WORDS_BIGENDIAN
+ if (m&O_CREAT)
+ tif->tif_flags |= TIFF_SWAB;
+ #endif
+ break;
+ case 'l':
+ #ifdef WORDS_BIGENDIAN
+ if ((m&O_CREAT))
+ tif->tif_flags |= TIFF_SWAB;
+ #endif
+ break;
+ case 'B':
+ tif->tif_flags = (tif->tif_flags &~ TIFF_FILLORDER) |
+ FILLORDER_MSB2LSB;
+ break;
+ case 'L':
+ tif->tif_flags = (tif->tif_flags &~ TIFF_FILLORDER) |
+ FILLORDER_LSB2MSB;
+ break;
+ case 'H':
+ tif->tif_flags = (tif->tif_flags &~ TIFF_FILLORDER) |
+ HOST_FILLORDER;
+ break;
+ case 'M':
+ if (m == O_RDONLY)
+ tif->tif_flags |= TIFF_MAPPED;
+ break;
+ case 'm':
+ if (m == O_RDONLY)
+ tif->tif_flags &= ~TIFF_MAPPED;
+ break;
+ case 'C':
+ if (m == O_RDONLY)
+ tif->tif_flags |= TIFF_STRIPCHOP;
+ break;
+ case 'c':
+ if (m == O_RDONLY)
+ tif->tif_flags &= ~TIFF_STRIPCHOP;
+ break;
+ case 'h':
+ tif->tif_flags |= TIFF_HEADERONLY;
+ break;
+ case '8':
+ if (m&O_CREAT)
+ tif->tif_flags |= TIFF_BIGTIFF;
+ break;
}
/*
* Read in TIFF header.
*/
- if (tif->tif_mode & O_TRUNC ||
- !ReadOK(tif, &tif->tif_header, sizeof (TIFFHeader))) {
+ if ((m & O_TRUNC) ||
+ !ReadOK(tif, &tif->tif_header, sizeof (TIFFHeaderClassic))) {
if (tif->tif_mode == O_RDONLY) {
- TIFFErrorExt(tif->tif_clientdata, name, "Cannot read TIFF header");
+ TIFFErrorExt(tif->tif_clientdata, name,
+ "Cannot read TIFF header");
goto bad;
}
/*
* Setup header and write.
*/
-#ifdef WORDS_BIGENDIAN
- tif->tif_header.tiff_magic = tif->tif_flags & TIFF_SWAB
+ #ifdef WORDS_BIGENDIAN
+ tif->tif_header.common.tiff_magic = tif->tif_flags & TIFF_SWAB
? TIFF_LITTLEENDIAN : TIFF_BIGENDIAN;
-#else
- tif->tif_header.tiff_magic = tif->tif_flags & TIFF_SWAB
+ #else
+ tif->tif_header.common.tiff_magic = tif->tif_flags & TIFF_SWAB
? TIFF_BIGENDIAN : TIFF_LITTLEENDIAN;
-#endif
- tif->tif_header.tiff_version = TIFF_VERSION;
- if (tif->tif_flags & TIFF_SWAB)
- TIFFSwabShort(&tif->tif_header.tiff_version);
- tif->tif_header.tiff_diroff = 0; /* filled in later */
-
-
- /*
- * The doc for "fopen" for some STD_C_LIBs says that if you
- * open a file for modify ("+"), then you must fseek (or
- * fflush?) between any freads and fwrites. This is not
- * necessary on most systems, but has been shown to be needed
- * on Solaris.
- */
- TIFFSeekFile( tif, 0, SEEK_SET );
-
- if (!WriteOK(tif, &tif->tif_header, sizeof (TIFFHeader))) {
- TIFFErrorExt(tif->tif_clientdata, name, "Error writing TIFF header");
+ #endif
+ if (!(tif->tif_flags&TIFF_BIGTIFF))
+ {
+ tif->tif_header.common.tiff_version = TIFF_VERSION_CLASSIC;
+ tif->tif_header.classic.tiff_diroff = 0;
+ if (tif->tif_flags & TIFF_SWAB)
+ TIFFSwabShort(&tif->tif_header.common.tiff_version);
+ tif->tif_header_size = sizeof(TIFFHeaderClassic);
+ }
+ else
+ {
+ tif->tif_header.common.tiff_version = TIFF_VERSION_BIG;
+ tif->tif_header.big.tiff_offsetsize = 8;
+ tif->tif_header.big.tiff_unused = 0;
+ tif->tif_header.big.tiff_diroff = 0;
+ if (tif->tif_flags & TIFF_SWAB)
+ {
+ TIFFSwabShort(&tif->tif_header.common.tiff_version);
+ TIFFSwabShort(&tif->tif_header.big.tiff_offsetsize);
+ }
+ tif->tif_header_size = sizeof (TIFFHeaderBig);
+ }
+ /*
+ * The doc for "fopen" for some STD_C_LIBs says that if you
+ * open a file for modify ("+"), then you must fseek (or
+ * fflush?) between any freads and fwrites. This is not
+ * necessary on most systems, but has been shown to be needed
+ * on Solaris.
+ */
+ TIFFSeekFile( tif, 0, SEEK_SET );
+ if (!WriteOK(tif, &tif->tif_header, (tmsize_t)(tif->tif_header_size))) {
+ TIFFErrorExt(tif->tif_clientdata, name,
+ "Error writing TIFF header");
goto bad;
}
/*
* Setup the byte order handling.
*/
- TIFFInitOrder(tif, tif->tif_header.tiff_magic);
+ if (tif->tif_header.common.tiff_magic == TIFF_BIGENDIAN) {
+ #ifndef WORDS_BIGENDIAN
+ tif->tif_flags |= TIFF_SWAB;
+ #endif
+ } else {
+ #ifdef WORDS_BIGENDIAN
+ tif->tif_flags |= TIFF_SWAB;
+ #endif
+ }
/*
* Setup default directory.
*/
goto bad;
tif->tif_diroff = 0;
tif->tif_dirlist = NULL;
+ tif->tif_dirlistsize = 0;
tif->tif_dirnumber = 0;
return (tif);
}
/*
* Setup the byte order handling.
*/
- if (tif->tif_header.tiff_magic != TIFF_BIGENDIAN &&
- tif->tif_header.tiff_magic != TIFF_LITTLEENDIAN
-#if MDI_SUPPORT
+ if (tif->tif_header.common.tiff_magic != TIFF_BIGENDIAN &&
+ tif->tif_header.common.tiff_magic != TIFF_LITTLEENDIAN
+ #if MDI_SUPPORT
&&
-#if HOST_BIGENDIAN
- tif->tif_header.tiff_magic != MDI_BIGENDIAN
-#else
- tif->tif_header.tiff_magic != MDI_LITTLEENDIAN
-#endif
+ #if HOST_BIGENDIAN
+ tif->tif_header.common.tiff_magic != MDI_BIGENDIAN
+ #else
+ tif->tif_header.common.tiff_magic != MDI_LITTLEENDIAN
+ #endif
) {
- TIFFErrorExt(tif->tif_clientdata, name, "Not a TIFF or MDI file, bad magic number %d (0x%x)",
-#else
+ TIFFErrorExt(tif->tif_clientdata, name,
+ "Not a TIFF or MDI file, bad magic number %d (0x%x)",
+ #else
) {
- TIFFErrorExt(tif->tif_clientdata, name, "Not a TIFF file, bad magic number %d (0x%x)",
-#endif
- tif->tif_header.tiff_magic,
- tif->tif_header.tiff_magic);
- goto bad;
- }
- TIFFInitOrder(tif, tif->tif_header.tiff_magic);
- /*
- * Swap header if required.
- */
- if (tif->tif_flags & TIFF_SWAB) {
- TIFFSwabShort(&tif->tif_header.tiff_version);
- TIFFSwabLong(&tif->tif_header.tiff_diroff);
- }
- /*
- * Now check version (if needed, it's been byte-swapped).
- * Note that this isn't actually a version number, it's a
- * magic number that doesn't change (stupid).
- */
- if (tif->tif_header.tiff_version == TIFF_BIGTIFF_VERSION) {
TIFFErrorExt(tif->tif_clientdata, name,
- "This is a BigTIFF file. This format not supported\n"
- "by this version of libtiff." );
+ "Not a TIFF file, bad magic number %d (0x%x)",
+ #endif
+ tif->tif_header.common.tiff_magic,
+ tif->tif_header.common.tiff_magic);
goto bad;
}
- if (tif->tif_header.tiff_version != TIFF_VERSION) {
+ if (tif->tif_header.common.tiff_magic == TIFF_BIGENDIAN) {
+ #ifndef WORDS_BIGENDIAN
+ tif->tif_flags |= TIFF_SWAB;
+ #endif
+ } else {
+ #ifdef WORDS_BIGENDIAN
+ tif->tif_flags |= TIFF_SWAB;
+ #endif
+ }
+ if (tif->tif_flags & TIFF_SWAB)
+ TIFFSwabShort(&tif->tif_header.common.tiff_version);
+ if ((tif->tif_header.common.tiff_version != TIFF_VERSION_CLASSIC)&&
+ (tif->tif_header.common.tiff_version != TIFF_VERSION_BIG)) {
TIFFErrorExt(tif->tif_clientdata, name,
"Not a TIFF file, bad version number %d (0x%x)",
- tif->tif_header.tiff_version,
- tif->tif_header.tiff_version);
+ tif->tif_header.common.tiff_version,
+ tif->tif_header.common.tiff_version);
goto bad;
}
+ if (tif->tif_header.common.tiff_version == TIFF_VERSION_CLASSIC)
+ {
+ if (tif->tif_flags & TIFF_SWAB)
+ TIFFSwabLong(&tif->tif_header.classic.tiff_diroff);
+ tif->tif_header_size = sizeof(TIFFHeaderClassic);
+ }
+ else
+ {
+ if (!ReadOK(tif, ((uint8*)(&tif->tif_header) + sizeof(TIFFHeaderClassic)), (sizeof(TIFFHeaderBig)-sizeof(TIFFHeaderClassic))))
+ {
+ TIFFErrorExt(tif->tif_clientdata, name,
+ "Cannot read TIFF header");
+ goto bad;
+ }
+ if (tif->tif_flags & TIFF_SWAB)
+ {
+ TIFFSwabShort(&tif->tif_header.big.tiff_offsetsize);
+ TIFFSwabLong8(&tif->tif_header.big.tiff_diroff);
+ }
+ if (tif->tif_header.big.tiff_offsetsize != 8)
+ {
+ TIFFErrorExt(tif->tif_clientdata, name,
+ "Not a TIFF file, bad BigTIFF offsetsize %d (0x%x)",
+ tif->tif_header.big.tiff_offsetsize,
+ tif->tif_header.big.tiff_offsetsize);
+ goto bad;
+ }
+ if (tif->tif_header.big.tiff_unused != 0)
+ {
+ TIFFErrorExt(tif->tif_clientdata, name,
+ "Not a TIFF file, bad BigTIFF unused %d (0x%x)",
+ tif->tif_header.big.tiff_unused,
+ tif->tif_header.big.tiff_unused);
+ goto bad;
+ }
+ tif->tif_header_size = sizeof(TIFFHeaderBig);
+ tif->tif_flags |= TIFF_BIGTIFF;
+ }
tif->tif_flags |= TIFF_MYBUFFER;
tif->tif_rawcp = tif->tif_rawdata = 0;
tif->tif_rawdatasize = 0;
+ tif->tif_rawdataoff = 0;
+ tif->tif_rawdataloaded = 0;
- /*
- * Sometimes we do not want to read the first directory (for example,
- * it may be broken) and want to proceed to other directories. I this
- * case we use the TIFF_HEADERONLY flag to open file and return
- * immediately after reading TIFF header.
- */
- if (tif->tif_flags & TIFF_HEADERONLY)
- return (tif);
-
- /*
- * Setup initial directory.
- */
switch (mode[0]) {
- case 'r':
- tif->tif_nextdiroff = tif->tif_header.tiff_diroff;
- /*
- * Try to use a memory-mapped file if the client
- * has not explicitly suppressed usage with the
- * 'm' flag in the open mode (see above).
- */
- if ((tif->tif_flags & TIFF_MAPPED) &&
- !TIFFMapFileContents(tif, (tdata_t*) &tif->tif_base, &tif->tif_size))
- tif->tif_flags &= ~TIFF_MAPPED;
- if (TIFFReadDirectory(tif)) {
- tif->tif_rawcc = -1;
- tif->tif_flags |= TIFF_BUFFERSETUP;
+ case 'r':
+ if (!(tif->tif_flags&TIFF_BIGTIFF))
+ tif->tif_nextdiroff = tif->tif_header.classic.tiff_diroff;
+ else
+ tif->tif_nextdiroff = tif->tif_header.big.tiff_diroff;
+ /*
+ * Try to use a memory-mapped file if the client
+ * has not explicitly suppressed usage with the
+ * 'm' flag in the open mode (see above).
+ */
+ if (tif->tif_flags & TIFF_MAPPED)
+ {
+ toff_t n;
+ if (TIFFMapFileContents(tif,(void**)(&tif->tif_base),&n))
+ {
+ tif->tif_size=(tmsize_t)n;
+ assert((toff_t)tif->tif_size==n);
+ }
+ else
+ tif->tif_flags &= ~TIFF_MAPPED;
+ }
+ /*
+ * Sometimes we do not want to read the first directory (for example,
+ * it may be broken) and want to proceed to other directories. I this
+ * case we use the TIFF_HEADERONLY flag to open file and return
+ * immediately after reading TIFF header.
+ */
+ if (tif->tif_flags & TIFF_HEADERONLY)
+ return (tif);
+
+ /*
+ * Setup initial directory.
+ */
+ if (TIFFReadDirectory(tif)) {
+ tif->tif_rawcc = (tmsize_t)-1;
+ tif->tif_flags |= TIFF_BUFFERSETUP;
+ return (tif);
+ }
+ break;
+ case 'a':
+ /*
+ * New directories are automatically append
+ * to the end of the directory chain when they
+ * are written out (see TIFFWriteDirectory).
+ */
+ if (!TIFFDefaultDirectory(tif))
+ goto bad;
return (tif);
- }
- break;
- case 'a':
- /*
- * New directories are automatically append
- * to the end of the directory chain when they
- * are written out (see TIFFWriteDirectory).
- */
- if (!TIFFDefaultDirectory(tif))
- goto bad;
- return (tif);
}
bad:
tif->tif_mode = O_RDONLY; /* XXX avoid flush */
/*
* Return index of the current directory.
*/
-tdir_t
+uint16
TIFFCurrentDirectory(TIFF* tif)
{
return (tif->tif_curdir);
/*
* Return current strip.
*/
-tstrip_t
+uint32
TIFFCurrentStrip(TIFF* tif)
{
return (tif->tif_curstrip);
/*
* Return current tile.
*/
-ttile_t
+uint32
TIFFCurrentTile(TIFF* tif)
{
return (tif->tif_curtile);
int
TIFFIsBigEndian(TIFF* tif)
{
- return (tif->tif_header.tiff_magic == TIFF_BIGENDIAN);
+ return (tif->tif_header.common.tiff_magic == TIFF_BIGENDIAN);
}
/*
}
/* vim: set ts=8 sts=8 sw=8 noet: */
+/*
+ * Local Variables:
+ * mode: c
+ * c-basic-offset: 8
+ * fill-column: 78
+ * End:
+ */