Home | History | Annotate | Download | only in giflib
      1 /******************************************************************************
      2 
      3 dgif_lib.c - GIF decoding
      4 
      5 The functions here and in egif_lib.c are partitioned carefully so that
      6 if you only require one of read and write capability, only one of these
      7 two modules will be linked.  Preserve this property!
      8 
      9 *****************************************************************************/
     10 
     11 #include <stdlib.h>
     12 #include <limits.h>
     13 #include <stdint.h>
     14 #include <fcntl.h>
     15 #include <unistd.h>
     16 #include <stdio.h>
     17 #include <string.h>
     18 
     19 #ifdef _WIN32
     20 #include <io.h>
     21 #endif /* _WIN32 */
     22 
     23 #include "gif_lib.h"
     24 #include "gif_lib_private.h"
     25 
     26 /* compose unsigned little endian value */
     27 #define UNSIGNED_LITTLE_ENDIAN(lo, hi)	((lo) | ((hi) << 8))
     28 
     29 /* avoid extra function call in case we use fread (TVT) */
     30 #define READ(_gif,_buf,_len)                                     \
     31   (((GifFilePrivateType*)_gif->Private)->Read ?                   \
     32     ((GifFilePrivateType*)_gif->Private)->Read(_gif,_buf,_len) : \
     33     fread(_buf,1,_len,((GifFilePrivateType*)_gif->Private)->File))
     34 
     35 static int DGifGetWord(GifFileType *GifFile, GifWord *Word);
     36 static int DGifSetupDecompress(GifFileType *GifFile);
     37 static int DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line,
     38                               int LineLen);
     39 static int DGifGetPrefixChar(GifPrefixType *Prefix, int Code, int ClearCode);
     40 static int DGifDecompressInput(GifFileType *GifFile, int *Code);
     41 static int DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf,
     42                              GifByteType *NextByte);
     43 
     44 /******************************************************************************
     45  Open a new GIF file for read, given by its name.
     46  Returns dynamically allocated GifFileType pointer which serves as the GIF
     47  info record.
     48 ******************************************************************************/
     49 GifFileType *
     50 DGifOpenFileName(const char *FileName, int *Error)
     51 {
     52     int FileHandle;
     53     GifFileType *GifFile;
     54 
     55     if ((FileHandle = open(FileName, O_RDONLY)) == -1) {
     56 	if (Error != NULL)
     57 	    *Error = D_GIF_ERR_OPEN_FAILED;
     58         return NULL;
     59     }
     60 
     61     GifFile = DGifOpenFileHandle(FileHandle, Error);
     62     // cppcheck-suppress resourceLeak
     63     return GifFile;
     64 }
     65 
     66 /******************************************************************************
     67  Update a new GIF file, given its file handle.
     68  Returns dynamically allocated GifFileType pointer which serves as the GIF
     69  info record.
     70 ******************************************************************************/
     71 GifFileType *
     72 DGifOpenFileHandle(int FileHandle, int *Error)
     73 {
     74     char Buf[GIF_STAMP_LEN + 1];
     75     GifFileType *GifFile;
     76     GifFilePrivateType *Private;
     77     FILE *f;
     78 
     79     GifFile = (GifFileType *)malloc(sizeof(GifFileType));
     80     if (GifFile == NULL) {
     81         if (Error != NULL)
     82 	    *Error = D_GIF_ERR_NOT_ENOUGH_MEM;
     83         (void)close(FileHandle);
     84         return NULL;
     85     }
     86 
     87     /*@i1@*/memset(GifFile, '\0', sizeof(GifFileType));
     88 
     89     /* Belt and suspenders, in case the null pointer isn't zero */
     90     GifFile->SavedImages = NULL;
     91     GifFile->SColorMap = NULL;
     92 
     93     Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType));
     94     if (Private == NULL) {
     95         if (Error != NULL)
     96 	    *Error = D_GIF_ERR_NOT_ENOUGH_MEM;
     97         (void)close(FileHandle);
     98         free((char *)GifFile);
     99         return NULL;
    100     }
    101 #ifdef _WIN32
    102     _setmode(FileHandle, O_BINARY);    /* Make sure it is in binary mode. */
    103 #endif /* _WIN32 */
    104 
    105     f = fdopen(FileHandle, "rb");    /* Make it into a stream: */
    106 
    107     /*@-mustfreeonly@*/
    108     GifFile->Private = (void *)Private;
    109     Private->FileHandle = FileHandle;
    110     Private->File = f;
    111     Private->FileState = FILE_STATE_READ;
    112     Private->Read = NULL;        /* don't use alternate input method (TVT) */
    113     GifFile->UserData = NULL;    /* TVT */
    114     /*@=mustfreeonly@*/
    115 
    116     /* Let's see if this is a GIF file: */
    117     if (READ(GifFile, (unsigned char *)Buf, GIF_STAMP_LEN) != GIF_STAMP_LEN) {
    118         if (Error != NULL)
    119 	    *Error = D_GIF_ERR_READ_FAILED;
    120         (void)fclose(f);
    121         free((char *)Private);
    122         free((char *)GifFile);
    123         return NULL;
    124     }
    125 
    126     /* Check for GIF prefix at start of file */
    127     Buf[GIF_STAMP_LEN] = 0;
    128     if (strncmp(GIF_STAMP, Buf, GIF_VERSION_POS) != 0) {
    129         if (Error != NULL)
    130 	    *Error = D_GIF_ERR_NOT_GIF_FILE;
    131         (void)fclose(f);
    132         free((char *)Private);
    133         free((char *)GifFile);
    134         return NULL;
    135     }
    136 
    137     if (DGifGetScreenDesc(GifFile) == GIF_ERROR) {
    138         (void)fclose(f);
    139         free((char *)Private);
    140         free((char *)GifFile);
    141         return NULL;
    142     }
    143 
    144     GifFile->Error = 0;
    145 
    146     /* What version of GIF? */
    147     Private->gif89 = (Buf[GIF_VERSION_POS] == '9');
    148 
    149     return GifFile;
    150 }
    151 
    152 /******************************************************************************
    153  GifFileType constructor with user supplied input function (TVT)
    154 ******************************************************************************/
    155 GifFileType *
    156 DGifOpen(void *userData, InputFunc readFunc, int *Error)
    157 {
    158     char Buf[GIF_STAMP_LEN + 1];
    159     GifFileType *GifFile;
    160     GifFilePrivateType *Private;
    161 
    162     GifFile = (GifFileType *)malloc(sizeof(GifFileType));
    163     if (GifFile == NULL) {
    164         if (Error != NULL)
    165 	    *Error = D_GIF_ERR_NOT_ENOUGH_MEM;
    166         return NULL;
    167     }
    168 
    169     memset(GifFile, '\0', sizeof(GifFileType));
    170 
    171     /* Belt and suspenders, in case the null pointer isn't zero */
    172     GifFile->SavedImages = NULL;
    173     GifFile->SColorMap = NULL;
    174 
    175     Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType));
    176     if (!Private) {
    177         if (Error != NULL)
    178 	    *Error = D_GIF_ERR_NOT_ENOUGH_MEM;
    179         free((char *)GifFile);
    180         return NULL;
    181     }
    182 
    183     GifFile->Private = (void *)Private;
    184     Private->FileHandle = 0;
    185     Private->File = NULL;
    186     Private->FileState = FILE_STATE_READ;
    187 
    188     Private->Read = readFunc;    /* TVT */
    189     GifFile->UserData = userData;    /* TVT */
    190 
    191     /* Lets see if this is a GIF file: */
    192     if (READ(GifFile, (unsigned char *)Buf, GIF_STAMP_LEN) != GIF_STAMP_LEN) {
    193         if (Error != NULL)
    194 	    *Error = D_GIF_ERR_READ_FAILED;
    195         free((char *)Private);
    196         free((char *)GifFile);
    197         return NULL;
    198     }
    199 
    200     /* Check for GIF prefix at start of file */
    201     Buf[GIF_STAMP_LEN] = '\0';
    202     if (strncmp(GIF_STAMP, Buf, GIF_VERSION_POS) != 0) {
    203         if (Error != NULL)
    204 	    *Error = D_GIF_ERR_NOT_GIF_FILE;
    205         free((char *)Private);
    206         free((char *)GifFile);
    207         return NULL;
    208     }
    209 
    210     if (DGifGetScreenDesc(GifFile) == GIF_ERROR) {
    211         free((char *)Private);
    212         free((char *)GifFile);
    213         return NULL;
    214     }
    215 
    216     GifFile->Error = 0;
    217 
    218     /* What version of GIF? */
    219     Private->gif89 = (Buf[GIF_VERSION_POS] == '9');
    220 
    221     return GifFile;
    222 }
    223 
    224 /******************************************************************************
    225  This routine should be called before any other DGif calls. Note that
    226  this routine is called automatically from DGif file open routines.
    227 ******************************************************************************/
    228 int
    229 DGifGetScreenDesc(GifFileType *GifFile)
    230 {
    231     int BitsPerPixel;
    232     bool SortFlag;
    233     GifByteType Buf[3];
    234     GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
    235 
    236     if (!IS_READABLE(Private)) {
    237         /* This file was NOT open for reading: */
    238         GifFile->Error = D_GIF_ERR_NOT_READABLE;
    239         return GIF_ERROR;
    240     }
    241 
    242     /* Put the screen descriptor into the file: */
    243     if (DGifGetWord(GifFile, &GifFile->SWidth) == GIF_ERROR ||
    244         DGifGetWord(GifFile, &GifFile->SHeight) == GIF_ERROR)
    245         return GIF_ERROR;
    246 
    247     if (READ(GifFile, Buf, 3) != 3) {
    248         GifFile->Error = D_GIF_ERR_READ_FAILED;
    249 	GifFreeMapObject(GifFile->SColorMap);
    250 	GifFile->SColorMap = NULL;
    251         return GIF_ERROR;
    252     }
    253     GifFile->SColorResolution = (((Buf[0] & 0x70) + 1) >> 4) + 1;
    254     SortFlag = (Buf[0] & 0x08) != 0;
    255     BitsPerPixel = (Buf[0] & 0x07) + 1;
    256     GifFile->SBackGroundColor = Buf[1];
    257     GifFile->AspectByte = Buf[2];
    258     if (Buf[0] & 0x80) {    /* Do we have global color map? */
    259 	int i;
    260 
    261         GifFile->SColorMap = GifMakeMapObject(1 << BitsPerPixel, NULL);
    262         if (GifFile->SColorMap == NULL) {
    263             GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM;
    264             return GIF_ERROR;
    265         }
    266 
    267         /* Get the global color map: */
    268 	GifFile->SColorMap->SortFlag = SortFlag;
    269         for (i = 0; i < GifFile->SColorMap->ColorCount; i++) {
    270             if (READ(GifFile, Buf, 3) != 3) {
    271                 GifFreeMapObject(GifFile->SColorMap);
    272                 GifFile->SColorMap = NULL;
    273                 GifFile->Error = D_GIF_ERR_READ_FAILED;
    274                 return GIF_ERROR;
    275             }
    276             GifFile->SColorMap->Colors[i].Red = Buf[0];
    277             GifFile->SColorMap->Colors[i].Green = Buf[1];
    278             GifFile->SColorMap->Colors[i].Blue = Buf[2];
    279         }
    280     } else {
    281         GifFile->SColorMap = NULL;
    282     }
    283 
    284     return GIF_OK;
    285 }
    286 
    287 /******************************************************************************
    288  This routine should be called before any attempt to read an image.
    289 ******************************************************************************/
    290 int
    291 DGifGetRecordType(GifFileType *GifFile, GifRecordType* Type)
    292 {
    293     GifByteType Buf;
    294     GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
    295 
    296     if (!IS_READABLE(Private)) {
    297         /* This file was NOT open for reading: */
    298         GifFile->Error = D_GIF_ERR_NOT_READABLE;
    299         return GIF_ERROR;
    300     }
    301 
    302     if (READ(GifFile, &Buf, 1) != 1) {
    303         GifFile->Error = D_GIF_ERR_READ_FAILED;
    304         return GIF_ERROR;
    305     }
    306 
    307     switch (Buf) {
    308       case DESCRIPTOR_INTRODUCER:
    309           *Type = IMAGE_DESC_RECORD_TYPE;
    310           break;
    311       case EXTENSION_INTRODUCER:
    312           *Type = EXTENSION_RECORD_TYPE;
    313           break;
    314       case TERMINATOR_INTRODUCER:
    315           *Type = TERMINATE_RECORD_TYPE;
    316           break;
    317       default:
    318           *Type = UNDEFINED_RECORD_TYPE;
    319           GifFile->Error = D_GIF_ERR_WRONG_RECORD;
    320           return GIF_ERROR;
    321     }
    322 
    323     return GIF_OK;
    324 }
    325 
    326 /******************************************************************************
    327  This routine should be called before any attempt to read an image.
    328  Note it is assumed the Image desc. header has been read.
    329 ******************************************************************************/
    330 int
    331 DGifGetImageDesc(GifFileType *GifFile)
    332 {
    333     unsigned int BitsPerPixel;
    334     GifByteType Buf[3];
    335     GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
    336     SavedImage *sp;
    337 
    338     if (!IS_READABLE(Private)) {
    339         /* This file was NOT open for reading: */
    340         GifFile->Error = D_GIF_ERR_NOT_READABLE;
    341         return GIF_ERROR;
    342     }
    343 
    344     if (DGifGetWord(GifFile, &GifFile->Image.Left) == GIF_ERROR ||
    345         DGifGetWord(GifFile, &GifFile->Image.Top) == GIF_ERROR ||
    346         DGifGetWord(GifFile, &GifFile->Image.Width) == GIF_ERROR ||
    347         DGifGetWord(GifFile, &GifFile->Image.Height) == GIF_ERROR)
    348         return GIF_ERROR;
    349     if (READ(GifFile, Buf, 1) != 1) {
    350         GifFile->Error = D_GIF_ERR_READ_FAILED;
    351 	GifFreeMapObject(GifFile->Image.ColorMap);
    352 	GifFile->Image.ColorMap = NULL;
    353         return GIF_ERROR;
    354     }
    355     BitsPerPixel = (Buf[0] & 0x07) + 1;
    356     GifFile->Image.Interlace = (Buf[0] & 0x40) ? true : false;
    357 
    358     /* Setup the colormap */
    359     if (GifFile->Image.ColorMap) {
    360         GifFreeMapObject(GifFile->Image.ColorMap);
    361         GifFile->Image.ColorMap = NULL;
    362     }
    363     /* Does this image have local color map? */
    364     if (Buf[0] & 0x80) {
    365 	unsigned int i;
    366 
    367         GifFile->Image.ColorMap = GifMakeMapObject(1 << BitsPerPixel, NULL);
    368         if (GifFile->Image.ColorMap == NULL) {
    369             GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM;
    370             return GIF_ERROR;
    371         }
    372 
    373         /* Get the image local color map: */
    374         for (i = 0; i < GifFile->Image.ColorMap->ColorCount; i++) {
    375             if (READ(GifFile, Buf, 3) != 3) {
    376                 GifFreeMapObject(GifFile->Image.ColorMap);
    377                 GifFile->Error = D_GIF_ERR_READ_FAILED;
    378                 GifFile->Image.ColorMap = NULL;
    379                 return GIF_ERROR;
    380             }
    381             GifFile->Image.ColorMap->Colors[i].Red = Buf[0];
    382             GifFile->Image.ColorMap->Colors[i].Green = Buf[1];
    383             GifFile->Image.ColorMap->Colors[i].Blue = Buf[2];
    384         }
    385     }
    386 
    387     if (GifFile->SavedImages) {
    388         if ((GifFile->SavedImages = (SavedImage *)realloc(GifFile->SavedImages,
    389                                       sizeof(SavedImage) *
    390                                       (GifFile->ImageCount + 1))) == NULL) {
    391             GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM;
    392             return GIF_ERROR;
    393         }
    394     } else {
    395         if ((GifFile->SavedImages =
    396              (SavedImage *) malloc(sizeof(SavedImage))) == NULL) {
    397             GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM;
    398             return GIF_ERROR;
    399         }
    400     }
    401 
    402     sp = &GifFile->SavedImages[GifFile->ImageCount];
    403     memcpy(&sp->ImageDesc, &GifFile->Image, sizeof(GifImageDesc));
    404     if (GifFile->Image.ColorMap != NULL) {
    405         sp->ImageDesc.ColorMap = GifMakeMapObject(
    406                                  GifFile->Image.ColorMap->ColorCount,
    407                                  GifFile->Image.ColorMap->Colors);
    408         if (sp->ImageDesc.ColorMap == NULL) {
    409             GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM;
    410             return GIF_ERROR;
    411         }
    412     }
    413     sp->RasterBits = (unsigned char *)NULL;
    414     sp->ExtensionBlockCount = 0;
    415     sp->ExtensionBlocks = (ExtensionBlock *) NULL;
    416 
    417     GifFile->ImageCount++;
    418 
    419     Private->PixelCount = (long)GifFile->Image.Width *
    420        (long)GifFile->Image.Height;
    421 
    422     /* Reset decompress algorithm parameters. */
    423     (void)DGifSetupDecompress(GifFile);
    424 
    425     return GIF_OK;
    426 }
    427 
    428 /******************************************************************************
    429  Get one full scanned line (Line) of length LineLen from GIF file.
    430 ******************************************************************************/
    431 int
    432 DGifGetLine(GifFileType *GifFile, GifPixelType *Line, int LineLen)
    433 {
    434     GifByteType *Dummy;
    435     GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
    436 
    437     if (!IS_READABLE(Private)) {
    438         /* This file was NOT open for reading: */
    439         GifFile->Error = D_GIF_ERR_NOT_READABLE;
    440         return GIF_ERROR;
    441     }
    442 
    443     if (!LineLen)
    444         LineLen = GifFile->Image.Width;
    445 
    446     if ((Private->PixelCount -= LineLen) > 0xffff0000UL) {
    447         GifFile->Error = D_GIF_ERR_DATA_TOO_BIG;
    448         return GIF_ERROR;
    449     }
    450 
    451     if (DGifDecompressLine(GifFile, Line, LineLen) == GIF_OK) {
    452         if (Private->PixelCount == 0) {
    453             /* We probably won't be called any more, so let's clean up
    454              * everything before we return: need to flush out all the
    455              * rest of image until an empty block (size 0)
    456              * detected. We use GetCodeNext.
    457 	     */
    458             do
    459                 if (DGifGetCodeNext(GifFile, &Dummy) == GIF_ERROR)
    460                     return GIF_ERROR;
    461             while (Dummy != NULL) ;
    462         }
    463         return GIF_OK;
    464     } else
    465         return GIF_ERROR;
    466 }
    467 
    468 /******************************************************************************
    469  Put one pixel (Pixel) into GIF file.
    470 ******************************************************************************/
    471 int
    472 DGifGetPixel(GifFileType *GifFile, GifPixelType Pixel)
    473 {
    474     GifByteType *Dummy;
    475     GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
    476 
    477     if (!IS_READABLE(Private)) {
    478         /* This file was NOT open for reading: */
    479         GifFile->Error = D_GIF_ERR_NOT_READABLE;
    480         return GIF_ERROR;
    481     }
    482     if (--Private->PixelCount > 0xffff0000UL)
    483     {
    484         GifFile->Error = D_GIF_ERR_DATA_TOO_BIG;
    485         return GIF_ERROR;
    486     }
    487 
    488     if (DGifDecompressLine(GifFile, &Pixel, 1) == GIF_OK) {
    489         if (Private->PixelCount == 0) {
    490             /* We probably won't be called any more, so let's clean up
    491              * everything before we return: need to flush out all the
    492              * rest of image until an empty block (size 0)
    493              * detected. We use GetCodeNext.
    494 	     */
    495             do
    496                 if (DGifGetCodeNext(GifFile, &Dummy) == GIF_ERROR)
    497                     return GIF_ERROR;
    498             while (Dummy != NULL) ;
    499         }
    500         return GIF_OK;
    501     } else
    502         return GIF_ERROR;
    503 }
    504 
    505 /******************************************************************************
    506  Get an extension block (see GIF manual) from GIF file. This routine only
    507  returns the first data block, and DGifGetExtensionNext should be called
    508  after this one until NULL extension is returned.
    509  The Extension should NOT be freed by the user (not dynamically allocated).
    510  Note it is assumed the Extension description header has been read.
    511 ******************************************************************************/
    512 int
    513 DGifGetExtension(GifFileType *GifFile, int *ExtCode, GifByteType **Extension)
    514 {
    515     GifByteType Buf;
    516     GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
    517 
    518     if (!IS_READABLE(Private)) {
    519         /* This file was NOT open for reading: */
    520         GifFile->Error = D_GIF_ERR_NOT_READABLE;
    521         return GIF_ERROR;
    522     }
    523 
    524     if (READ(GifFile, &Buf, 1) != 1) {
    525         GifFile->Error = D_GIF_ERR_READ_FAILED;
    526         return GIF_ERROR;
    527     }
    528     *ExtCode = Buf;
    529 
    530     return DGifGetExtensionNext(GifFile, Extension);
    531 }
    532 
    533 /******************************************************************************
    534  Get a following extension block (see GIF manual) from GIF file. This
    535  routine should be called until NULL Extension is returned.
    536  The Extension should NOT be freed by the user (not dynamically allocated).
    537 ******************************************************************************/
    538 int
    539 DGifGetExtensionNext(GifFileType *GifFile, GifByteType ** Extension)
    540 {
    541     GifByteType Buf;
    542     GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
    543 
    544     if (READ(GifFile, &Buf, 1) != 1) {
    545         GifFile->Error = D_GIF_ERR_READ_FAILED;
    546         return GIF_ERROR;
    547     }
    548     if (Buf > 0) {
    549         *Extension = Private->Buf;    /* Use private unused buffer. */
    550         (*Extension)[0] = Buf;  /* Pascal strings notation (pos. 0 is len.). */
    551 	/* coverity[tainted_data] */
    552         if (READ(GifFile, &((*Extension)[1]), Buf) != Buf) {
    553             GifFile->Error = D_GIF_ERR_READ_FAILED;
    554             return GIF_ERROR;
    555         }
    556     } else
    557         *Extension = NULL;
    558 
    559     return GIF_OK;
    560 }
    561 
    562 /******************************************************************************
    563  Extract a Graphics Control Block from raw extension data
    564 ******************************************************************************/
    565 
    566 int DGifExtensionToGCB(const size_t GifExtensionLength,
    567 		       const GifByteType *GifExtension,
    568 		       GraphicsControlBlock *GCB)
    569 {
    570     if (GifExtensionLength != 4) {
    571 	return GIF_ERROR;
    572     }
    573 
    574     GCB->DisposalMode = (GifExtension[0] >> 2) & 0x07;
    575     GCB->UserInputFlag = (GifExtension[0] & 0x02) != 0;
    576     GCB->DelayTime = UNSIGNED_LITTLE_ENDIAN(GifExtension[1], GifExtension[2]);
    577     if (GifExtension[0] & 0x01)
    578 	GCB->TransparentColor = (int)GifExtension[3];
    579     else
    580 	GCB->TransparentColor = NO_TRANSPARENT_COLOR;
    581 
    582     return GIF_OK;
    583 }
    584 
    585 /******************************************************************************
    586  Extract the Graphics Control Block for a saved image, if it exists.
    587 ******************************************************************************/
    588 
    589 int DGifSavedExtensionToGCB(GifFileType *GifFile,
    590 			    int ImageIndex, GraphicsControlBlock *GCB)
    591 {
    592     int i;
    593 
    594     if (ImageIndex < 0 || ImageIndex > GifFile->ImageCount - 1)
    595 	return GIF_ERROR;
    596 
    597     GCB->DisposalMode = DISPOSAL_UNSPECIFIED;
    598     GCB->UserInputFlag = false;
    599     GCB->DelayTime = 0;
    600     GCB->TransparentColor = NO_TRANSPARENT_COLOR;
    601 
    602     for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount; i++) {
    603 	ExtensionBlock *ep = &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i];
    604 	if (ep->Function == GRAPHICS_EXT_FUNC_CODE)
    605 	    return DGifExtensionToGCB(ep->ByteCount, ep->Bytes, GCB);
    606     }
    607 
    608     return GIF_ERROR;
    609 }
    610 
    611 /******************************************************************************
    612  This routine should be called last, to close the GIF file.
    613 ******************************************************************************/
    614 int
    615 DGifCloseFile(GifFileType *GifFile)
    616 {
    617     GifFilePrivateType *Private;
    618 
    619     if (GifFile == NULL || GifFile->Private == NULL)
    620         return GIF_ERROR;
    621 
    622     if (GifFile->Image.ColorMap) {
    623         GifFreeMapObject(GifFile->Image.ColorMap);
    624         GifFile->Image.ColorMap = NULL;
    625     }
    626 
    627     if (GifFile->SColorMap) {
    628         GifFreeMapObject(GifFile->SColorMap);
    629         GifFile->SColorMap = NULL;
    630     }
    631 
    632     if (GifFile->SavedImages) {
    633         GifFreeSavedImages(GifFile);
    634         GifFile->SavedImages = NULL;
    635     }
    636 
    637     GifFreeExtensions(&GifFile->ExtensionBlockCount, &GifFile->ExtensionBlocks);
    638 
    639     Private = (GifFilePrivateType *) GifFile->Private;
    640 
    641     if (!IS_READABLE(Private)) {
    642         /* This file was NOT open for reading: */
    643         GifFile->Error = D_GIF_ERR_NOT_READABLE;
    644         return GIF_ERROR;
    645     }
    646 
    647     if (Private->File && (fclose(Private->File) != 0)) {
    648         GifFile->Error = D_GIF_ERR_CLOSE_FAILED;
    649         return GIF_ERROR;
    650     }
    651 
    652     free((char *)GifFile->Private);
    653 
    654     /*
    655      * Without the #ifndef, we get spurious warnings because Coverity mistakenly
    656      * thinks the GIF structure is freed on an error return.
    657      */
    658 #ifndef __COVERITY__
    659     free(GifFile);
    660 #endif /* __COVERITY__ */
    661 
    662     return GIF_OK;
    663 }
    664 
    665 /******************************************************************************
    666  Get 2 bytes (word) from the given file:
    667 ******************************************************************************/
    668 static int
    669 DGifGetWord(GifFileType *GifFile, GifWord *Word)
    670 {
    671     unsigned char c[2];
    672 
    673     if (READ(GifFile, c, 2) != 2) {
    674         GifFile->Error = D_GIF_ERR_READ_FAILED;
    675         return GIF_ERROR;
    676     }
    677 
    678     *Word = (GifWord)UNSIGNED_LITTLE_ENDIAN(c[0], c[1]);
    679     return GIF_OK;
    680 }
    681 
    682 /******************************************************************************
    683  Get the image code in compressed form.  This routine can be called if the
    684  information needed to be piped out as is. Obviously this is much faster
    685  than decoding and encoding again. This routine should be followed by calls
    686  to DGifGetCodeNext, until NULL block is returned.
    687  The block should NOT be freed by the user (not dynamically allocated).
    688 ******************************************************************************/
    689 int
    690 DGifGetCode(GifFileType *GifFile, int *CodeSize, GifByteType **CodeBlock)
    691 {
    692     GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
    693 
    694     if (!IS_READABLE(Private)) {
    695         /* This file was NOT open for reading: */
    696         GifFile->Error = D_GIF_ERR_NOT_READABLE;
    697         return GIF_ERROR;
    698     }
    699 
    700     *CodeSize = Private->BitsPerPixel;
    701 
    702     return DGifGetCodeNext(GifFile, CodeBlock);
    703 }
    704 
    705 /******************************************************************************
    706  Continue to get the image code in compressed form. This routine should be
    707  called until NULL block is returned.
    708  The block should NOT be freed by the user (not dynamically allocated).
    709 ******************************************************************************/
    710 int
    711 DGifGetCodeNext(GifFileType *GifFile, GifByteType **CodeBlock)
    712 {
    713     GifByteType Buf;
    714     GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
    715 
    716     /* coverity[tainted_data_argument] */
    717     if (READ(GifFile, &Buf, 1) != 1) {
    718         GifFile->Error = D_GIF_ERR_READ_FAILED;
    719         return GIF_ERROR;
    720     }
    721 
    722     /* coverity[lower_bounds] */
    723     if (Buf > 0) {
    724         *CodeBlock = Private->Buf;    /* Use private unused buffer. */
    725         (*CodeBlock)[0] = Buf;  /* Pascal strings notation (pos. 0 is len.). */
    726 	/* coverity[tainted_data] */
    727         if (READ(GifFile, &((*CodeBlock)[1]), Buf) != Buf) {
    728             GifFile->Error = D_GIF_ERR_READ_FAILED;
    729             return GIF_ERROR;
    730         }
    731     } else {
    732         *CodeBlock = NULL;
    733         Private->Buf[0] = 0;    /* Make sure the buffer is empty! */
    734         Private->PixelCount = 0;    /* And local info. indicate image read. */
    735     }
    736 
    737     return GIF_OK;
    738 }
    739 
    740 /******************************************************************************
    741  Setup the LZ decompression for this image:
    742 ******************************************************************************/
    743 static int
    744 DGifSetupDecompress(GifFileType *GifFile)
    745 {
    746     int i, BitsPerPixel;
    747     GifByteType CodeSize;
    748     GifPrefixType *Prefix;
    749     GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
    750 
    751     READ(GifFile, &CodeSize, 1);    /* Read Code size from file. */
    752     BitsPerPixel = CodeSize;
    753 
    754     Private->Buf[0] = 0;    /* Input Buffer empty. */
    755     Private->BitsPerPixel = BitsPerPixel;
    756     Private->ClearCode = (1 << BitsPerPixel);
    757     Private->EOFCode = Private->ClearCode + 1;
    758     Private->RunningCode = Private->EOFCode + 1;
    759     Private->RunningBits = BitsPerPixel + 1;    /* Number of bits per code. */
    760     Private->MaxCode1 = 1 << Private->RunningBits;    /* Max. code + 1. */
    761     Private->StackPtr = 0;    /* No pixels on the pixel stack. */
    762     Private->LastCode = NO_SUCH_CODE;
    763     Private->CrntShiftState = 0;    /* No information in CrntShiftDWord. */
    764     Private->CrntShiftDWord = 0;
    765 
    766     Prefix = Private->Prefix;
    767     for (i = 0; i <= LZ_MAX_CODE; i++)
    768         Prefix[i] = NO_SUCH_CODE;
    769 
    770     return GIF_OK;
    771 }
    772 
    773 /******************************************************************************
    774  The LZ decompression routine:
    775  This version decompress the given GIF file into Line of length LineLen.
    776  This routine can be called few times (one per scan line, for example), in
    777  order the complete the whole image.
    778 ******************************************************************************/
    779 static int
    780 DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line, int LineLen)
    781 {
    782     int i = 0;
    783     int j, CrntCode, EOFCode, ClearCode, CrntPrefix, LastCode, StackPtr;
    784     GifByteType *Stack, *Suffix;
    785     GifPrefixType *Prefix;
    786     GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
    787 
    788     StackPtr = Private->StackPtr;
    789     Prefix = Private->Prefix;
    790     Suffix = Private->Suffix;
    791     Stack = Private->Stack;
    792     EOFCode = Private->EOFCode;
    793     ClearCode = Private->ClearCode;
    794     LastCode = Private->LastCode;
    795 
    796     if (StackPtr > LZ_MAX_CODE) {
    797         return GIF_ERROR;
    798     }
    799 
    800     if (StackPtr != 0) {
    801         /* Let pop the stack off before continueing to read the GIF file: */
    802         while (StackPtr != 0 && i < LineLen)
    803             Line[i++] = Stack[--StackPtr];
    804     }
    805 
    806     while (i < LineLen) {    /* Decode LineLen items. */
    807         if (DGifDecompressInput(GifFile, &CrntCode) == GIF_ERROR)
    808             return GIF_ERROR;
    809 
    810         if (CrntCode == EOFCode) {
    811             /* Note however that usually we will not be here as we will stop
    812              * decoding as soon as we got all the pixel, or EOF code will
    813              * not be read at all, and DGifGetLine/Pixel clean everything.  */
    814 	    GifFile->Error = D_GIF_ERR_EOF_TOO_SOON;
    815 	    return GIF_ERROR;
    816         } else if (CrntCode == ClearCode) {
    817             /* We need to start over again: */
    818             for (j = 0; j <= LZ_MAX_CODE; j++)
    819                 Prefix[j] = NO_SUCH_CODE;
    820             Private->RunningCode = Private->EOFCode + 1;
    821             Private->RunningBits = Private->BitsPerPixel + 1;
    822             Private->MaxCode1 = 1 << Private->RunningBits;
    823             LastCode = Private->LastCode = NO_SUCH_CODE;
    824         } else {
    825             /* Its regular code - if in pixel range simply add it to output
    826              * stream, otherwise trace to codes linked list until the prefix
    827              * is in pixel range: */
    828             if (CrntCode < ClearCode) {
    829                 /* This is simple - its pixel scalar, so add it to output: */
    830                 Line[i++] = CrntCode;
    831             } else {
    832                 /* Its a code to needed to be traced: trace the linked list
    833                  * until the prefix is a pixel, while pushing the suffix
    834                  * pixels on our stack. If we done, pop the stack in reverse
    835                  * (thats what stack is good for!) order to output.  */
    836                 if (Prefix[CrntCode] == NO_SUCH_CODE) {
    837                     /* Only allowed if CrntCode is exactly the running code:
    838                      * In that case CrntCode = XXXCode, CrntCode or the
    839                      * prefix code is last code and the suffix char is
    840                      * exactly the prefix of last code! */
    841                     if (CrntCode == Private->RunningCode - 2) {
    842                         CrntPrefix = LastCode;
    843                         Suffix[Private->RunningCode - 2] =
    844                            Stack[StackPtr++] = DGifGetPrefixChar(Prefix,
    845                                                                  LastCode,
    846                                                                  ClearCode);
    847                     } else {
    848                         GifFile->Error = D_GIF_ERR_IMAGE_DEFECT;
    849                         return GIF_ERROR;
    850                     }
    851                 } else
    852                     CrntPrefix = CrntCode;
    853 
    854                 /* Now (if image is O.K.) we should not get a NO_SUCH_CODE
    855                  * during the trace. As we might loop forever, in case of
    856                  * defective image, we use StackPtr as loop counter and stop
    857                  * before overflowing Stack[]. */
    858                 while (StackPtr < LZ_MAX_CODE &&
    859                        CrntPrefix > ClearCode && CrntPrefix <= LZ_MAX_CODE) {
    860                     Stack[StackPtr++] = Suffix[CrntPrefix];
    861                     CrntPrefix = Prefix[CrntPrefix];
    862                 }
    863                 if (StackPtr >= LZ_MAX_CODE || CrntPrefix > LZ_MAX_CODE) {
    864                     GifFile->Error = D_GIF_ERR_IMAGE_DEFECT;
    865                     return GIF_ERROR;
    866                 }
    867                 /* Push the last character on stack: */
    868                 Stack[StackPtr++] = CrntPrefix;
    869 
    870                 /* Now lets pop all the stack into output: */
    871                 while (StackPtr != 0 && i < LineLen)
    872                     Line[i++] = Stack[--StackPtr];
    873             }
    874             if (LastCode != NO_SUCH_CODE && Prefix[Private->RunningCode - 2] == NO_SUCH_CODE) {
    875                 Prefix[Private->RunningCode - 2] = LastCode;
    876 
    877                 if (CrntCode == Private->RunningCode - 2) {
    878                     /* Only allowed if CrntCode is exactly the running code:
    879                      * In that case CrntCode = XXXCode, CrntCode or the
    880                      * prefix code is last code and the suffix char is
    881                      * exactly the prefix of last code! */
    882                     Suffix[Private->RunningCode - 2] =
    883                        DGifGetPrefixChar(Prefix, LastCode, ClearCode);
    884                 } else {
    885                     Suffix[Private->RunningCode - 2] =
    886                        DGifGetPrefixChar(Prefix, CrntCode, ClearCode);
    887                 }
    888             }
    889             LastCode = CrntCode;
    890         }
    891     }
    892 
    893     Private->LastCode = LastCode;
    894     Private->StackPtr = StackPtr;
    895 
    896     return GIF_OK;
    897 }
    898 
    899 /******************************************************************************
    900  Routine to trace the Prefixes linked list until we get a prefix which is
    901  not code, but a pixel value (less than ClearCode). Returns that pixel value.
    902  If image is defective, we might loop here forever, so we limit the loops to
    903  the maximum possible if image O.k. - LZ_MAX_CODE times.
    904 ******************************************************************************/
    905 static int
    906 DGifGetPrefixChar(GifPrefixType *Prefix, int Code, int ClearCode)
    907 {
    908     int i = 0;
    909 
    910     while (Code > ClearCode && i++ <= LZ_MAX_CODE) {
    911         if (Code > LZ_MAX_CODE) {
    912             return NO_SUCH_CODE;
    913         }
    914         Code = Prefix[Code];
    915     }
    916     return Code;
    917 }
    918 
    919 /******************************************************************************
    920  Interface for accessing the LZ codes directly. Set Code to the real code
    921  (12bits), or to -1 if EOF code is returned.
    922 ******************************************************************************/
    923 int
    924 DGifGetLZCodes(GifFileType *GifFile, int *Code)
    925 {
    926     GifByteType *CodeBlock;
    927     GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
    928 
    929     if (!IS_READABLE(Private)) {
    930         /* This file was NOT open for reading: */
    931         GifFile->Error = D_GIF_ERR_NOT_READABLE;
    932         return GIF_ERROR;
    933     }
    934 
    935     if (DGifDecompressInput(GifFile, Code) == GIF_ERROR)
    936         return GIF_ERROR;
    937 
    938     if (*Code == Private->EOFCode) {
    939         /* Skip rest of codes (hopefully only NULL terminating block): */
    940         do {
    941             if (DGifGetCodeNext(GifFile, &CodeBlock) == GIF_ERROR)
    942                 return GIF_ERROR;
    943         } while (CodeBlock != NULL) ;
    944 
    945         *Code = -1;
    946     } else if (*Code == Private->ClearCode) {
    947         /* We need to start over again: */
    948         Private->RunningCode = Private->EOFCode + 1;
    949         Private->RunningBits = Private->BitsPerPixel + 1;
    950         Private->MaxCode1 = 1 << Private->RunningBits;
    951     }
    952 
    953     return GIF_OK;
    954 }
    955 
    956 /******************************************************************************
    957  The LZ decompression input routine:
    958  This routine is responsable for the decompression of the bit stream from
    959  8 bits (bytes) packets, into the real codes.
    960  Returns GIF_OK if read successfully.
    961 ******************************************************************************/
    962 static int
    963 DGifDecompressInput(GifFileType *GifFile, int *Code)
    964 {
    965     static const unsigned short CodeMasks[] = {
    966 	0x0000, 0x0001, 0x0003, 0x0007,
    967 	0x000f, 0x001f, 0x003f, 0x007f,
    968 	0x00ff, 0x01ff, 0x03ff, 0x07ff,
    969 	0x0fff
    970     };
    971 
    972     GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
    973 
    974     GifByteType NextByte;
    975 
    976     /* The image can't contain more than LZ_BITS per code. */
    977     if (Private->RunningBits > LZ_BITS) {
    978         GifFile->Error = D_GIF_ERR_IMAGE_DEFECT;
    979         return GIF_ERROR;
    980     }
    981 
    982     while (Private->CrntShiftState < Private->RunningBits) {
    983         /* Needs to get more bytes from input stream for next code: */
    984         if (DGifBufferedInput(GifFile, Private->Buf, &NextByte) == GIF_ERROR) {
    985             return GIF_ERROR;
    986         }
    987         Private->CrntShiftDWord |=
    988 	    ((unsigned long)NextByte) << Private->CrntShiftState;
    989         Private->CrntShiftState += 8;
    990     }
    991     *Code = Private->CrntShiftDWord & CodeMasks[Private->RunningBits];
    992 
    993     Private->CrntShiftDWord >>= Private->RunningBits;
    994     Private->CrntShiftState -= Private->RunningBits;
    995 
    996     /* If code cannot fit into RunningBits bits, must raise its size. Note
    997      * however that codes above 4095 are used for special signaling.
    998      * If we're using LZ_BITS bits already and we're at the max code, just
    999      * keep using the table as it is, don't increment Private->RunningCode.
   1000      */
   1001     if (Private->RunningCode < LZ_MAX_CODE + 2 &&
   1002 	++Private->RunningCode > Private->MaxCode1 &&
   1003 	Private->RunningBits < LZ_BITS) {
   1004         Private->MaxCode1 <<= 1;
   1005         Private->RunningBits++;
   1006     }
   1007     return GIF_OK;
   1008 }
   1009 
   1010 /******************************************************************************
   1011  This routines read one GIF data block at a time and buffers it internally
   1012  so that the decompression routine could access it.
   1013  The routine returns the next byte from its internal buffer (or read next
   1014  block in if buffer empty) and returns GIF_OK if succesful.
   1015 ******************************************************************************/
   1016 static int
   1017 DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf, GifByteType *NextByte)
   1018 {
   1019     if (Buf[0] == 0) {
   1020         /* Needs to read the next buffer - this one is empty: */
   1021         if (READ(GifFile, Buf, 1) != 1) {
   1022             GifFile->Error = D_GIF_ERR_READ_FAILED;
   1023             return GIF_ERROR;
   1024         }
   1025         /* There shouldn't be any empty data blocks here as the LZW spec
   1026          * says the LZW termination code should come first.  Therefore we
   1027          * shouldn't be inside this routine at that point.
   1028          */
   1029         if (Buf[0] == 0) {
   1030             GifFile->Error = D_GIF_ERR_IMAGE_DEFECT;
   1031             return GIF_ERROR;
   1032         }
   1033         /* There shouldn't be any empty data blocks here as the LZW spec
   1034          * says the LZW termination code should come first.  Therefore we
   1035          * shouldn't be inside this routine at that point.
   1036          */
   1037         if (Buf[0] == 0) {
   1038             GifFile->Error = D_GIF_ERR_IMAGE_DEFECT;
   1039             return GIF_ERROR;
   1040         }
   1041         if (READ(GifFile, &Buf[1], Buf[0]) != Buf[0]) {
   1042             GifFile->Error = D_GIF_ERR_READ_FAILED;
   1043             return GIF_ERROR;
   1044         }
   1045         *NextByte = Buf[1];
   1046         Buf[1] = 2;    /* We use now the second place as last char read! */
   1047         Buf[0]--;
   1048     } else {
   1049         *NextByte = Buf[Buf[1]++];
   1050         Buf[0]--;
   1051     }
   1052 
   1053     return GIF_OK;
   1054 }
   1055 
   1056 /******************************************************************************
   1057  This routine reads an entire GIF into core, hanging all its state info off
   1058  the GifFileType pointer.  Call DGifOpenFileName() or DGifOpenFileHandle()
   1059  first to initialize I/O.  Its inverse is EGifSpew().
   1060 *******************************************************************************/
   1061 int
   1062 DGifSlurp(GifFileType *GifFile)
   1063 {
   1064     size_t ImageSize;
   1065     GifRecordType RecordType;
   1066     SavedImage *sp;
   1067     GifByteType *ExtData;
   1068     int ExtFunction;
   1069 
   1070     GifFile->ExtensionBlocks = NULL;
   1071     GifFile->ExtensionBlockCount = 0;
   1072 
   1073     do {
   1074         if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR)
   1075             return (GIF_ERROR);
   1076 
   1077         switch (RecordType) {
   1078           case IMAGE_DESC_RECORD_TYPE:
   1079               if (DGifGetImageDesc(GifFile) == GIF_ERROR)
   1080                   return (GIF_ERROR);
   1081 
   1082               sp = &GifFile->SavedImages[GifFile->ImageCount - 1];
   1083               /* Allocate memory for the image */
   1084               if (sp->ImageDesc.Width < 0 && sp->ImageDesc.Height < 0 &&
   1085                       sp->ImageDesc.Width > (INT_MAX / sp->ImageDesc.Height)) {
   1086                   return GIF_ERROR;
   1087               }
   1088               ImageSize = sp->ImageDesc.Width * sp->ImageDesc.Height;
   1089 
   1090               if (ImageSize > (SIZE_MAX / sizeof(GifPixelType))) {
   1091                   return GIF_ERROR;
   1092               }
   1093               sp->RasterBits = (unsigned char *)malloc(ImageSize *
   1094                       sizeof(GifPixelType));
   1095 
   1096               if (sp->RasterBits == NULL) {
   1097                   return GIF_ERROR;
   1098               }
   1099 
   1100 	      if (sp->ImageDesc.Interlace) {
   1101 		  int i, j;
   1102 		   /*
   1103 		    * The way an interlaced image should be read -
   1104 		    * offsets and jumps...
   1105 		    */
   1106 		  int InterlacedOffset[] = { 0, 4, 2, 1 };
   1107 		  int InterlacedJumps[] = { 8, 8, 4, 2 };
   1108 		  /* Need to perform 4 passes on the image */
   1109 		  for (i = 0; i < 4; i++)
   1110 		      for (j = InterlacedOffset[i];
   1111 			   j < sp->ImageDesc.Height;
   1112 			   j += InterlacedJumps[i]) {
   1113 			  if (DGifGetLine(GifFile,
   1114 					  sp->RasterBits+j*sp->ImageDesc.Width,
   1115 					  sp->ImageDesc.Width) == GIF_ERROR)
   1116 			      return GIF_ERROR;
   1117 		      }
   1118 	      }
   1119 	      else {
   1120 		  if (DGifGetLine(GifFile,sp->RasterBits,ImageSize)==GIF_ERROR)
   1121 		      return (GIF_ERROR);
   1122 	      }
   1123 
   1124               if (GifFile->ExtensionBlocks) {
   1125                   sp->ExtensionBlocks = GifFile->ExtensionBlocks;
   1126                   sp->ExtensionBlockCount = GifFile->ExtensionBlockCount;
   1127 
   1128                   GifFile->ExtensionBlocks = NULL;
   1129                   GifFile->ExtensionBlockCount = 0;
   1130               }
   1131               break;
   1132 
   1133           case EXTENSION_RECORD_TYPE:
   1134               if (DGifGetExtension(GifFile,&ExtFunction,&ExtData) == GIF_ERROR)
   1135                   return (GIF_ERROR);
   1136 	      /* Create an extension block with our data */
   1137               if (ExtData != NULL) {
   1138 	          if (GifAddExtensionBlock(&GifFile->ExtensionBlockCount,
   1139 				       &GifFile->ExtensionBlocks,
   1140 				       ExtFunction, ExtData[0], &ExtData[1])
   1141 		  == GIF_ERROR)
   1142 		  return (GIF_ERROR);
   1143               }
   1144               while (ExtData != NULL) {
   1145                   if (DGifGetExtensionNext(GifFile, &ExtData) == GIF_ERROR)
   1146                       return (GIF_ERROR);
   1147                   /* Continue the extension block */
   1148 		  if (ExtData != NULL)
   1149 		      if (GifAddExtensionBlock(&GifFile->ExtensionBlockCount,
   1150 					       &GifFile->ExtensionBlocks,
   1151 					       CONTINUE_EXT_FUNC_CODE,
   1152 					       ExtData[0], &ExtData[1]) == GIF_ERROR)
   1153                       return (GIF_ERROR);
   1154               }
   1155               break;
   1156 
   1157           case TERMINATE_RECORD_TYPE:
   1158               break;
   1159 
   1160           default:    /* Should be trapped by DGifGetRecordType */
   1161               break;
   1162         }
   1163     } while (RecordType != TERMINATE_RECORD_TYPE);
   1164 
   1165     return (GIF_OK);
   1166 }
   1167 
   1168 /* end */
   1169