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