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