1 /*
2 * SGI image file format library routines for CUPS.
3 *
4 * Copyright 2007-2011 by Apple Inc.
5 * Copyright 1993-2005 by Easy Software Products.
6 *
7 * These coded instructions, statements, and computer programs are the
8 * property of Apple Inc. and are protected by Federal copyright
9 * law. Distribution and use rights are outlined in the file "COPYING"
10 * which should have been included with this file.
11 *
12 * Contents:
13 *
14 * sgiClose() - Close an SGI image file.
15 * sgiGetRow() - Get a row of image data from a file.
16 * sgiOpen() - Open an SGI image file for reading or writing.
17 * sgiOpenFile() - Open an SGI image file for reading or writing.
18 * sgiPutRow() - Put a row of image data to a file.
19 * getlong() - Get a 32-bit big-endian integer.
20 * getshort() - Get a 16-bit big-endian integer.
21 * putlong() - Put a 32-bit big-endian integer.
22 * putshort() - Put a 16-bit big-endian integer.
23 * read_rle8() - Read 8-bit RLE data.
24 * read_rle16() - Read 16-bit RLE data.
25 * write_rle8() - Write 8-bit RLE data.
26 * write_rle16() - Write 16-bit RLE data.
27 */
28
29 #include "image-sgi.h"
30 #include "image-private.h"
31
32
33 /*
34 * Local functions...
35 */
36
37 static int getlong(FILE *);
38 static int getshort(FILE *);
39 static int putlong(long, FILE *);
40 static int putshort(unsigned short, FILE *);
41 static int read_rle8(FILE *, unsigned short *, int);
42 static int read_rle16(FILE *, unsigned short *, int);
43 static int write_rle8(FILE *, unsigned short *, int);
44 static int write_rle16(FILE *, unsigned short *, int);
45
46
47 /*
48 * 'sgiClose()' - Close an SGI image file.
49 */
50
51 int /* O - 0 on success, -1 on error */
sgiClose(sgi_t * sgip)52 sgiClose(sgi_t *sgip) /* I - SGI image */
53 {
54 int i; /* Return status */
55 long *offset; /* Looping var for offset table */
56
57
58 if (sgip == NULL)
59 return (-1);
60
61 if (sgip->mode == SGI_WRITE && sgip->comp != SGI_COMP_NONE)
62 {
63 /*
64 * Write the scanline offset table to the file...
65 */
66
67 fseek(sgip->file, 512, SEEK_SET);
68
69 for (i = sgip->ysize * sgip->zsize, offset = sgip->table[0];
70 i > 0;
71 i --, offset ++)
72 if (putlong(offset[0], sgip->file) < 0)
73 return (-1);
74
75 for (i = sgip->ysize * sgip->zsize, offset = sgip->length[0];
76 i > 0;
77 i --, offset ++)
78 if (putlong(offset[0], sgip->file) < 0)
79 return (-1);
80 }
81
82 if (sgip->table != NULL)
83 {
84 free(sgip->table[0]);
85 free(sgip->table);
86 }
87
88 if (sgip->length != NULL)
89 {
90 free(sgip->length[0]);
91 free(sgip->length);
92 }
93
94 if (sgip->comp == SGI_COMP_ARLE)
95 free(sgip->arle_row);
96
97 i = fclose(sgip->file);
98 free(sgip);
99
100 return (i);
101 }
102
103
104 /*
105 * 'sgiGetRow()' - Get a row of image data from a file.
106 */
107
108 int /* O - 0 on success, -1 on error */
sgiGetRow(sgi_t * sgip,unsigned short * row,int y,int z)109 sgiGetRow(sgi_t *sgip, /* I - SGI image */
110 unsigned short *row, /* O - Row to read */
111 int y, /* I - Line to read */
112 int z) /* I - Channel to read */
113 {
114 int x; /* X coordinate */
115 long offset; /* File offset */
116
117
118 if (sgip == NULL ||
119 row == NULL ||
120 y < 0 || y >= sgip->ysize ||
121 z < 0 || z >= sgip->zsize)
122 return (-1);
123
124 switch (sgip->comp)
125 {
126 case SGI_COMP_NONE :
127 /*
128 * Seek to the image row - optimize buffering by only seeking if
129 * necessary...
130 */
131
132 offset = 512 + (y + z * sgip->ysize) * sgip->xsize * sgip->bpp;
133 if (offset != ftell(sgip->file))
134 fseek(sgip->file, offset, SEEK_SET);
135
136 if (sgip->bpp == 1)
137 {
138 for (x = sgip->xsize; x > 0; x --, row ++)
139 *row = getc(sgip->file);
140 }
141 else
142 {
143 for (x = sgip->xsize; x > 0; x --, row ++)
144 *row = getshort(sgip->file);
145 }
146 break;
147
148 case SGI_COMP_RLE :
149 offset = sgip->table[z][y];
150 if (offset != ftell(sgip->file))
151 fseek(sgip->file, offset, SEEK_SET);
152
153 if (sgip->bpp == 1)
154 return (read_rle8(sgip->file, row, sgip->xsize));
155 else
156 return (read_rle16(sgip->file, row, sgip->xsize));
157 }
158
159 return (0);
160 }
161
162
163 /*
164 * 'sgiOpen()' - Open an SGI image file for reading or writing.
165 */
166
167 sgi_t * /* O - New image */
sgiOpen(const char * filename,int mode,int comp,int bpp,int xsize,int ysize,int zsize)168 sgiOpen(const char *filename, /* I - File to open */
169 int mode, /* I - Open mode (SGI_READ or SGI_WRITE) */
170 int comp, /* I - Type of compression */
171 int bpp, /* I - Bytes per pixel */
172 int xsize, /* I - Width of image in pixels */
173 int ysize, /* I - Height of image in pixels */
174 int zsize) /* I - Number of channels */
175 {
176 sgi_t *sgip; /* New SGI image file */
177 FILE *file; /* Image file pointer */
178
179
180 if (mode == SGI_READ)
181 file = fopen(filename, "rb");
182 else
183 file = fopen(filename, "wb+");
184
185 if (file == NULL)
186 return (NULL);
187
188 if ((sgip = sgiOpenFile(file, mode, comp, bpp, xsize, ysize, zsize)) == NULL)
189 fclose(file);
190
191 return (sgip);
192 }
193
194
195 /*
196 * 'sgiOpenFile()' - Open an SGI image file for reading or writing.
197 */
198
199 sgi_t * /* O - New image */
sgiOpenFile(FILE * file,int mode,int comp,int bpp,int xsize,int ysize,int zsize)200 sgiOpenFile(FILE *file, /* I - File to open */
201 int mode, /* I - Open mode (SGI_READ or SGI_WRITE) */
202 int comp, /* I - Type of compression */
203 int bpp, /* I - Bytes per pixel */
204 int xsize, /* I - Width of image in pixels */
205 int ysize, /* I - Height of image in pixels */
206 int zsize) /* I - Number of channels */
207 {
208 int i, j; /* Looping var */
209 char name[80]; /* Name of file in image header */
210 short magic; /* Magic number */
211 sgi_t *sgip; /* New image pointer */
212
213
214 if ((sgip = calloc(sizeof(sgi_t), 1)) == NULL)
215 return (NULL);
216
217 sgip->file = file;
218
219 switch (mode)
220 {
221 case SGI_READ :
222 sgip->mode = SGI_READ;
223
224 magic = getshort(sgip->file);
225 if (magic != SGI_MAGIC)
226 {
227 free(sgip);
228 return (NULL);
229 }
230
231 sgip->comp = getc(sgip->file);
232 sgip->bpp = getc(sgip->file);
233 getshort(sgip->file); /* Dimensions */
234 sgip->xsize = getshort(sgip->file);
235 sgip->ysize = getshort(sgip->file);
236 sgip->zsize = getshort(sgip->file);
237 getlong(sgip->file); /* Minimum pixel */
238 getlong(sgip->file); /* Maximum pixel */
239
240 if (sgip->comp)
241 {
242 /*
243 * This file is compressed; read the scanline tables...
244 */
245
246 fseek(sgip->file, 512, SEEK_SET);
247
248 if ((sgip->table = calloc(sgip->zsize, sizeof(long *))) == NULL)
249 {
250 free(sgip);
251 return (NULL);
252 }
253
254 if ((sgip->table[0] = calloc(sgip->ysize * sgip->zsize,
255 sizeof(long))) == NULL)
256 {
257 free(sgip->table);
258 free(sgip);
259 return (NULL);
260 }
261
262 for (i = 1; i < sgip->zsize; i ++)
263 sgip->table[i] = sgip->table[0] + i * sgip->ysize;
264
265 for (i = 0; i < sgip->zsize; i ++)
266 for (j = 0; j < sgip->ysize; j ++)
267 sgip->table[i][j] = getlong(sgip->file);
268 }
269 break;
270
271 case SGI_WRITE :
272 if (xsize < 1 ||
273 ysize < 1 ||
274 zsize < 1 ||
275 bpp < 1 || bpp > 2 ||
276 comp < SGI_COMP_NONE || comp > SGI_COMP_ARLE)
277 {
278 free(sgip);
279 return (NULL);
280 }
281
282 sgip->mode = SGI_WRITE;
283
284 putshort(SGI_MAGIC, sgip->file);
285 putc(((sgip->comp = comp) != 0) ? '1': '0', sgip->file);
286 putc(sgip->bpp = bpp, sgip->file);
287 putshort(3, sgip->file); /* Dimensions */
288 putshort(sgip->xsize = xsize, sgip->file);
289 putshort(sgip->ysize = ysize, sgip->file);
290 putshort(sgip->zsize = zsize, sgip->file);
291 if (bpp == 1)
292 {
293 putlong(0, sgip->file); /* Minimum pixel */
294 putlong(255, sgip->file); /* Maximum pixel */
295 }
296 else
297 {
298 putlong(-32768, sgip->file); /* Minimum pixel */
299 putlong(32767, sgip->file); /* Maximum pixel */
300 }
301 putlong(0, sgip->file); /* Reserved */
302
303 memset(name, 0, sizeof(name));
304 fwrite(name, sizeof(name), 1, sgip->file);
305
306 for (i = 0; i < 102; i ++)
307 putlong(0, sgip->file);
308
309 switch (comp)
310 {
311 case SGI_COMP_NONE : /* No compression */
312 /*
313 * This file is uncompressed. To avoid problems with sparse files,
314 * we need to write blank pixels for the entire image...
315 */
316
317 if (bpp == 1)
318 {
319 for (i = xsize * ysize * zsize; i > 0; i --)
320 putc(0, sgip->file);
321 }
322 else
323 {
324 for (i = xsize * ysize * zsize; i > 0; i --)
325 putshort(0, sgip->file);
326 }
327 break;
328
329 case SGI_COMP_ARLE : /* Aggressive RLE */
330 sgip->arle_row = calloc(xsize, sizeof(unsigned short));
331 sgip->arle_offset = 0;
332
333 case SGI_COMP_RLE : /* Run-Length Encoding */
334 /*
335 * This file is compressed; write the (blank) scanline tables...
336 */
337
338 for (i = 2 * ysize * zsize; i > 0; i --)
339 putlong(0, sgip->file);
340
341 sgip->firstrow = ftell(sgip->file);
342 sgip->nextrow = ftell(sgip->file);
343 if ((sgip->table = calloc(sgip->zsize, sizeof(long *))) == NULL)
344 {
345 free(sgip);
346 return (NULL);
347 }
348
349 if ((sgip->table[0] = calloc(sgip->ysize * sgip->zsize,
350 sizeof(long))) == NULL)
351 {
352 free(sgip->table);
353 free(sgip);
354 return (NULL);
355 }
356
357 for (i = 1; i < sgip->zsize; i ++)
358 sgip->table[i] = sgip->table[0] + i * sgip->ysize;
359
360 if ((sgip->length = calloc(sgip->zsize, sizeof(long *))) == NULL)
361 {
362 free(sgip->table);
363 free(sgip);
364 return (NULL);
365 }
366
367 if ((sgip->length[0] = calloc(sgip->ysize * sgip->zsize,
368 sizeof(long))) == NULL)
369 {
370 free(sgip->length);
371 free(sgip->table);
372 free(sgip);
373 return (NULL);
374 }
375
376 for (i = 1; i < sgip->zsize; i ++)
377 sgip->length[i] = sgip->length[0] + i * sgip->ysize;
378 break;
379 }
380 break;
381
382 default :
383 free(sgip);
384 return (NULL);
385 }
386
387 return (sgip);
388 }
389
390
391 /*
392 * 'sgiPutRow()' - Put a row of image data to a file.
393 */
394
395 int /* O - 0 on success, -1 on error */
sgiPutRow(sgi_t * sgip,unsigned short * row,int y,int z)396 sgiPutRow(sgi_t *sgip, /* I - SGI image */
397 unsigned short *row, /* I - Row to write */
398 int y, /* I - Line to write */
399 int z) /* I - Channel to write */
400 {
401 int x; /* X coordinate */
402 long offset; /* File offset */
403
404
405 if (sgip == NULL ||
406 row == NULL ||
407 y < 0 || y >= sgip->ysize ||
408 z < 0 || z >= sgip->zsize)
409 return (-1);
410
411 switch (sgip->comp)
412 {
413 case SGI_COMP_NONE :
414 /*
415 * Seek to the image row - optimize buffering by only seeking if
416 * necessary...
417 */
418
419 offset = 512 + (y + z * sgip->ysize) * sgip->xsize * sgip->bpp;
420 if (offset != ftell(sgip->file))
421 fseek(sgip->file, offset, SEEK_SET);
422
423 if (sgip->bpp == 1)
424 {
425 for (x = sgip->xsize; x > 0; x --, row ++)
426 putc(*row, sgip->file);
427 }
428 else
429 {
430 for (x = sgip->xsize; x > 0; x --, row ++)
431 putshort(*row, sgip->file);
432 }
433 break;
434
435 case SGI_COMP_ARLE :
436 if (sgip->table[z][y] != 0)
437 return (-1);
438
439 /*
440 * First check the last row written...
441 */
442
443 if (sgip->arle_offset > 0)
444 {
445 for (x = 0; x < sgip->xsize; x ++)
446 if (row[x] != sgip->arle_row[x])
447 break;
448
449 if (x == sgip->xsize)
450 {
451 sgip->table[z][y] = sgip->arle_offset;
452 sgip->length[z][y] = sgip->arle_length;
453 return (0);
454 }
455 }
456
457 /*
458 * If that didn't match, search all the previous rows...
459 */
460
461 fseek(sgip->file, sgip->firstrow, SEEK_SET);
462
463 if (sgip->bpp == 1)
464 {
465 for (;;)
466 {
467 sgip->arle_offset = ftell(sgip->file);
468 if ((sgip->arle_length = read_rle8(sgip->file, sgip->arle_row, sgip->xsize)) < 0)
469 {
470 x = 0;
471 break;
472 }
473
474 if (memcmp(row, sgip->arle_row, sgip->xsize * sizeof(unsigned short)) == 0)
475 {
476 x = sgip->xsize;
477 break;
478 }
479 }
480 }
481 else
482 {
483 for (;;)
484 {
485 sgip->arle_offset = ftell(sgip->file);
486 if ((sgip->arle_length = read_rle16(sgip->file, sgip->arle_row, sgip->xsize)) < 0)
487 {
488 x = 0;
489 break;
490 }
491
492 if (memcmp(row, sgip->arle_row, sgip->xsize * sizeof(unsigned short)) == 0)
493 {
494 x = sgip->xsize;
495 break;
496 }
497 }
498 }
499
500 if (x == sgip->xsize)
501 {
502 sgip->table[z][y] = sgip->arle_offset;
503 sgip->length[z][y] = sgip->arle_length;
504 return (0);
505 }
506 else
507 fseek(sgip->file, 0, SEEK_END); /* Clear EOF */
508
509 case SGI_COMP_RLE :
510 if (sgip->table[z][y] != 0)
511 return (-1);
512
513 offset = sgip->table[z][y] = sgip->nextrow;
514
515 if (offset != ftell(sgip->file))
516 fseek(sgip->file, offset, SEEK_SET);
517
518 if (sgip->bpp == 1)
519 x = write_rle8(sgip->file, row, sgip->xsize);
520 else
521 x = write_rle16(sgip->file, row, sgip->xsize);
522
523 if (sgip->comp == SGI_COMP_ARLE)
524 {
525 sgip->arle_offset = offset;
526 sgip->arle_length = x;
527 memcpy(sgip->arle_row, row, sgip->xsize * sizeof(unsigned short));
528 }
529
530 sgip->nextrow = ftell(sgip->file);
531 sgip->length[z][y] = x;
532
533 return (x);
534 }
535
536 return (0);
537 }
538
539
540 /*
541 * 'getlong()' - Get a 32-bit big-endian integer.
542 */
543
544 static int /* O - Long value */
getlong(FILE * fp)545 getlong(FILE *fp) /* I - File to read from */
546 {
547 unsigned char b[4]; /* Bytes from file */
548
549
550 if (fread(b, 4, 1, fp) == 0 && ferror(fp))
551 DEBUG_printf(("Error reading file!"));
552 return ((b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]);
553 }
554
555
556 /*
557 * 'getshort()' - Get a 16-bit big-endian integer.
558 */
559
560 static int /* O - Short value */
getshort(FILE * fp)561 getshort(FILE *fp) /* I - File to read from */
562 {
563 unsigned char b[2]; /* Bytes from file */
564
565
566 if (fread(b, 2, 1, fp) == 0 && ferror(fp))
567 DEBUG_printf(("Error reading file!"));
568 return ((b[0] << 8) | b[1]);
569 }
570
571
572 /*
573 * 'putlong()' - Put a 32-bit big-endian integer.
574 */
575
576 static int /* O - 0 on success, -1 on error */
putlong(long n,FILE * fp)577 putlong(long n, /* I - Long to write */
578 FILE *fp) /* I - File to write to */
579 {
580 if (putc(n >> 24, fp) == EOF)
581 return (EOF);
582 if (putc(n >> 16, fp) == EOF)
583 return (EOF);
584 if (putc(n >> 8, fp) == EOF)
585 return (EOF);
586 if (putc(n, fp) == EOF)
587 return (EOF);
588 else
589 return (0);
590 }
591
592
593 /*
594 * 'putshort()' - Put a 16-bit big-endian integer.
595 */
596
597 static int /* O - 0 on success, -1 on error */
putshort(unsigned short n,FILE * fp)598 putshort(unsigned short n, /* I - Short to write */
599 FILE *fp) /* I - File to write to */
600 {
601 if (putc(n >> 8, fp) == EOF)
602 return (EOF);
603 if (putc(n, fp) == EOF)
604 return (EOF);
605 else
606 return (0);
607 }
608
609
610 /*
611 * 'read_rle8()' - Read 8-bit RLE data.
612 */
613
614 static int /* O - Value on success, -1 on error */
read_rle8(FILE * fp,unsigned short * row,int xsize)615 read_rle8(FILE *fp, /* I - File to read from */
616 unsigned short *row, /* O - Data */
617 int xsize) /* I - Width of data in pixels */
618 {
619 int i, /* Looping var */
620 ch, /* Current character */
621 count, /* RLE count */
622 length; /* Number of bytes read... */
623
624
625 length = 0;
626
627 while (xsize > 0)
628 {
629 if ((ch = getc(fp)) == EOF)
630 return (-1);
631 length ++;
632
633 count = ch & 127;
634 if (count == 0)
635 break;
636
637 if (ch & 128)
638 {
639 for (i = 0; i < count; i ++, row ++, xsize --, length ++)
640 if (xsize > 0)
641 *row = getc(fp);
642 }
643 else
644 {
645 ch = getc(fp);
646 length ++;
647 for (i = 0; i < count && xsize > 0; i ++, row ++, xsize --)
648 *row = ch;
649 }
650 }
651
652 return (xsize > 0 ? -1 : length);
653 }
654
655
656 /*
657 * 'read_rle16()' - Read 16-bit RLE data.
658 */
659
660 static int /* O - Value on success, -1 on error */
read_rle16(FILE * fp,unsigned short * row,int xsize)661 read_rle16(FILE *fp, /* I - File to read from */
662 unsigned short *row, /* O - Data */
663 int xsize) /* I - Width of data in pixels */
664 {
665 int i, /* Looping var */
666 ch, /* Current character */
667 count, /* RLE count */
668 length; /* Number of bytes read... */
669
670
671 length = 0;
672
673 while (xsize > 0)
674 {
675 if ((ch = getshort(fp)) == EOF)
676 return (-1);
677 length ++;
678
679 count = ch & 127;
680 if (count == 0)
681 break;
682
683 if (ch & 128)
684 {
685 for (i = 0; i < count; i ++, row ++, xsize --, length ++)
686 if (xsize > 0)
687 *row = getshort(fp);
688 }
689 else
690 {
691 ch = getshort(fp);
692 length ++;
693 for (i = 0; i < count && xsize > 0; i ++, row ++, xsize --)
694 *row = ch;
695 }
696 }
697
698 return (xsize > 0 ? -1 : length * 2);
699 }
700
701
702 /*
703 * 'write_rle8()' - Write 8-bit RLE data.
704 */
705
706 static int /* O - Length on success, -1 on error */
write_rle8(FILE * fp,unsigned short * row,int xsize)707 write_rle8(FILE *fp, /* I - File to write to */
708 unsigned short *row, /* I - Data */
709 int xsize) /* I - Width of data in pixels */
710 {
711 int length, /* Length in bytes */
712 count, /* Number of repeating pixels */
713 i, /* Looping var */
714 x; /* Current column */
715 unsigned short *start, /* Start of current sequence */
716 repeat; /* Repeated pixel */
717
718
719 for (x = xsize, length = 0; x > 0;)
720 {
721 start = row;
722 row += 2;
723 x -= 2;
724
725 while (x > 0 && (row[-2] != row[-1] || row[-1] != row[0]))
726 {
727 row ++;
728 x --;
729 }
730
731 row -= 2;
732 x += 2;
733
734 count = row - start;
735 while (count > 0)
736 {
737 i = count > 126 ? 126 : count;
738 count -= i;
739
740 if (putc(128 | i, fp) == EOF)
741 return (-1);
742 length ++;
743
744 while (i > 0)
745 {
746 if (putc(*start, fp) == EOF)
747 return (-1);
748 start ++;
749 i --;
750 length ++;
751 }
752 }
753
754 if (x <= 0)
755 break;
756
757 start = row;
758 repeat = row[0];
759
760 row ++;
761 x --;
762
763 while (x > 0 && *row == repeat)
764 {
765 row ++;
766 x --;
767 }
768
769 count = row - start;
770 while (count > 0)
771 {
772 i = count > 126 ? 126 : count;
773 count -= i;
774
775 if (putc(i, fp) == EOF)
776 return (-1);
777 length ++;
778
779 if (putc(repeat, fp) == EOF)
780 return (-1);
781 length ++;
782 }
783 }
784
785 length ++;
786
787 if (putc(0, fp) == EOF)
788 return (-1);
789 else
790 return (length);
791 }
792
793
794 /*
795 * 'write_rle16()' - Write 16-bit RLE data.
796 */
797
798 static int /* O - Length in words */
write_rle16(FILE * fp,unsigned short * row,int xsize)799 write_rle16(FILE *fp, /* I - File to write to */
800 unsigned short *row, /* I - Data */
801 int xsize) /* I - Width of data in pixels */
802 {
803 int length, /* Length in words */
804 count, /* Number of repeating pixels */
805 i, /* Looping var */
806 x; /* Current column */
807 unsigned short *start, /* Start of current sequence */
808 repeat; /* Repeated pixel */
809
810
811 for (x = xsize, length = 0; x > 0;)
812 {
813 start = row;
814 row += 2;
815 x -= 2;
816
817 while (x > 0 && (row[-2] != row[-1] || row[-1] != row[0]))
818 {
819 row ++;
820 x --;
821 }
822
823 row -= 2;
824 x += 2;
825
826 count = row - start;
827 while (count > 0)
828 {
829 i = count > 126 ? 126 : count;
830 count -= i;
831
832 if (putshort(128 | i, fp) == EOF)
833 return (-1);
834 length ++;
835
836 while (i > 0)
837 {
838 if (putshort(*start, fp) == EOF)
839 return (-1);
840 start ++;
841 i --;
842 length ++;
843 }
844 }
845
846 if (x <= 0)
847 break;
848
849 start = row;
850 repeat = row[0];
851
852 row ++;
853 x --;
854
855 while (x > 0 && *row == repeat)
856 {
857 row ++;
858 x --;
859 }
860
861 count = row - start;
862 while (count > 0)
863 {
864 i = count > 126 ? 126 : count;
865 count -= i;
866
867 if (putshort(i, fp) == EOF)
868 return (-1);
869 length ++;
870
871 if (putshort(repeat, fp) == EOF)
872 return (-1);
873 length ++;
874 }
875 }
876
877 length ++;
878
879 if (putshort(0, fp) == EOF)
880 return (-1);
881 else
882 return (2 * length);
883 }
884
885