xref: /aosp_15_r20/external/giflib/egif_lib.c (revision 324bb76b8d05e2a05aa88511fff61cf3f9ca5892)
1 /******************************************************************************
2 
3 egif_lib.c - GIF encoding
4 
5 The functions here and in dgif_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 SPDX-License-Identifier: MIT
10 
11 *****************************************************************************/
12 
13 #include <fcntl.h>
14 #include <stdint.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 
19 #ifdef _WIN32
20 #include <io.h>
21 #else
22 #include <sys/types.h>
23 #include <unistd.h>
24 #endif /* _WIN32 */
25 #include <sys/stat.h>
26 
27 #include "gif_lib.h"
28 #include "gif_lib_private.h"
29 
30 /* Masks given codes to BitsPerPixel, to make sure all codes are in range: */
31 /*@+charint@*/
32 static const GifPixelType CodeMask[] = {0x00, 0x01, 0x03, 0x07, 0x0f,
33                                         0x1f, 0x3f, 0x7f, 0xff};
34 /*@-charint@*/
35 
36 static int EGifPutWord(int Word, GifFileType *GifFile);
37 static int EGifSetupCompress(GifFileType *GifFile);
38 static int EGifCompressLine(GifFileType *GifFile, const GifPixelType *Line,
39                             const int LineLen);
40 static int EGifCompressOutput(GifFileType *GifFile, int Code);
41 static int EGifBufferedOutput(GifFileType *GifFile, GifByteType *Buf, int c);
42 
43 /* extract bytes from an unsigned word */
44 #define LOBYTE(x) ((x)&0xff)
45 #define HIBYTE(x) (((x) >> 8) & 0xff)
46 
47 #ifndef S_IREAD
48 #define S_IREAD S_IRUSR
49 #endif
50 
51 #ifndef S_IWRITE
52 #define S_IWRITE S_IWUSR
53 #endif
54 /******************************************************************************
55  Open a new GIF file for write, specified by name. If TestExistance then
56  if the file exists this routines fails (returns NULL).
57  Returns a dynamically allocated GifFileType pointer which serves as the GIF
58  info record. The Error member is cleared if successful.
59 ******************************************************************************/
EGifOpenFileName(const char * FileName,const bool TestExistence,int * Error)60 GifFileType *EGifOpenFileName(const char *FileName, const bool TestExistence,
61                               int *Error) {
62 
63 	int FileHandle;
64 	GifFileType *GifFile;
65 
66 	if (TestExistence) {
67 		FileHandle = open(FileName, O_WRONLY | O_CREAT | O_EXCL,
68 		                  S_IREAD | S_IWRITE);
69 	} else {
70 		FileHandle = open(FileName, O_WRONLY | O_CREAT | O_TRUNC,
71 		                  S_IREAD | S_IWRITE);
72 	}
73 
74 	if (FileHandle == -1) {
75 		if (Error != NULL) {
76 			*Error = E_GIF_ERR_OPEN_FAILED;
77 		}
78 		return NULL;
79 	}
80 	GifFile = EGifOpenFileHandle(FileHandle, Error);
81 	if (GifFile == (GifFileType *)NULL) {
82 		(void)close(FileHandle);
83 	}
84 	return GifFile;
85 }
86 
87 /******************************************************************************
88  Update a new GIF file, given its file handle, which must be opened for
89  write in binary mode.
90  Returns dynamically allocated a GifFileType pointer which serves as the GIF
91  info record.
92  Only fails on a memory allocation error.
93 ******************************************************************************/
EGifOpenFileHandle(const int FileHandle,int * Error)94 GifFileType *EGifOpenFileHandle(const int FileHandle, int *Error) {
95 	GifFileType *GifFile;
96 	GifFilePrivateType *Private;
97 	FILE *f;
98 
99 	GifFile = (GifFileType *)malloc(sizeof(GifFileType));
100 	if (GifFile == NULL) {
101 		return NULL;
102 	}
103 
104 	memset(GifFile, '\0', sizeof(GifFileType));
105 
106 	Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType));
107 	if (Private == NULL) {
108 		free(GifFile);
109 		if (Error != NULL) {
110 			*Error = E_GIF_ERR_NOT_ENOUGH_MEM;
111 		}
112 		return NULL;
113 	}
114 	/*@i1@*/ memset(Private, '\0', sizeof(GifFilePrivateType));
115 	if ((Private->HashTable = _InitHashTable()) == NULL) {
116 		free(GifFile);
117 		free(Private);
118 		if (Error != NULL) {
119 			*Error = E_GIF_ERR_NOT_ENOUGH_MEM;
120 		}
121 		return NULL;
122 	}
123 
124 #ifdef _WIN32
125 	_setmode(FileHandle, O_BINARY); /* Make sure it is in binary mode. */
126 #endif                                  /* _WIN32 */
127 
128 	f = fdopen(FileHandle, "wb"); /* Make it into a stream: */
129 
130 	GifFile->Private = (void *)Private;
131 	Private->FileHandle = FileHandle;
132 	Private->File = f;
133 	Private->FileState = FILE_STATE_WRITE;
134 	Private->gif89 = false;
135 
136 	Private->Write = (OutputFunc)0;   /* No user write routine (MRB) */
137 	GifFile->UserData = (void *)NULL; /* No user write handle (MRB) */
138 
139 	GifFile->Error = 0;
140 
141 	return GifFile;
142 }
143 
144 /******************************************************************************
145  Output constructor that takes user supplied output function.
146  Basically just a copy of EGifOpenFileHandle. (MRB)
147 ******************************************************************************/
EGifOpen(void * userData,OutputFunc writeFunc,int * Error)148 GifFileType *EGifOpen(void *userData, OutputFunc writeFunc, int *Error) {
149 	GifFileType *GifFile;
150 	GifFilePrivateType *Private;
151 
152 	GifFile = (GifFileType *)malloc(sizeof(GifFileType));
153 	if (GifFile == NULL) {
154 		if (Error != NULL) {
155 			*Error = E_GIF_ERR_NOT_ENOUGH_MEM;
156 		}
157 		return NULL;
158 	}
159 
160 	memset(GifFile, '\0', sizeof(GifFileType));
161 
162 	Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType));
163 	if (Private == NULL) {
164 		free(GifFile);
165 		if (Error != NULL) {
166 			*Error = E_GIF_ERR_NOT_ENOUGH_MEM;
167 		}
168 		return NULL;
169 	}
170 
171 	memset(Private, '\0', sizeof(GifFilePrivateType));
172 
173 	Private->HashTable = _InitHashTable();
174 	if (Private->HashTable == NULL) {
175 		free(GifFile);
176 		free(Private);
177 		if (Error != NULL) {
178 			*Error = E_GIF_ERR_NOT_ENOUGH_MEM;
179 		}
180 		return NULL;
181 	}
182 
183 	GifFile->Private = (void *)Private;
184 	Private->FileHandle = 0;
185 	Private->File = (FILE *)0;
186 	Private->FileState = FILE_STATE_WRITE;
187 
188 	Private->Write = writeFunc;   /* User write routine (MRB) */
189 	GifFile->UserData = userData; /* User write handle (MRB) */
190 
191 	Private->gif89 = false; /* initially, write GIF87 */
192 
193 	GifFile->Error = 0;
194 
195 	return GifFile;
196 }
197 
198 /******************************************************************************
199  Routine to compute the GIF version that will be written on output.
200 ******************************************************************************/
EGifGetGifVersion(GifFileType * GifFile)201 const char *EGifGetGifVersion(GifFileType *GifFile) {
202 	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
203 	int i, j;
204 
205 	/*
206 	 * Bulletproofing - always write GIF89 if we need to.
207 	 * Note, we don't clear the gif89 flag here because
208 	 * users of the sequential API might have called EGifSetGifVersion()
209 	 * in order to set that flag.
210 	 */
211 	for (i = 0; i < GifFile->ImageCount; i++) {
212 		for (j = 0; j < GifFile->SavedImages[i].ExtensionBlockCount;
213 		     j++) {
214 			int function =
215 			    GifFile->SavedImages[i].ExtensionBlocks[j].Function;
216 
217 			if (function == COMMENT_EXT_FUNC_CODE ||
218 			    function == GRAPHICS_EXT_FUNC_CODE ||
219 			    function == PLAINTEXT_EXT_FUNC_CODE ||
220 			    function == APPLICATION_EXT_FUNC_CODE) {
221 				Private->gif89 = true;
222 			}
223 		}
224 	}
225 	for (i = 0; i < GifFile->ExtensionBlockCount; i++) {
226 		int function = GifFile->ExtensionBlocks[i].Function;
227 
228 		if (function == COMMENT_EXT_FUNC_CODE ||
229 		    function == GRAPHICS_EXT_FUNC_CODE ||
230 		    function == PLAINTEXT_EXT_FUNC_CODE ||
231 		    function == APPLICATION_EXT_FUNC_CODE) {
232 			Private->gif89 = true;
233 		}
234 	}
235 
236 	if (Private->gif89) {
237 		return GIF89_STAMP;
238 	} else {
239 		return GIF87_STAMP;
240 	}
241 }
242 
243 /******************************************************************************
244  Set the GIF version. In the extremely unlikely event that there is ever
245  another version, replace the bool argument with an enum in which the
246  GIF87 value is 0 (numerically the same as bool false) and the GIF89 value
247  is 1 (numerically the same as bool true).  That way we'll even preserve
248  object-file compatibility!
249 ******************************************************************************/
EGifSetGifVersion(GifFileType * GifFile,const bool gif89)250 void EGifSetGifVersion(GifFileType *GifFile, const bool gif89) {
251 	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
252 
253 	Private->gif89 = gif89;
254 }
255 
256 /******************************************************************************
257  All writes to the GIF should go through this.
258 ******************************************************************************/
InternalWrite(GifFileType * GifFileOut,const unsigned char * buf,size_t len)259 static int InternalWrite(GifFileType *GifFileOut, const unsigned char *buf,
260                          size_t len) {
261 	GifFilePrivateType *Private = (GifFilePrivateType *)GifFileOut->Private;
262 	if (Private->Write) {
263 		return Private->Write(GifFileOut, buf, len);
264 	} else {
265 		return fwrite(buf, 1, len, Private->File);
266 	}
267 }
268 
269 /******************************************************************************
270  This routine should be called before any other EGif calls, immediately
271  following the GIF file opening.
272 ******************************************************************************/
EGifPutScreenDesc(GifFileType * GifFile,const int Width,const int Height,const int ColorRes,const int BackGround,const ColorMapObject * ColorMap)273 int EGifPutScreenDesc(GifFileType *GifFile, const int Width, const int Height,
274                       const int ColorRes, const int BackGround,
275                       const ColorMapObject *ColorMap) {
276 	GifByteType Buf[3];
277 	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
278 	const char *write_version;
279 	GifFile->SColorMap = NULL;
280 
281 	if (Private->FileState & FILE_STATE_SCREEN) {
282 		/* If already has screen descriptor - something is wrong! */
283 		GifFile->Error = E_GIF_ERR_HAS_SCRN_DSCR;
284 		return GIF_ERROR;
285 	}
286 	if (!IS_WRITEABLE(Private)) {
287 		/* This file was NOT open for writing: */
288 		GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
289 		return GIF_ERROR;
290 	}
291 
292 	write_version = EGifGetGifVersion(GifFile);
293 
294 	/* First write the version prefix into the file. */
295 	if (InternalWrite(GifFile, (unsigned char *)write_version,
296 	                  strlen(write_version)) != strlen(write_version)) {
297 		GifFile->Error = E_GIF_ERR_WRITE_FAILED;
298 		return GIF_ERROR;
299 	}
300 
301 	GifFile->SWidth = Width;
302 	GifFile->SHeight = Height;
303 	GifFile->SColorResolution = ColorRes;
304 	GifFile->SBackGroundColor = BackGround;
305 	if (ColorMap) {
306 		GifFile->SColorMap =
307 		    GifMakeMapObject(ColorMap->ColorCount, ColorMap->Colors);
308 		if (GifFile->SColorMap == NULL) {
309 			GifFile->Error = E_GIF_ERR_NOT_ENOUGH_MEM;
310 			return GIF_ERROR;
311 		}
312 	} else {
313 		GifFile->SColorMap = NULL;
314 	}
315 
316 	/*
317 	 * Put the logical screen descriptor into the file:
318 	 */
319 	/* Logical Screen Descriptor: Dimensions */
320 	(void)EGifPutWord(Width, GifFile);
321 	(void)EGifPutWord(Height, GifFile);
322 
323 	/* Logical Screen Descriptor: Packed Fields */
324 	/* Note: We have actual size of the color table default to the largest
325 	 * possible size (7+1 == 8 bits) because the decoder can use it to
326 	 * decide how to display the files.
327 	 */
328 	Buf[0] =
329 	    (ColorMap ? 0x80 : 0x00) | /* Yes/no global colormap */
330 	    ((ColorRes - 1) << 4) | /* Bits allocated to each primary color */
331 	    (ColorMap ? ColorMap->BitsPerPixel - 1
332 	              : 0x07); /* Actual size of the
333 	                          color table. */
334 	if (ColorMap != NULL && ColorMap->SortFlag) {
335 		Buf[0] |= 0x08;
336 	}
337 	Buf[1] =
338 	    BackGround; /* Index into the ColorTable for background color */
339 	Buf[2] = GifFile->AspectByte; /* Pixel Aspect Ratio */
340 	InternalWrite(GifFile, Buf, 3);
341 
342 	/* If we have Global color map - dump it also: */
343 	if (ColorMap != NULL) {
344 		int i;
345 		for (i = 0; i < ColorMap->ColorCount; i++) {
346 			/* Put the ColorMap out also: */
347 			Buf[0] = ColorMap->Colors[i].Red;
348 			Buf[1] = ColorMap->Colors[i].Green;
349 			Buf[2] = ColorMap->Colors[i].Blue;
350 			if (InternalWrite(GifFile, Buf, 3) != 3) {
351 				GifFile->Error = E_GIF_ERR_WRITE_FAILED;
352 				return GIF_ERROR;
353 			}
354 		}
355 	}
356 
357 	/* Mark this file as has screen descriptor, and no pixel written yet: */
358 	Private->FileState |= FILE_STATE_SCREEN;
359 
360 	return GIF_OK;
361 }
362 
363 /******************************************************************************
364  This routine should be called before any attempt to dump an image - any
365  call to any of the pixel dump routines.
366 ******************************************************************************/
EGifPutImageDesc(GifFileType * GifFile,const int Left,const int Top,const int Width,const int Height,const bool Interlace,const ColorMapObject * ColorMap)367 int EGifPutImageDesc(GifFileType *GifFile, const int Left, const int Top,
368                      const int Width, const int Height, const bool Interlace,
369                      const ColorMapObject *ColorMap) {
370 	GifByteType Buf[3];
371 	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
372 
373 	if (Private->FileState & FILE_STATE_IMAGE &&
374 	    Private->PixelCount > 0xffff0000UL) {
375 		/* If already has active image descriptor - something is wrong!
376 		 */
377 		GifFile->Error = E_GIF_ERR_HAS_IMAG_DSCR;
378 		return GIF_ERROR;
379 	}
380 	if (!IS_WRITEABLE(Private)) {
381 		/* This file was NOT open for writing: */
382 		GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
383 		return GIF_ERROR;
384 	}
385 	GifFile->Image.Left = Left;
386 	GifFile->Image.Top = Top;
387 	GifFile->Image.Width = Width;
388 	GifFile->Image.Height = Height;
389 	GifFile->Image.Interlace = Interlace;
390 	if (ColorMap != GifFile->Image.ColorMap) {
391 		if (ColorMap) {
392 			if (GifFile->Image.ColorMap != NULL) {
393 				GifFreeMapObject(GifFile->Image.ColorMap);
394 				GifFile->Image.ColorMap = NULL;
395 			}
396 			GifFile->Image.ColorMap = GifMakeMapObject(
397 			    ColorMap->ColorCount, ColorMap->Colors);
398 			if (GifFile->Image.ColorMap == NULL) {
399 				GifFile->Error = E_GIF_ERR_NOT_ENOUGH_MEM;
400 				return GIF_ERROR;
401 			}
402 		} else {
403 			GifFile->Image.ColorMap = NULL;
404 		}
405 	}
406 
407 	/* Put the image descriptor into the file: */
408 	Buf[0] = DESCRIPTOR_INTRODUCER; /* Image separator character. */
409 	InternalWrite(GifFile, Buf, 1);
410 	(void)EGifPutWord(Left, GifFile);
411 	(void)EGifPutWord(Top, GifFile);
412 	(void)EGifPutWord(Width, GifFile);
413 	(void)EGifPutWord(Height, GifFile);
414 	Buf[0] = (ColorMap ? 0x80 : 0x00) | (Interlace ? 0x40 : 0x00) |
415 	         (ColorMap ? ColorMap->BitsPerPixel - 1 : 0);
416 	InternalWrite(GifFile, Buf, 1);
417 
418 	/* If we have Global color map - dump it also: */
419 	if (ColorMap != NULL) {
420 		int i;
421 		for (i = 0; i < ColorMap->ColorCount; i++) {
422 			/* Put the ColorMap out also: */
423 			Buf[0] = ColorMap->Colors[i].Red;
424 			Buf[1] = ColorMap->Colors[i].Green;
425 			Buf[2] = ColorMap->Colors[i].Blue;
426 			if (InternalWrite(GifFile, Buf, 3) != 3) {
427 				GifFile->Error = E_GIF_ERR_WRITE_FAILED;
428 				return GIF_ERROR;
429 			}
430 		}
431 	}
432 	if (GifFile->SColorMap == NULL && GifFile->Image.ColorMap == NULL) {
433 		GifFile->Error = E_GIF_ERR_NO_COLOR_MAP;
434 		return GIF_ERROR;
435 	}
436 
437 	/* Mark this file as has screen descriptor: */
438 	Private->FileState |= FILE_STATE_IMAGE;
439 	Private->PixelCount = (long)Width * (long)Height;
440 
441 	/* Reset compress algorithm parameters. */
442 	(void)EGifSetupCompress(GifFile);
443 
444 	return GIF_OK;
445 }
446 
447 /******************************************************************************
448  Put one full scanned line (Line) of length LineLen into GIF file.
449 ******************************************************************************/
EGifPutLine(GifFileType * GifFile,GifPixelType * Line,int LineLen)450 int EGifPutLine(GifFileType *GifFile, GifPixelType *Line, int LineLen) {
451 	int i;
452 	GifPixelType Mask;
453 	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
454 
455 	if (!IS_WRITEABLE(Private)) {
456 		/* This file was NOT open for writing: */
457 		GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
458 		return GIF_ERROR;
459 	}
460 
461 	if (!LineLen) {
462 		LineLen = GifFile->Image.Width;
463 	}
464 	if (Private->PixelCount < (unsigned)LineLen) {
465 		GifFile->Error = E_GIF_ERR_DATA_TOO_BIG;
466 		return GIF_ERROR;
467 	}
468 	Private->PixelCount -= LineLen;
469 
470 	/* Make sure the codes are not out of bit range, as we might generate
471 	 * wrong code (because of overflow when we combine them) in this case:
472 	 */
473 	Mask = CodeMask[Private->BitsPerPixel];
474 	for (i = 0; i < LineLen; i++) {
475 		Line[i] &= Mask;
476 	}
477 
478 	return EGifCompressLine(GifFile, Line, LineLen);
479 }
480 
481 /******************************************************************************
482  Put one pixel (Pixel) into GIF file.
483 ******************************************************************************/
EGifPutPixel(GifFileType * GifFile,GifPixelType Pixel)484 int EGifPutPixel(GifFileType *GifFile, GifPixelType Pixel) {
485 	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
486 
487 	if (!IS_WRITEABLE(Private)) {
488 		/* This file was NOT open for writing: */
489 		GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
490 		return GIF_ERROR;
491 	}
492 
493 	if (Private->PixelCount == 0) {
494 		GifFile->Error = E_GIF_ERR_DATA_TOO_BIG;
495 		return GIF_ERROR;
496 	}
497 	--Private->PixelCount;
498 
499 	/* Make sure the code is not out of bit range, as we might generate
500 	 * wrong code (because of overflow when we combine them) in this case:
501 	 */
502 	Pixel &= CodeMask[Private->BitsPerPixel];
503 
504 	return EGifCompressLine(GifFile, &Pixel, 1);
505 }
506 
507 /******************************************************************************
508  Put a comment into GIF file using the GIF89 comment extension block.
509 ******************************************************************************/
EGifPutComment(GifFileType * GifFile,const char * Comment)510 int EGifPutComment(GifFileType *GifFile, const char *Comment) {
511 	unsigned int length;
512 	char *buf;
513 
514 	length = strlen(Comment);
515 	if (length <= 255) {
516 		return EGifPutExtension(GifFile, COMMENT_EXT_FUNC_CODE, length,
517 		                        Comment);
518 	} else {
519 		buf = (char *)Comment;
520 		if (EGifPutExtensionLeader(GifFile, COMMENT_EXT_FUNC_CODE) ==
521 		    GIF_ERROR) {
522 			return GIF_ERROR;
523 		}
524 
525 		/* Break the comment into 255 byte sub blocks */
526 		while (length > 255) {
527 			if (EGifPutExtensionBlock(GifFile, 255, buf) ==
528 			    GIF_ERROR) {
529 				return GIF_ERROR;
530 			}
531 			buf = buf + 255;
532 			length -= 255;
533 		}
534 		/* Output any partial block and the clear code. */
535 		if (length > 0) {
536 			if (EGifPutExtensionBlock(GifFile, length, buf) ==
537 			    GIF_ERROR) {
538 				return GIF_ERROR;
539 			}
540 		}
541 		if (EGifPutExtensionTrailer(GifFile) == GIF_ERROR) {
542 			return GIF_ERROR;
543 		}
544 	}
545 	return GIF_OK;
546 }
547 
548 /******************************************************************************
549  Begin an extension block (see GIF manual).  More
550  extensions can be dumped using EGifPutExtensionBlock until
551  EGifPutExtensionTrailer is invoked.
552 ******************************************************************************/
EGifPutExtensionLeader(GifFileType * GifFile,const int ExtCode)553 int EGifPutExtensionLeader(GifFileType *GifFile, const int ExtCode) {
554 	GifByteType Buf[3];
555 	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
556 
557 	if (!IS_WRITEABLE(Private)) {
558 		/* This file was NOT open for writing: */
559 		GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
560 		return GIF_ERROR;
561 	}
562 
563 	Buf[0] = EXTENSION_INTRODUCER;
564 	Buf[1] = ExtCode;
565 	InternalWrite(GifFile, Buf, 2);
566 
567 	return GIF_OK;
568 }
569 
570 /******************************************************************************
571  Put extension block data (see GIF manual) into a GIF file.
572 ******************************************************************************/
EGifPutExtensionBlock(GifFileType * GifFile,const int ExtLen,const void * Extension)573 int EGifPutExtensionBlock(GifFileType *GifFile, const int ExtLen,
574                           const void *Extension) {
575 	GifByteType Buf;
576 	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
577 
578 	if (!IS_WRITEABLE(Private)) {
579 		/* This file was NOT open for writing: */
580 		GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
581 		return GIF_ERROR;
582 	}
583 
584 	Buf = ExtLen;
585 	InternalWrite(GifFile, &Buf, 1);
586 	InternalWrite(GifFile, Extension, ExtLen);
587 
588 	return GIF_OK;
589 }
590 
591 /******************************************************************************
592  Put a terminating block (see GIF manual) into a GIF file.
593 ******************************************************************************/
EGifPutExtensionTrailer(GifFileType * GifFile)594 int EGifPutExtensionTrailer(GifFileType *GifFile) {
595 
596 	GifByteType Buf;
597 	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
598 
599 	if (!IS_WRITEABLE(Private)) {
600 		/* This file was NOT open for writing: */
601 		GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
602 		return GIF_ERROR;
603 	}
604 
605 	/* Write the block terminator */
606 	Buf = 0;
607 	InternalWrite(GifFile, &Buf, 1);
608 
609 	return GIF_OK;
610 }
611 
612 /******************************************************************************
613  Put an extension block (see GIF manual) into a GIF file.
614  Warning: This function is only useful for Extension blocks that have at
615  most one subblock.  Extensions with more than one subblock need to use the
616  EGifPutExtension{Leader,Block,Trailer} functions instead.
617 ******************************************************************************/
EGifPutExtension(GifFileType * GifFile,const int ExtCode,const int ExtLen,const void * Extension)618 int EGifPutExtension(GifFileType *GifFile, const int ExtCode, const int ExtLen,
619                      const void *Extension) {
620 
621 	GifByteType Buf[3];
622 	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
623 
624 	if (!IS_WRITEABLE(Private)) {
625 		/* This file was NOT open for writing: */
626 		GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
627 		return GIF_ERROR;
628 	}
629 
630 	if (ExtCode == 0) {
631 		InternalWrite(GifFile, (GifByteType *)&ExtLen, 1);
632 	} else {
633 		Buf[0] = EXTENSION_INTRODUCER;
634 		Buf[1] = ExtCode; /* Extension Label */
635 		Buf[2] = ExtLen;  /* Extension length */
636 		InternalWrite(GifFile, Buf, 3);
637 	}
638 	InternalWrite(GifFile, Extension, ExtLen);
639 	Buf[0] = 0;
640 	InternalWrite(GifFile, Buf, 1);
641 
642 	return GIF_OK;
643 }
644 
645 /******************************************************************************
646  Render a Graphics Control Block as raw extension data
647 ******************************************************************************/
648 
EGifGCBToExtension(const GraphicsControlBlock * GCB,GifByteType * GifExtension)649 size_t EGifGCBToExtension(const GraphicsControlBlock *GCB,
650                           GifByteType *GifExtension) {
651 	GifExtension[0] = 0;
652 	GifExtension[0] |=
653 	    (GCB->TransparentColor == NO_TRANSPARENT_COLOR) ? 0x00 : 0x01;
654 	GifExtension[0] |= GCB->UserInputFlag ? 0x02 : 0x00;
655 	GifExtension[0] |= ((GCB->DisposalMode & 0x07) << 2);
656 	GifExtension[1] = LOBYTE(GCB->DelayTime);
657 	GifExtension[2] = HIBYTE(GCB->DelayTime);
658 	GifExtension[3] = (char)GCB->TransparentColor;
659 	return 4;
660 }
661 
662 /******************************************************************************
663  Replace the Graphics Control Block for a saved image, if it exists.
664 ******************************************************************************/
665 
EGifGCBToSavedExtension(const GraphicsControlBlock * GCB,GifFileType * GifFile,int ImageIndex)666 int EGifGCBToSavedExtension(const GraphicsControlBlock *GCB,
667                             GifFileType *GifFile, int ImageIndex) {
668 	int i;
669 	size_t Len;
670 	GifByteType buf[sizeof(GraphicsControlBlock)]; /* a bit dodgy... */
671 
672 	if (ImageIndex < 0 || ImageIndex > GifFile->ImageCount - 1) {
673 		return GIF_ERROR;
674 	}
675 
676 	for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount;
677 	     i++) {
678 		ExtensionBlock *ep =
679 		    &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i];
680 		if (ep->Function == GRAPHICS_EXT_FUNC_CODE) {
681 			EGifGCBToExtension(GCB, ep->Bytes);
682 			return GIF_OK;
683 		}
684 	}
685 
686 	Len = EGifGCBToExtension(GCB, (GifByteType *)buf);
687 	if (GifAddExtensionBlock(
688 	        &GifFile->SavedImages[ImageIndex].ExtensionBlockCount,
689 	        &GifFile->SavedImages[ImageIndex].ExtensionBlocks,
690 	        GRAPHICS_EXT_FUNC_CODE, Len,
691 	        (unsigned char *)buf) == GIF_ERROR) {
692 		return (GIF_ERROR);
693 	}
694 
695 	return (GIF_OK);
696 }
697 
698 /******************************************************************************
699  Put the image code in compressed form. This routine can be called if the
700  information needed to be piped out as is. Obviously this is much faster
701  than decoding and encoding again. This routine should be followed by calls
702  to EGifPutCodeNext, until NULL block is given.
703  The block should NOT be freed by the user (not dynamically allocated).
704 ******************************************************************************/
EGifPutCode(GifFileType * GifFile,int CodeSize,const GifByteType * CodeBlock)705 int EGifPutCode(GifFileType *GifFile, int CodeSize,
706                 const GifByteType *CodeBlock) {
707 	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
708 
709 	if (!IS_WRITEABLE(Private)) {
710 		/* This file was NOT open for writing: */
711 		GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
712 		return GIF_ERROR;
713 	}
714 
715 	/* No need to dump code size as Compression set up does any for us: */
716 	/*
717 	 * Buf = CodeSize;
718 	 * if (InternalWrite(GifFile, &Buf, 1) != 1) {
719 	 *      GifFile->Error = E_GIF_ERR_WRITE_FAILED;
720 	 *      return GIF_ERROR;
721 	 * }
722 	 */
723 
724 	return EGifPutCodeNext(GifFile, CodeBlock);
725 }
726 
727 /******************************************************************************
728  Continue to put the image code in compressed form. This routine should be
729  called with blocks of code as read via DGifGetCode/DGifGetCodeNext. If
730  given buffer pointer is NULL, empty block is written to mark end of code.
731 ******************************************************************************/
EGifPutCodeNext(GifFileType * GifFile,const GifByteType * CodeBlock)732 int EGifPutCodeNext(GifFileType *GifFile, const GifByteType *CodeBlock) {
733 	GifByteType Buf;
734 	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
735 
736 	if (CodeBlock != NULL) {
737 		if (InternalWrite(GifFile, CodeBlock, CodeBlock[0] + 1) !=
738 		    (unsigned)(CodeBlock[0] + 1)) {
739 			GifFile->Error = E_GIF_ERR_WRITE_FAILED;
740 			return GIF_ERROR;
741 		}
742 	} else {
743 		Buf = 0;
744 		if (InternalWrite(GifFile, &Buf, 1) != 1) {
745 			GifFile->Error = E_GIF_ERR_WRITE_FAILED;
746 			return GIF_ERROR;
747 		}
748 		Private->PixelCount =
749 		    0; /* And local info. indicate image read. */
750 	}
751 
752 	return GIF_OK;
753 }
754 
755 /******************************************************************************
756  This routine should be called last, to close the GIF file.
757 ******************************************************************************/
EGifCloseFile(GifFileType * GifFile,int * ErrorCode)758 int EGifCloseFile(GifFileType *GifFile, int *ErrorCode) {
759 	GifByteType Buf;
760 	GifFilePrivateType *Private;
761 	FILE *File;
762 
763 	if (GifFile == NULL) {
764 		return GIF_ERROR;
765 	}
766 
767 	Private = (GifFilePrivateType *)GifFile->Private;
768 	if (Private == NULL) {
769 		return GIF_ERROR;
770 	} else if (!IS_WRITEABLE(Private)) {
771 		/* This file was NOT open for writing: */
772 		if (ErrorCode != NULL) {
773 			*ErrorCode = E_GIF_ERR_NOT_WRITEABLE;
774 		}
775 		free(GifFile);
776 		return GIF_ERROR;
777 	} else {
778 		File = Private->File;
779 
780 		Buf = TERMINATOR_INTRODUCER;
781 		InternalWrite(GifFile, &Buf, 1);
782 
783 		if (GifFile->Image.ColorMap) {
784 			GifFreeMapObject(GifFile->Image.ColorMap);
785 			GifFile->Image.ColorMap = NULL;
786 		}
787 		if (GifFile->SColorMap) {
788 			GifFreeMapObject(GifFile->SColorMap);
789 			GifFile->SColorMap = NULL;
790 		}
791 		if (Private->HashTable) {
792 			free((char *)Private->HashTable);
793 		}
794 		free((char *)Private);
795 
796 		if (File && fclose(File) != 0) {
797 			if (ErrorCode != NULL) {
798 				*ErrorCode = E_GIF_ERR_CLOSE_FAILED;
799 			}
800 			free(GifFile);
801 			return GIF_ERROR;
802 		}
803 
804 		free(GifFile);
805 		if (ErrorCode != NULL) {
806 			*ErrorCode = E_GIF_SUCCEEDED;
807 		}
808 	}
809 	return GIF_OK;
810 }
811 
812 /******************************************************************************
813  Put 2 bytes (a word) into the given file in little-endian order:
814 ******************************************************************************/
EGifPutWord(int Word,GifFileType * GifFile)815 static int EGifPutWord(int Word, GifFileType *GifFile) {
816 	unsigned char c[2];
817 
818 	c[0] = LOBYTE(Word);
819 	c[1] = HIBYTE(Word);
820 	if (InternalWrite(GifFile, c, 2) == 2) {
821 		return GIF_OK;
822 	} else {
823 		return GIF_ERROR;
824 	}
825 }
826 
827 /******************************************************************************
828  Setup the LZ compression for this image:
829 ******************************************************************************/
EGifSetupCompress(GifFileType * GifFile)830 static int EGifSetupCompress(GifFileType *GifFile) {
831 	int BitsPerPixel;
832 	GifByteType Buf;
833 	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
834 
835 	/* Test and see what color map to use, and from it # bits per pixel: */
836 	if (GifFile->Image.ColorMap) {
837 		BitsPerPixel = GifFile->Image.ColorMap->BitsPerPixel;
838 	} else if (GifFile->SColorMap) {
839 		BitsPerPixel = GifFile->SColorMap->BitsPerPixel;
840 	} else {
841 		GifFile->Error = E_GIF_ERR_NO_COLOR_MAP;
842 		return GIF_ERROR;
843 	}
844 
845 	Buf = BitsPerPixel = (BitsPerPixel < 2 ? 2 : BitsPerPixel);
846 	InternalWrite(GifFile, &Buf, 1); /* Write the Code size to file. */
847 
848 	Private->Buf[0] = 0; /* Nothing was output yet. */
849 	Private->BitsPerPixel = BitsPerPixel;
850 	Private->ClearCode = (1 << BitsPerPixel);
851 	Private->EOFCode = Private->ClearCode + 1;
852 	Private->RunningCode = Private->EOFCode + 1;
853 	Private->RunningBits = BitsPerPixel + 1; /* Number of bits per code. */
854 	Private->MaxCode1 = 1 << Private->RunningBits; /* Max. code + 1. */
855 	Private->CrntCode = FIRST_CODE; /* Signal that this is first one! */
856 	Private->CrntShiftState = 0;    /* No information in CrntShiftDWord. */
857 	Private->CrntShiftDWord = 0;
858 
859 	/* Clear hash table and send Clear to make sure the decoder do the same.
860 	 */
861 	_ClearHashTable(Private->HashTable);
862 
863 	if (EGifCompressOutput(GifFile, Private->ClearCode) == GIF_ERROR) {
864 		GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
865 		return GIF_ERROR;
866 	}
867 	return GIF_OK;
868 }
869 
870 /******************************************************************************
871  The LZ compression routine:
872  This version compresses the given buffer Line of length LineLen.
873  This routine can be called a few times (one per scan line, for example), in
874  order to complete the whole image.
875 ******************************************************************************/
EGifCompressLine(GifFileType * GifFile,const GifPixelType * Line,const int LineLen)876 static int EGifCompressLine(GifFileType *GifFile, const GifPixelType *Line,
877                             const int LineLen) {
878 	int i = 0, CrntCode;
879 	GifHashTableType *HashTable;
880 	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
881 
882 	HashTable = Private->HashTable;
883 
884 	if (Private->CrntCode == FIRST_CODE) { /* Its first time! */
885 		CrntCode = Line[i++];
886 	} else {
887 		CrntCode =
888 		    Private->CrntCode; /* Get last code in compression. */
889 	}
890 	while (i < LineLen) { /* Decode LineLen items. */
891 		GifPixelType Pixel =
892 		    Line[i++]; /* Get next pixel from stream. */
893 		/* Form a new unique key to search hash table for the code
894 		 * combines CrntCode as Prefix string with Pixel as postfix
895 		 * char.
896 		 */
897 		int NewCode;
898 		unsigned long NewKey = (((uint32_t)CrntCode) << 8) + Pixel;
899 		if ((NewCode = _ExistsHashTable(HashTable, NewKey)) >= 0) {
900 			/* This Key is already there, or the string is old one,
901 			 * so simple take new code as our CrntCode:
902 			 */
903 			CrntCode = NewCode;
904 		} else {
905 			/* Put it in hash table, output the prefix code, and
906 			 * make our CrntCode equal to Pixel.
907 			 */
908 			if (EGifCompressOutput(GifFile, CrntCode) ==
909 			    GIF_ERROR) {
910 				GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
911 				return GIF_ERROR;
912 			}
913 			CrntCode = Pixel;
914 
915 			/* If however the HashTable if full, we send a clear
916 			 * first and Clear the hash table.
917 			 */
918 			if (Private->RunningCode >= LZ_MAX_CODE) {
919 				/* Time to do some clearance: */
920 				if (EGifCompressOutput(GifFile,
921 				                       Private->ClearCode) ==
922 				    GIF_ERROR) {
923 					GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
924 					return GIF_ERROR;
925 				}
926 				Private->RunningCode = Private->EOFCode + 1;
927 				Private->RunningBits =
928 				    Private->BitsPerPixel + 1;
929 				Private->MaxCode1 = 1 << Private->RunningBits;
930 				_ClearHashTable(HashTable);
931 			} else {
932 				/* Put this unique key with its relative Code in
933 				 * hash table: */
934 				_InsertHashTable(HashTable, NewKey,
935 				                 Private->RunningCode++);
936 			}
937 		}
938 	}
939 
940 	/* Preserve the current state of the compression algorithm: */
941 	Private->CrntCode = CrntCode;
942 
943 	if (Private->PixelCount == 0) {
944 		/* We are done - output last Code and flush output buffers: */
945 		if (EGifCompressOutput(GifFile, CrntCode) == GIF_ERROR) {
946 			GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
947 			return GIF_ERROR;
948 		}
949 		if (EGifCompressOutput(GifFile, Private->EOFCode) ==
950 		    GIF_ERROR) {
951 			GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
952 			return GIF_ERROR;
953 		}
954 		if (EGifCompressOutput(GifFile, FLUSH_OUTPUT) == GIF_ERROR) {
955 			GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
956 			return GIF_ERROR;
957 		}
958 	}
959 
960 	return GIF_OK;
961 }
962 
963 /******************************************************************************
964  The LZ compression output routine:
965  This routine is responsible for the compression of the bit stream into
966  8 bits (bytes) packets.
967  Returns GIF_OK if written successfully.
968 ******************************************************************************/
EGifCompressOutput(GifFileType * GifFile,const int Code)969 static int EGifCompressOutput(GifFileType *GifFile, const int Code) {
970 	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
971 	int retval = GIF_OK;
972 
973 	if (Code == FLUSH_OUTPUT) {
974 		while (Private->CrntShiftState > 0) {
975 			/* Get Rid of what is left in DWord, and flush it. */
976 			if (EGifBufferedOutput(GifFile, Private->Buf,
977 			                       Private->CrntShiftDWord &
978 			                           0xff) == GIF_ERROR) {
979 				retval = GIF_ERROR;
980 			}
981 			Private->CrntShiftDWord >>= 8;
982 			Private->CrntShiftState -= 8;
983 		}
984 		Private->CrntShiftState = 0; /* For next time. */
985 		if (EGifBufferedOutput(GifFile, Private->Buf, FLUSH_OUTPUT) ==
986 		    GIF_ERROR) {
987 			retval = GIF_ERROR;
988 		}
989 	} else {
990 		Private->CrntShiftDWord |= ((long)Code)
991 		                           << Private->CrntShiftState;
992 		Private->CrntShiftState += Private->RunningBits;
993 		while (Private->CrntShiftState >= 8) {
994 			/* Dump out full bytes: */
995 			if (EGifBufferedOutput(GifFile, Private->Buf,
996 			                       Private->CrntShiftDWord &
997 			                           0xff) == GIF_ERROR) {
998 				retval = GIF_ERROR;
999 			}
1000 			Private->CrntShiftDWord >>= 8;
1001 			Private->CrntShiftState -= 8;
1002 		}
1003 	}
1004 
1005 	/* If code cannt fit into RunningBits bits, must raise its size. Note */
1006 	/* however that codes above 4095 are used for special signaling.      */
1007 	if (Private->RunningCode >= Private->MaxCode1 && Code <= 4095) {
1008 		Private->MaxCode1 = 1 << ++Private->RunningBits;
1009 	}
1010 
1011 	return retval;
1012 }
1013 
1014 /******************************************************************************
1015  This routines buffers the given characters until 255 characters are ready
1016  to be output. If Code is equal to -1 the buffer is flushed (EOF).
1017  The buffer is Dumped with first byte as its size, as GIF format requires.
1018  Returns GIF_OK if written successfully.
1019 ******************************************************************************/
EGifBufferedOutput(GifFileType * GifFile,GifByteType * Buf,int c)1020 static int EGifBufferedOutput(GifFileType *GifFile, GifByteType *Buf, int c) {
1021 	if (c == FLUSH_OUTPUT) {
1022 		/* Flush everything out. */
1023 		if (Buf[0] != 0 && InternalWrite(GifFile, Buf, Buf[0] + 1) !=
1024 		                       (unsigned)(Buf[0] + 1)) {
1025 			GifFile->Error = E_GIF_ERR_WRITE_FAILED;
1026 			return GIF_ERROR;
1027 		}
1028 		/* Mark end of compressed data, by an empty block (see GIF doc):
1029 		 */
1030 		Buf[0] = 0;
1031 		if (InternalWrite(GifFile, Buf, 1) != 1) {
1032 			GifFile->Error = E_GIF_ERR_WRITE_FAILED;
1033 			return GIF_ERROR;
1034 		}
1035 	} else {
1036 		if (Buf[0] == 255) {
1037 			/* Dump out this buffer - it is full: */
1038 			if (InternalWrite(GifFile, Buf, Buf[0] + 1) !=
1039 			    (unsigned)(Buf[0] + 1)) {
1040 				GifFile->Error = E_GIF_ERR_WRITE_FAILED;
1041 				return GIF_ERROR;
1042 			}
1043 			Buf[0] = 0;
1044 		}
1045 		Buf[++Buf[0]] = c;
1046 	}
1047 
1048 	return GIF_OK;
1049 }
1050 
1051 /******************************************************************************
1052  This routine writes to disk an in-core representation of a GIF previously
1053  created by DGifSlurp().
1054 ******************************************************************************/
1055 
EGifWriteExtensions(GifFileType * GifFileOut,ExtensionBlock * ExtensionBlocks,int ExtensionBlockCount)1056 static int EGifWriteExtensions(GifFileType *GifFileOut,
1057                                ExtensionBlock *ExtensionBlocks,
1058                                int ExtensionBlockCount) {
1059 	if (ExtensionBlocks) {
1060 		int j;
1061 
1062 		for (j = 0; j < ExtensionBlockCount; j++) {
1063 			ExtensionBlock *ep = &ExtensionBlocks[j];
1064 			if (ep->Function != CONTINUE_EXT_FUNC_CODE) {
1065 				if (EGifPutExtensionLeader(GifFileOut,
1066 				                           ep->Function) ==
1067 				    GIF_ERROR) {
1068 					return (GIF_ERROR);
1069 				}
1070 			}
1071 			if (EGifPutExtensionBlock(GifFileOut, ep->ByteCount,
1072 			                          ep->Bytes) == GIF_ERROR) {
1073 				return (GIF_ERROR);
1074 			}
1075 			if (j == ExtensionBlockCount - 1 ||
1076 			    (ep + 1)->Function != CONTINUE_EXT_FUNC_CODE) {
1077 				if (EGifPutExtensionTrailer(GifFileOut) ==
1078 				    GIF_ERROR) {
1079 					return (GIF_ERROR);
1080 				}
1081 			}
1082 		}
1083 	}
1084 
1085 	return (GIF_OK);
1086 }
1087 
EGifSpew(GifFileType * GifFileOut)1088 int EGifSpew(GifFileType *GifFileOut) {
1089 	int i, j;
1090 
1091 	if (EGifPutScreenDesc(GifFileOut, GifFileOut->SWidth,
1092 	                      GifFileOut->SHeight, GifFileOut->SColorResolution,
1093 	                      GifFileOut->SBackGroundColor,
1094 	                      GifFileOut->SColorMap) == GIF_ERROR) {
1095 		return (GIF_ERROR);
1096 	}
1097 
1098 	for (i = 0; i < GifFileOut->ImageCount; i++) {
1099 		SavedImage *sp = &GifFileOut->SavedImages[i];
1100 		int SavedHeight = sp->ImageDesc.Height;
1101 		int SavedWidth = sp->ImageDesc.Width;
1102 
1103 		/* this allows us to delete images by nuking their rasters */
1104 		if (sp->RasterBits == NULL) {
1105 			continue;
1106 		}
1107 
1108 		if (EGifWriteExtensions(GifFileOut, sp->ExtensionBlocks,
1109 		                        sp->ExtensionBlockCount) == GIF_ERROR) {
1110 			return (GIF_ERROR);
1111 		}
1112 
1113 		if (EGifPutImageDesc(GifFileOut, sp->ImageDesc.Left,
1114 		                     sp->ImageDesc.Top, SavedWidth, SavedHeight,
1115 		                     sp->ImageDesc.Interlace,
1116 		                     sp->ImageDesc.ColorMap) == GIF_ERROR) {
1117 			return (GIF_ERROR);
1118 		}
1119 
1120 		if (sp->ImageDesc.Interlace) {
1121 			/*
1122 			 * The way an interlaced image should be written -
1123 			 * offsets and jumps...
1124 			 */
1125 			static const int InterlacedOffset[] = {0, 4, 2, 1};
1126 			static const int InterlacedJumps[] = {8, 8, 4, 2};
1127 			int k;
1128 			/* Need to perform 4 passes on the images: */
1129 			for (k = 0; k < 4; k++) {
1130 				for (j = InterlacedOffset[k]; j < SavedHeight;
1131 				     j += InterlacedJumps[k]) {
1132 					if (EGifPutLine(
1133 					        GifFileOut,
1134 					        sp->RasterBits + j * SavedWidth,
1135 					        SavedWidth) == GIF_ERROR) {
1136 						return (GIF_ERROR);
1137 					}
1138 				}
1139 			}
1140 		} else {
1141 			for (j = 0; j < SavedHeight; j++) {
1142 				if (EGifPutLine(GifFileOut,
1143 				                sp->RasterBits + j * SavedWidth,
1144 				                SavedWidth) == GIF_ERROR) {
1145 					return (GIF_ERROR);
1146 				}
1147 			}
1148 		}
1149 	}
1150 
1151 	if (EGifWriteExtensions(GifFileOut, GifFileOut->ExtensionBlocks,
1152 	                        GifFileOut->ExtensionBlockCount) == GIF_ERROR) {
1153 		return (GIF_ERROR);
1154 	}
1155 
1156 	if (EGifCloseFile(GifFileOut, NULL) == GIF_ERROR) {
1157 		return (GIF_ERROR);
1158 	}
1159 
1160 	return (GIF_OK);
1161 }
1162 
1163 /* end */
1164