]> granicus.if.org Git - imagemagick/blob - coders/mpc.c
(no commit message)
[imagemagick] / coders / mpc.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                            M   M  PPPP    CCCC                              %
7 %                            MM MM  P   P  C                                  %
8 %                            M M M  PPPP   C                                  %
9 %                            M   M  P      C                                  %
10 %                            M   M  P       CCCC                              %
11 %                                                                             %
12 %                                                                             %
13 %              Read/Write Magick Persistant Cache Image Format                %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                John Cristy                                  %
17 %                                 March 2000                                  %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 %
38 */
39 \f
40 /*
41   Include declarations.
42 */
43 #include "magick/studio.h"
44 #include "magick/artifact.h"
45 #include "magick/attribute.h"
46 #include "magick/blob.h"
47 #include "magick/blob-private.h"
48 #include "magick/cache.h"
49 #include "magick/color.h"
50 #include "magick/color-private.h"
51 #include "magick/colormap.h"
52 #include "magick/constitute.h"
53 #include "magick/exception.h"
54 #include "magick/exception-private.h"
55 #include "magick/geometry.h"
56 #include "magick/hashmap.h"
57 #include "magick/image.h"
58 #include "magick/image-private.h"
59 #include "magick/list.h"
60 #include "magick/magick.h"
61 #include "magick/memory_.h"
62 #include "magick/module.h"
63 #include "magick/monitor.h"
64 #include "magick/monitor-private.h"
65 #include "magick/option.h"
66 #include "magick/profile.h"
67 #include "magick/property.h"
68 #include "magick/quantum-private.h"
69 #include "magick/static.h"
70 #include "magick/statistic.h"
71 #include "magick/string_.h"
72 #include "magick/string-private.h"
73 #include "magick/utility.h"
74 \f
75 /*
76   Forward declarations.
77 */
78 static MagickBooleanType
79   WriteMPCImage(const ImageInfo *,Image *);
80 \f
81 /*
82 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
83 %                                                                             %
84 %                                                                             %
85 %                                                                             %
86 %   I s M P C                                                                 %
87 %                                                                             %
88 %                                                                             %
89 %                                                                             %
90 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
91 %
92 %  IsMPC() returns MagickTrue if the image format type, identified by the
93 %  magick string, is an Magick Persistent Cache image.
94 %
95 %  The format of the IsMPC method is:
96 %
97 %      MagickBooleanType IsMPC(const unsigned char *magick,const size_t length)
98 %
99 %  A description of each parameter follows:
100 %
101 %    o magick: compare image format pattern against these bytes.
102 %
103 %    o length: Specifies the length of the magick string.
104 %
105 */
106 static MagickBooleanType IsMPC(const unsigned char *magick,const size_t length)
107 {
108   if (length < 14)
109     return(MagickFalse);
110   if (LocaleNCompare((const char *) magick,"id=MagickCache",14) == 0)
111     return(MagickTrue);
112   return(MagickFalse);
113 }
114 \f
115 /*
116 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
117 %                                                                             %
118 %                                                                             %
119 %                                                                             %
120 %   R e a d C A C H E I m a g e                                               %
121 %                                                                             %
122 %                                                                             %
123 %                                                                             %
124 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
125 %
126 %  ReadMPCImage() reads an Magick Persistent Cache image file and returns
127 %  it.  It allocates the memory necessary for the new Image structure and
128 %  returns a pointer to the new image.
129 %
130 %  The format of the ReadMPCImage method is:
131 %
132 %      Image *ReadMPCImage(const ImageInfo *image_info,ExceptionInfo *exception)
133 %
134 %  Decompression code contributed by Kyle Shorter.
135 %
136 %  A description of each parameter follows:
137 %
138 %    o image_info: the image info.
139 %
140 %    o exception: return any errors or warnings in this structure.
141 %
142 */
143 static Image *ReadMPCImage(const ImageInfo *image_info,ExceptionInfo *exception)
144 {
145   char
146     cache_filename[MaxTextExtent],
147     id[MaxTextExtent],
148     keyword[MaxTextExtent],
149     *options;
150
151   const unsigned char
152     *p;
153
154   GeometryInfo
155     geometry_info;
156
157   Image
158     *image;
159
160   int
161     c;
162
163   LinkedListInfo
164     *profiles;
165
166   MagickBooleanType
167     status;
168
169   MagickOffsetType
170     offset;
171
172   MagickStatusType
173     flags;
174
175   register ssize_t
176     i;
177
178   size_t
179     depth,
180     length,
181     quantum_depth;
182
183   ssize_t
184     count;
185
186   StringInfo
187     *profile;
188
189   /*
190     Open image file.
191   */
192   assert(image_info != (const ImageInfo *) NULL);
193   assert(image_info->signature == MagickSignature);
194   if (image_info->debug != MagickFalse)
195     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
196       image_info->filename);
197   assert(exception != (ExceptionInfo *) NULL);
198   assert(exception->signature == MagickSignature);
199   image=AcquireImage(image_info);
200   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
201   if (status == MagickFalse)
202     {
203       image=DestroyImageList(image);
204       return((Image *) NULL);
205     }
206   (void) CopyMagickString(cache_filename,image->filename,MaxTextExtent);
207   AppendImageFormat("cache",cache_filename);
208   c=ReadBlobByte(image);
209   if (c == EOF)
210     {
211       image=DestroyImage(image);
212       return((Image *) NULL);
213     }
214   *id='\0';
215   (void) ResetMagickMemory(keyword,0,sizeof(keyword));
216   offset=0;
217   do
218   {
219     /*
220       Decode image header;  header terminates one character beyond a ':'.
221     */
222     profiles=(LinkedListInfo *) NULL;
223     length=MaxTextExtent;
224     options=AcquireString((char *) NULL);
225     quantum_depth=MAGICKCORE_QUANTUM_DEPTH;
226     image->depth=8;
227     image->compression=NoCompression;
228     while ((isgraph(c) != MagickFalse) && (c != (int) ':'))
229     {
230       register char
231         *p;
232
233       if (c == (int) '{')
234         {
235           char
236             *comment;
237
238           /*
239             Read comment-- any text between { }.
240           */
241           length=MaxTextExtent;
242           comment=AcquireString((char *) NULL);
243           for (p=comment; comment != (char *) NULL; p++)
244           {
245             c=ReadBlobByte(image);
246             if ((c == EOF) || (c == (int) '}'))
247               break;
248             if ((size_t) (p-comment+1) >= length)
249               {
250                 *p='\0';
251                 length<<=1;
252                 comment=(char *) ResizeQuantumMemory(comment,length+
253                   MaxTextExtent,sizeof(*comment));
254                 if (comment == (char *) NULL)
255                   break;
256                 p=comment+strlen(comment);
257               }
258             *p=(char) c;
259           }
260           if (comment == (char *) NULL)
261             ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
262           *p='\0';
263           (void) SetImageProperty(image,"comment",comment);
264           comment=DestroyString(comment);
265           c=ReadBlobByte(image);
266         }
267       else
268         if (isalnum(c) != MagickFalse)
269           {
270             /*
271               Get the keyword.
272             */
273             p=keyword;
274             do
275             {
276               if (c == (int) '=')
277                 break;
278               if ((size_t) (p-keyword) < (MaxTextExtent-1))
279                 *p++=(char) c;
280               c=ReadBlobByte(image);
281             } while (c != EOF);
282             *p='\0';
283             p=options;
284             while (isspace((int) ((unsigned char) c)) != 0)
285               c=ReadBlobByte(image);
286             if (c == (int) '=')
287               {
288                 /*
289                   Get the keyword value.
290                 */
291                 c=ReadBlobByte(image);
292                 while ((c != (int) '}') && (c != EOF))
293                 {
294                   if ((size_t) (p-options+1) >= length)
295                     {
296                       *p='\0';
297                       length<<=1;
298                       options=(char *) ResizeQuantumMemory(options,length+
299                         MaxTextExtent,sizeof(*options));
300                       if (options == (char *) NULL)
301                         break;
302                       p=options+strlen(options);
303                     }
304                   if (options == (char *) NULL)
305                     ThrowReaderException(ResourceLimitError,
306                       "MemoryAllocationFailed");
307                   *p++=(char) c;
308                   c=ReadBlobByte(image);
309                   if (*options != '{')
310                     if (isspace((int) ((unsigned char) c)) != 0)
311                       break;
312                 }
313               }
314             *p='\0';
315             if (*options == '{')
316               (void) CopyMagickString(options,options+1,MaxTextExtent);
317             /*
318               Assign a value to the specified keyword.
319             */
320             switch (*keyword)
321             {
322               case 'b':
323               case 'B':
324               {
325                 if (LocaleCompare(keyword,"background-color") == 0)
326                   {
327                     (void) QueryColorDatabase(options,&image->background_color,
328                       exception);
329                     break;
330                   }
331                 if (LocaleCompare(keyword,"blue-primary") == 0)
332                   {
333                     flags=ParseGeometry(options,&geometry_info);
334                     image->chromaticity.blue_primary.x=geometry_info.rho;
335                     image->chromaticity.blue_primary.y=geometry_info.sigma;
336                     if ((flags & SigmaValue) == 0)
337                       image->chromaticity.blue_primary.y=
338                         image->chromaticity.blue_primary.x;
339                     break;
340                   }
341                 if (LocaleCompare(keyword,"border-color") == 0)
342                   {
343                     (void) QueryColorDatabase(options,&image->border_color,
344                       exception);
345                     break;
346                   }
347                 (void) SetImageProperty(image,keyword,options);
348                 break;
349               }
350               case 'c':
351               case 'C':
352               {
353                 if (LocaleCompare(keyword,"class") == 0)
354                   {
355                     ssize_t
356                       storage_class;
357
358                     storage_class=ParseCommandOption(MagickClassOptions,
359                       MagickFalse,options);
360                     if (storage_class < 0)
361                       break;
362                     image->storage_class=(ClassType) storage_class;
363                     break;
364                   }
365                 if (LocaleCompare(keyword,"colors") == 0)
366                   {
367                     image->colors=StringToUnsignedLong(options);
368                     break;
369                   }
370                 if (LocaleCompare(keyword,"colorspace") == 0)
371                   {
372                     ssize_t
373                       colorspace;
374
375                     colorspace=ParseCommandOption(MagickColorspaceOptions,
376                       MagickFalse,options);
377                     if (colorspace < 0)
378                       break;
379                     image->colorspace=(ColorspaceType) colorspace;
380                     break;
381                   }
382                 if (LocaleCompare(keyword,"compression") == 0)
383                   {
384                     ssize_t
385                       compression;
386
387                     compression=ParseCommandOption(MagickCompressOptions,
388                       MagickFalse,options);
389                     if (compression < 0)
390                       break;
391                     image->compression=(CompressionType) compression;
392                     break;
393                   }
394                 if (LocaleCompare(keyword,"columns") == 0)
395                   {
396                     image->columns=StringToUnsignedLong(options);
397                     break;
398                   }
399                 (void) SetImageProperty(image,keyword,options);
400                 break;
401               }
402               case 'd':
403               case 'D':
404               {
405                 if (LocaleCompare(keyword,"delay") == 0)
406                   {
407                     image->delay=StringToUnsignedLong(options);
408                     break;
409                   }
410                 if (LocaleCompare(keyword,"depth") == 0)
411                   {
412                     image->depth=StringToUnsignedLong(options);
413                     break;
414                   }
415                 if (LocaleCompare(keyword,"dispose") == 0)
416                   {
417                     ssize_t
418                       dispose;
419
420                     dispose=ParseCommandOption(MagickDisposeOptions,MagickFalse,
421                       options);
422                     if (dispose < 0)
423                       break;
424                     image->dispose=(DisposeType) dispose;
425                     break;
426                   }
427                 (void) SetImageProperty(image,keyword,options);
428                 break;
429               }
430               case 'e':
431               case 'E':
432               {
433                 if (LocaleCompare(keyword,"endian") == 0)
434                   {
435                     ssize_t
436                       endian;
437
438                     endian=ParseCommandOption(MagickEndianOptions,MagickFalse,
439                       options);
440                     if (endian < 0)
441                       break;
442                     image->endian=(EndianType) endian;
443                     break;
444                   }
445                 if (LocaleCompare(keyword,"error") == 0)
446                   {
447                     image->error.mean_error_per_pixel=StringToDouble(options,(char **) NULL);
448                     break;
449                   }
450                 (void) SetImageProperty(image,keyword,options);
451                 break;
452               }
453               case 'g':
454               case 'G':
455               {
456                 if (LocaleCompare(keyword,"gamma") == 0)
457                   {
458                     image->gamma=StringToDouble(options,(char **) NULL);
459                     break;
460                   }
461                 if (LocaleCompare(keyword,"green-primary") == 0)
462                   {
463                     flags=ParseGeometry(options,&geometry_info);
464                     image->chromaticity.green_primary.x=geometry_info.rho;
465                     image->chromaticity.green_primary.y=geometry_info.sigma;
466                     if ((flags & SigmaValue) == 0)
467                       image->chromaticity.green_primary.y=
468                         image->chromaticity.green_primary.x;
469                     break;
470                   }
471                 (void) SetImageProperty(image,keyword,options);
472                 break;
473               }
474               case 'i':
475               case 'I':
476               {
477                 if (LocaleCompare(keyword,"id") == 0)
478                   {
479                     (void) CopyMagickString(id,options,MaxTextExtent);
480                     break;
481                   }
482                 if (LocaleCompare(keyword,"iterations") == 0)
483                   {
484                     image->iterations=StringToUnsignedLong(options);
485                     break;
486                   }
487                 (void) SetImageProperty(image,keyword,options);
488                 break;
489               }
490               case 'm':
491               case 'M':
492               {
493                 if (LocaleCompare(keyword,"matte") == 0)
494                   {
495                     ssize_t
496                       matte;
497
498                     matte=ParseCommandOption(MagickBooleanOptions,MagickFalse,
499                       options);
500                     if (matte < 0)
501                       break;
502                     image->matte=(MagickBooleanType) matte;
503                     break;
504                   }
505                 if (LocaleCompare(keyword,"matte-color") == 0)
506                   {
507                     (void) QueryColorDatabase(options,&image->matte_color,
508                       exception);
509                     break;
510                   }
511                 if (LocaleCompare(keyword,"maximum-error") == 0)
512                   {
513                     image->error.normalized_maximum_error=
514                       StringToDouble(options,(char **) NULL);
515                     break;
516                   }
517                 if (LocaleCompare(keyword,"mean-error") == 0)
518                   {
519                     image->error.normalized_mean_error=StringToDouble(options,(char **) NULL);
520                     break;
521                   }
522                 if (LocaleCompare(keyword,"montage") == 0)
523                   {
524                     (void) CloneString(&image->montage,options);
525                     break;
526                   }
527                 (void) SetImageProperty(image,keyword,options);
528                 break;
529               }
530               case 'o':
531               case 'O':
532               {
533                 if (LocaleCompare(keyword,"opaque") == 0)
534                   {
535                     ssize_t
536                       matte;
537
538                     matte=ParseCommandOption(MagickBooleanOptions,MagickFalse,
539                       options);
540                     if (matte < 0)
541                       break;
542                     image->matte=(MagickBooleanType) matte;
543                     break;
544                   }
545                 if (LocaleCompare(keyword,"orientation") == 0)
546                   {
547                     ssize_t
548                       orientation;
549
550                     orientation=ParseCommandOption(MagickOrientationOptions,
551                       MagickFalse,options);
552                     if (orientation < 0)
553                       break;
554                     image->orientation=(OrientationType) orientation;
555                     break;
556                   }
557                 (void) SetImageProperty(image,keyword,options);
558                 break;
559               }
560               case 'p':
561               case 'P':
562               {
563                 if (LocaleCompare(keyword,"page") == 0)
564                   {
565                     char
566                       *geometry;
567
568                     geometry=GetPageGeometry(options);
569                     (void) ParseAbsoluteGeometry(geometry,&image->page);
570                     geometry=DestroyString(geometry);
571                     break;
572                   }
573                 if ((LocaleNCompare(keyword,"profile:",8) == 0) ||
574                     (LocaleNCompare(keyword,"profile-",8) == 0))
575                   {
576                     if (profiles == (LinkedListInfo *) NULL)
577                       profiles=NewLinkedList(0);
578                     (void) AppendValueToLinkedList(profiles,
579                       AcquireString(keyword+8));
580                     profile=AcquireStringInfo((size_t) StringToLong(options));
581                     (void) SetImageProfile(image,keyword+8,profile);
582                     profile=DestroyStringInfo(profile);
583                     break;
584                   }
585                 (void) SetImageProperty(image,keyword,options);
586                 break;
587               }
588               case 'q':
589               case 'Q':
590               {
591                 if (LocaleCompare(keyword,"quality") == 0)
592                   {
593                     image->quality=StringToUnsignedLong(options);
594                     break;
595                   }
596                 if (LocaleCompare(keyword,"quantum-depth") == 0)
597                   {
598                     quantum_depth=StringToUnsignedLong(options);
599                     break;
600                   }
601                 (void) SetImageProperty(image,keyword,options);
602                 break;
603               }
604               case 'r':
605               case 'R':
606               {
607                 if (LocaleCompare(keyword,"red-primary") == 0)
608                   {
609                     flags=ParseGeometry(options,&geometry_info);
610                     image->chromaticity.red_primary.x=geometry_info.rho;
611                     if ((flags & SigmaValue) != 0)
612                       image->chromaticity.red_primary.y=geometry_info.sigma;
613                     break;
614                   }
615                 if (LocaleCompare(keyword,"rendering-intent") == 0)
616                   {
617                     ssize_t
618                       rendering_intent;
619
620                     rendering_intent=ParseCommandOption(MagickIntentOptions,
621                       MagickFalse,options);
622                     if (rendering_intent < 0)
623                       break;
624                     image->rendering_intent=(RenderingIntent) rendering_intent;
625                     break;
626                   }
627                 if (LocaleCompare(keyword,"resolution") == 0)
628                   {
629                     flags=ParseGeometry(options,&geometry_info);
630                     image->x_resolution=geometry_info.rho;
631                     image->y_resolution=geometry_info.sigma;
632                     if ((flags & SigmaValue) == 0)
633                       image->y_resolution=image->x_resolution;
634                     break;
635                   }
636                 if (LocaleCompare(keyword,"rows") == 0)
637                   {
638                     image->rows=StringToUnsignedLong(options);
639                     break;
640                   }
641                 (void) SetImageProperty(image,keyword,options);
642                 break;
643               }
644               case 's':
645               case 'S':
646               {
647                 if (LocaleCompare(keyword,"scene") == 0)
648                   {
649                     image->scene=StringToUnsignedLong(options);
650                     break;
651                   }
652                 (void) SetImageProperty(image,keyword,options);
653                 break;
654               }
655               case 't':
656               case 'T':
657               {
658                 if (LocaleCompare(keyword,"ticks-per-second") == 0)
659                   {
660                     image->ticks_per_second=(ssize_t) StringToLong(options);
661                     break;
662                   }
663                 if (LocaleCompare(keyword,"tile-offset") == 0)
664                   {
665                     char
666                       *geometry;
667
668                     geometry=GetPageGeometry(options);
669                     (void) ParseAbsoluteGeometry(geometry,&image->tile_offset);
670                     geometry=DestroyString(geometry);
671                   }
672                 if (LocaleCompare(keyword,"type") == 0)
673                   {
674                     ssize_t
675                       type;
676
677                     type=ParseCommandOption(MagickTypeOptions,MagickFalse,
678                       options);
679                     if (type < 0)
680                       break;
681                     image->type=(ImageType) type;
682                     break;
683                   }
684                 (void) SetImageProperty(image,keyword,options);
685                 break;
686               }
687               case 'u':
688               case 'U':
689               {
690                 if (LocaleCompare(keyword,"units") == 0)
691                   {
692                     ssize_t
693                       units;
694
695                     units=ParseCommandOption(MagickResolutionOptions,MagickFalse,
696                       options);
697                     if (units < 0)
698                       break;
699                     image->units=(ResolutionType) units;
700                     break;
701                   }
702                 (void) SetImageProperty(image,keyword,options);
703                 break;
704               }
705               case 'w':
706               case 'W':
707               {
708                 if (LocaleCompare(keyword,"white-point") == 0)
709                   {
710                     flags=ParseGeometry(options,&geometry_info);
711                     image->chromaticity.white_point.x=geometry_info.rho;
712                     image->chromaticity.white_point.y=geometry_info.sigma;
713                     if ((flags & SigmaValue) == 0)
714                       image->chromaticity.white_point.y=
715                         image->chromaticity.white_point.x;
716                     break;
717                   }
718                 (void) SetImageProperty(image,keyword,options);
719                 break;
720               }
721               default:
722               {
723                 (void) SetImageProperty(image,keyword,options);
724                 break;
725               }
726             }
727           }
728         else
729           c=ReadBlobByte(image);
730       while (isspace((int) ((unsigned char) c)) != 0)
731         c=ReadBlobByte(image);
732     }
733     options=DestroyString(options);
734     (void) ReadBlobByte(image);
735     /*
736       Verify that required image information is defined.
737     */
738     if ((LocaleCompare(id,"MagickCache") != 0) ||
739         (image->storage_class == UndefinedClass) ||
740         (image->compression == UndefinedCompression) || (image->columns == 0) ||
741         (image->rows == 0))
742       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
743     if (quantum_depth != MAGICKCORE_QUANTUM_DEPTH)
744       ThrowReaderException(CacheError,"InconsistentPersistentCacheDepth");
745     if (image->montage != (char *) NULL)
746       {
747         register char
748           *p;
749
750         /*
751           Image directory.
752         */
753         length=MaxTextExtent;
754         image->directory=AcquireString((char *) NULL);
755         p=image->directory;
756         do
757         {
758           *p='\0';
759           if ((strlen(image->directory)+MaxTextExtent) >= length)
760             {
761               /*
762                 Allocate more memory for the image directory.
763               */
764               length<<=1;
765               image->directory=(char *) ResizeQuantumMemory(image->directory,
766                 length+MaxTextExtent,sizeof(*image->directory));
767               if (image->directory == (char *) NULL)
768                 ThrowReaderException(CorruptImageError,"UnableToReadImageData");
769               p=image->directory+strlen(image->directory);
770             }
771           c=ReadBlobByte(image);
772           *p++=(char) c;
773         } while (c != (int) '\0');
774       }
775     if (profiles != (LinkedListInfo *) NULL)
776       {
777         const char
778           *name;
779
780         const StringInfo
781           *profile;
782
783         register unsigned char
784           *p;
785
786         /*
787           Read image profiles.
788         */
789         ResetLinkedListIterator(profiles);
790         name=(const char *) GetNextValueInLinkedList(profiles);
791         while (name != (const char *) NULL)
792         {
793           profile=GetImageProfile(image,name);
794           if (profile != (StringInfo *) NULL)
795             {
796               p=GetStringInfoDatum(profile);
797               count=ReadBlob(image,GetStringInfoLength(profile),p);
798             }
799           name=(const char *) GetNextValueInLinkedList(profiles);
800         }
801         profiles=DestroyLinkedList(profiles,RelinquishMagickMemory);
802       }
803     depth=GetImageQuantumDepth(image,MagickFalse);
804     if (image->storage_class == PseudoClass)
805       {
806         /*
807           Create image colormap.
808         */
809         if (AcquireImageColormap(image,image->colors) == MagickFalse)
810           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
811         if (image->colors != 0)
812           {
813             size_t
814               packet_size;
815
816             unsigned char
817               *colormap;
818
819             /*
820               Read image colormap from file.
821             */
822             packet_size=(size_t) (3UL*depth/8UL);
823             colormap=(unsigned char *) AcquireQuantumMemory(image->colors,
824               packet_size*sizeof(*colormap));
825             if (colormap == (unsigned char *) NULL)
826               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
827             count=ReadBlob(image,packet_size*image->colors,colormap);
828             if (count != (ssize_t) (packet_size*image->colors))
829               ThrowReaderException(CorruptImageError,
830                 "InsufficientImageDataInFile");
831             p=colormap;
832             switch (depth)
833             {
834               default:
835                 ThrowReaderException(CorruptImageError,
836                   "ImageDepthNotSupported");
837               case 8:
838               {
839                 unsigned char
840                   pixel;
841
842                 for (i=0; i < (ssize_t) image->colors; i++)
843                 {
844                   p=PushCharPixel(p,&pixel);
845                   image->colormap[i].red=ScaleCharToQuantum(pixel);
846                   p=PushCharPixel(p,&pixel);
847                   image->colormap[i].green=ScaleCharToQuantum(pixel);
848                   p=PushCharPixel(p,&pixel);
849                   image->colormap[i].blue=ScaleCharToQuantum(pixel);
850                 }
851                 break;
852               }
853               case 16:
854               {
855                 unsigned short
856                   pixel;
857
858                 for (i=0; i < (ssize_t) image->colors; i++)
859                 {
860                   p=PushShortPixel(MSBEndian,p,&pixel);
861                   image->colormap[i].red=ScaleShortToQuantum(pixel);
862                   p=PushShortPixel(MSBEndian,p,&pixel);
863                   image->colormap[i].green=ScaleShortToQuantum(pixel);
864                   p=PushShortPixel(MSBEndian,p,&pixel);
865                   image->colormap[i].blue=ScaleShortToQuantum(pixel);
866                 }
867                 break;
868               }
869               case 32:
870               {
871                 unsigned int
872                   pixel;
873
874                 for (i=0; i < (ssize_t) image->colors; i++)
875                 {
876                   p=PushLongPixel(MSBEndian,p,&pixel);
877                   image->colormap[i].red=ScaleLongToQuantum(pixel);
878                   p=PushLongPixel(MSBEndian,p,&pixel);
879                   image->colormap[i].green=ScaleLongToQuantum(pixel);
880                   p=PushLongPixel(MSBEndian,p,&pixel);
881                   image->colormap[i].blue=ScaleLongToQuantum(pixel);
882                 }
883                 break;
884               }
885             }
886             colormap=(unsigned char *) RelinquishMagickMemory(colormap);
887           }
888       }
889     if (EOFBlob(image) != MagickFalse)
890       {
891         ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
892           image->filename);
893         break;
894       }
895     if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
896       if (image->scene >= (image_info->scene+image_info->number_scenes-1))
897         break;
898     /*
899       Attach persistent pixel cache.
900     */
901     status=PersistPixelCache(image,cache_filename,MagickTrue,&offset,exception);
902     if (status == MagickFalse)
903       ThrowReaderException(CacheError,"UnableToPersistPixelCache");
904     /*
905       Proceed to next image.
906     */
907     do
908     {
909       c=ReadBlobByte(image);
910     } while ((isgraph(c) == MagickFalse) && (c != EOF));
911     if (c != EOF)
912       {
913         /*
914           Allocate next image structure.
915         */
916         AcquireNextImage(image_info,image);
917         if (GetNextImageInList(image) == (Image *) NULL)
918           {
919             image=DestroyImageList(image);
920             return((Image *) NULL);
921           }
922         image=SyncNextImageInList(image);
923         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
924           GetBlobSize(image));
925         if (status == MagickFalse)
926           break;
927       }
928   } while (c != EOF);
929   (void) CloseBlob(image);
930   return(GetFirstImageInList(image));
931 }
932 \f
933 /*
934 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
935 %                                                                             %
936 %                                                                             %
937 %                                                                             %
938 %   R e g i s t e r M P C I m a g e                                           %
939 %                                                                             %
940 %                                                                             %
941 %                                                                             %
942 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
943 %
944 %  RegisterMPCImage() adds properties for the Cache image format to
945 %  the list of supported formats.  The properties include the image format
946 %  tag, a method to read and/or write the format, whether the format
947 %  supports the saving of more than one frame to the same file or blob,
948 %  whether the format supports native in-memory I/O, and a brief
949 %  description of the format.
950 %
951 %  The format of the RegisterMPCImage method is:
952 %
953 %      size_t RegisterMPCImage(void)
954 %
955 */
956 ModuleExport size_t RegisterMPCImage(void)
957 {
958   MagickInfo
959     *entry;
960
961   entry=SetMagickInfo("CACHE");
962   entry->description=ConstantString("Magick Persistent Cache image format");
963   entry->module=ConstantString("CACHE");
964   entry->stealth=MagickTrue;
965   (void) RegisterMagickInfo(entry);
966   entry=SetMagickInfo("MPC");
967   entry->decoder=(DecodeImageHandler *) ReadMPCImage;
968   entry->encoder=(EncodeImageHandler *) WriteMPCImage;
969   entry->magick=(IsImageFormatHandler *) IsMPC;
970   entry->description=ConstantString("Magick Persistent Cache image format");
971   entry->module=ConstantString("MPC");
972   (void) RegisterMagickInfo(entry);
973   return(MagickImageCoderSignature);
974 }
975 \f
976 /*
977 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
978 %                                                                             %
979 %                                                                             %
980 %                                                                             %
981 %   U n r e g i s t e r M P C I m a g e                                       %
982 %                                                                             %
983 %                                                                             %
984 %                                                                             %
985 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
986 %
987 %  UnregisterMPCImage() removes format registrations made by the
988 %  MPC module from the list of supported formats.
989 %
990 %  The format of the UnregisterMPCImage method is:
991 %
992 %      UnregisterMPCImage(void)
993 %
994 */
995 ModuleExport void UnregisterMPCImage(void)
996 {
997   (void) UnregisterMagickInfo("CACHE");
998   (void) UnregisterMagickInfo("MPC");
999 }
1000 \f
1001 /*
1002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1003 %                                                                             %
1004 %                                                                             %
1005 %                                                                             %
1006 %   W r i t e M P C I m a g e                                                 %
1007 %                                                                             %
1008 %                                                                             %
1009 %                                                                             %
1010 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1011 %
1012 %  WriteMPCImage() writes an Magick Persistent Cache image to a file.
1013 %
1014 %  The format of the WriteMPCImage method is:
1015 %
1016 %      MagickBooleanType WriteMPCImage(const ImageInfo *image_info,Image *image)
1017 %
1018 %  A description of each parameter follows:
1019 %
1020 %    o image_info: the image info.
1021 %
1022 %    o image: the image.
1023 %
1024 */
1025 static MagickBooleanType WriteMPCImage(const ImageInfo *image_info,Image *image)
1026 {
1027   char
1028     buffer[MaxTextExtent],
1029     cache_filename[MaxTextExtent];
1030
1031   const char
1032     *property,
1033     *value;
1034
1035   MagickBooleanType
1036     status;
1037
1038   MagickOffsetType
1039     offset,
1040     scene;
1041
1042   register ssize_t
1043     i;
1044
1045   size_t
1046     depth,
1047     one;
1048
1049   /*
1050     Open persistent cache.
1051   */
1052   assert(image_info != (const ImageInfo *) NULL);
1053   assert(image_info->signature == MagickSignature);
1054   assert(image != (Image *) NULL);
1055   assert(image->signature == MagickSignature);
1056   if (image->debug != MagickFalse)
1057     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1058   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
1059   if (status == MagickFalse)
1060     return(status);
1061   (void) CopyMagickString(cache_filename,image->filename,MaxTextExtent);
1062   AppendImageFormat("cache",cache_filename);
1063   scene=0;
1064   offset=0;
1065   one=1;
1066   do
1067   {
1068     /*
1069       Write persistent cache meta-information.
1070     */
1071     depth=GetImageQuantumDepth(image,MagickTrue);
1072     if ((image->storage_class == PseudoClass) &&
1073         (image->colors > (one << depth)))
1074       image->storage_class=DirectClass;
1075     (void) WriteBlobString(image,"id=MagickCache\n");
1076     (void) FormatMagickString(buffer,MaxTextExtent,"quantum-depth=%d\n",
1077       MAGICKCORE_QUANTUM_DEPTH);
1078     (void) WriteBlobString(image,buffer);
1079     (void) FormatMagickString(buffer,MaxTextExtent,
1080       "class=%s  colors=%.20g  matte=%s\n",CommandOptionToMnemonic(
1081       MagickClassOptions,image->storage_class),(double) image->colors,
1082       CommandOptionToMnemonic(MagickBooleanOptions,(ssize_t) image->matte));
1083     (void) WriteBlobString(image,buffer);
1084     (void) FormatMagickString(buffer,MaxTextExtent,
1085       "columns=%.20g  rows=%.20g depth=%.20g\n",(double) image->columns,
1086       (double) image->rows,(double) image->depth);
1087     (void) WriteBlobString(image,buffer);
1088     if (image->type != UndefinedType)
1089       {
1090         (void) FormatMagickString(buffer,MaxTextExtent,"type=%s\n",
1091           CommandOptionToMnemonic(MagickTypeOptions,image->type));
1092         (void) WriteBlobString(image,buffer);
1093       }
1094     if (image->colorspace != UndefinedColorspace)
1095       {
1096         (void) FormatMagickString(buffer,MaxTextExtent,"colorspace=%s\n",
1097           CommandOptionToMnemonic(MagickColorspaceOptions,image->colorspace));
1098         (void) WriteBlobString(image,buffer);
1099       }
1100     if (image->endian != UndefinedEndian)
1101       {
1102         (void) FormatMagickString(buffer,MaxTextExtent,"endian=%s\n",
1103           CommandOptionToMnemonic(MagickEndianOptions,image->endian));
1104         (void) WriteBlobString(image,buffer);
1105       }
1106     if (image->compression != UndefinedCompression)
1107       {
1108         (void) FormatMagickString(buffer,MaxTextExtent,
1109           "compression=%s  quality=%.20g\n",CommandOptionToMnemonic(
1110           MagickCompressOptions,image->compression),(double) image->quality);
1111         (void) WriteBlobString(image,buffer);
1112       }
1113     if (image->units != UndefinedResolution)
1114       {
1115         (void) FormatMagickString(buffer,MaxTextExtent,"units=%s\n",
1116           CommandOptionToMnemonic(MagickResolutionOptions,image->units));
1117         (void) WriteBlobString(image,buffer);
1118       }
1119     if ((image->x_resolution != 0) || (image->y_resolution != 0))
1120       {
1121         (void) FormatMagickString(buffer,MaxTextExtent,
1122           "resolution=%gx%g\n",image->x_resolution,image->y_resolution);
1123         (void) WriteBlobString(image,buffer);
1124       }
1125     if ((image->page.width != 0) || (image->page.height != 0))
1126       {
1127         (void) FormatMagickString(buffer,MaxTextExtent,
1128           "page=%.20gx%.20g%+.20g%+.20g\n",(double) image->page.width,(double)
1129           image->page.height,(double) image->page.x,(double) image->page.y);
1130         (void) WriteBlobString(image,buffer);
1131       }
1132     else
1133       if ((image->page.x != 0) || (image->page.y != 0))
1134         {
1135           (void) FormatMagickString(buffer,MaxTextExtent,"page=%+ld%+ld\n",
1136             (long) image->page.x,(long) image->page.y);
1137           (void) WriteBlobString(image,buffer);
1138         }
1139     if ((image->page.x != 0) || (image->page.y != 0))
1140       {
1141         (void) FormatMagickString(buffer,MaxTextExtent,"tile-offset=%+ld%+ld\n",
1142           (long) image->tile_offset.x,(long) image->tile_offset.y);
1143         (void) WriteBlobString(image,buffer);
1144       }
1145     if ((GetNextImageInList(image) != (Image *) NULL) ||
1146         (GetPreviousImageInList(image) != (Image *) NULL))
1147       {
1148         if (image->scene == 0)
1149           (void) FormatMagickString(buffer,MaxTextExtent,
1150             "iterations=%.20g  delay=%.20g  ticks-per-second=%.20g\n",(double)
1151             image->iterations,(double) image->delay,(double)
1152             image->ticks_per_second);
1153         else
1154           (void) FormatMagickString(buffer,MaxTextExtent,"scene=%.20g  "
1155             "iterations=%.20g  delay=%.20g  ticks-per-second=%.20g\n",
1156             (double) image->scene,(double) image->iterations,(double)
1157             image->delay,(double) image->ticks_per_second);
1158         (void) WriteBlobString(image,buffer);
1159       }
1160     else
1161       {
1162         if (image->scene != 0)
1163           {
1164             (void) FormatMagickString(buffer,MaxTextExtent,"scene=%.20g\n",
1165               (double) image->scene);
1166             (void) WriteBlobString(image,buffer);
1167           }
1168         if (image->iterations != 0)
1169           {
1170             (void) FormatMagickString(buffer,MaxTextExtent,"iterations=%.20g\n",
1171               (double) image->iterations);
1172             (void) WriteBlobString(image,buffer);
1173           }
1174         if (image->delay != 0)
1175           {
1176             (void) FormatMagickString(buffer,MaxTextExtent,"delay=%.20g\n",
1177               (double) image->delay);
1178             (void) WriteBlobString(image,buffer);
1179           }
1180         if (image->ticks_per_second != UndefinedTicksPerSecond)
1181           {
1182             (void) FormatMagickString(buffer,MaxTextExtent,
1183               "ticks-per-second=%.20g\n",(double) image->ticks_per_second);
1184             (void) WriteBlobString(image,buffer);
1185           }
1186       }
1187     if (image->gravity != UndefinedGravity)
1188       {
1189         (void) FormatMagickString(buffer,MaxTextExtent,"gravity=%s\n",
1190           CommandOptionToMnemonic(MagickGravityOptions,image->gravity));
1191         (void) WriteBlobString(image,buffer);
1192       }
1193     if (image->dispose != UndefinedDispose)
1194       {
1195         (void) FormatMagickString(buffer,MaxTextExtent,"dispose=%s\n",
1196           CommandOptionToMnemonic(MagickDisposeOptions,image->dispose));
1197         (void) WriteBlobString(image,buffer);
1198       }
1199     if (image->rendering_intent != UndefinedIntent)
1200       {
1201         (void) FormatMagickString(buffer,MaxTextExtent,
1202           "rendering-intent=%s\n",CommandOptionToMnemonic(MagickIntentOptions,
1203           image->rendering_intent));
1204         (void) WriteBlobString(image,buffer);
1205       }
1206     if (image->gamma != 0.0)
1207       {
1208         (void) FormatMagickString(buffer,MaxTextExtent,"gamma=%g\n",
1209           image->gamma);
1210         (void) WriteBlobString(image,buffer);
1211       }
1212     if (image->chromaticity.white_point.x != 0.0)
1213       {
1214         /*
1215           Note chomaticity points.
1216         */
1217         (void) FormatMagickString(buffer,MaxTextExtent,"red-primary="
1218           "%g,%g  green-primary=%g,%g  blue-primary=%g,%g\n",
1219           image->chromaticity.red_primary.x,image->chromaticity.red_primary.y,
1220           image->chromaticity.green_primary.x,
1221           image->chromaticity.green_primary.y,
1222           image->chromaticity.blue_primary.x,
1223           image->chromaticity.blue_primary.y);
1224         (void) WriteBlobString(image,buffer);
1225         (void) FormatMagickString(buffer,MaxTextExtent,
1226           "white-point=%g,%g\n",image->chromaticity.white_point.x,
1227           image->chromaticity.white_point.y);
1228         (void) WriteBlobString(image,buffer);
1229       }
1230     if (image->orientation != UndefinedOrientation)
1231       {
1232         (void) FormatMagickString(buffer,MaxTextExtent,
1233           "orientation=%s\n",CommandOptionToMnemonic(MagickOrientationOptions,
1234           image->orientation));
1235         (void) WriteBlobString(image,buffer);
1236       }
1237     if (image->profiles != (void *) NULL)
1238       {
1239         const char
1240           *name;
1241
1242         const StringInfo
1243           *profile;
1244
1245         /*
1246           Generic profile.
1247         */
1248         ResetImageProfileIterator(image);
1249         for (name=GetNextImageProfile(image); name != (const char *) NULL; )
1250         {
1251           profile=GetImageProfile(image,name);
1252           if (profile != (StringInfo *) NULL)
1253             {
1254               (void) FormatMagickString(buffer,MaxTextExtent,
1255                 "profile:%s=%.20g\n",name,(double)
1256                 GetStringInfoLength(profile));
1257               (void) WriteBlobString(image,buffer);
1258             }
1259           name=GetNextImageProfile(image);
1260         }
1261       }
1262     if (image->montage != (char *) NULL)
1263       {
1264         (void) FormatMagickString(buffer,MaxTextExtent,"montage=%s\n",
1265           image->montage);
1266         (void) WriteBlobString(image,buffer);
1267       }
1268     ResetImagePropertyIterator(image);
1269     property=GetNextImageProperty(image);
1270     while (property != (const char *) NULL)
1271     {
1272       (void) FormatMagickString(buffer,MaxTextExtent,"%s=",property);
1273       (void) WriteBlobString(image,buffer);
1274       value=GetImageProperty(image,property);
1275       if (value != (const char *) NULL)
1276         {
1277           for (i=0; i < (ssize_t) strlen(value); i++)
1278             if (isspace((int) ((unsigned char) value[i])) != 0)
1279               break;
1280           if (i <= (ssize_t) strlen(value))
1281             (void) WriteBlobByte(image,'{');
1282           (void) WriteBlob(image,strlen(value),(unsigned char *) value);
1283           if (i <= (ssize_t) strlen(value))
1284             (void) WriteBlobByte(image,'}');
1285         }
1286       (void) WriteBlobByte(image,'\n');
1287       property=GetNextImageProperty(image);
1288     }
1289     ResetImageArtifactIterator(image);
1290     (void) WriteBlobString(image,"\f\n:\032");
1291     if (image->montage != (char *) NULL)
1292       {
1293         /*
1294           Write montage tile directory.
1295         */
1296         if (image->directory != (char *) NULL)
1297           (void) WriteBlobString(image,image->directory);
1298         (void) WriteBlobByte(image,'\0');
1299       }
1300     if (image->profiles != 0)
1301       {
1302         const char
1303           *name;
1304
1305         const StringInfo
1306           *profile;
1307
1308         /*
1309           Write image profiles.
1310         */
1311         ResetImageProfileIterator(image);
1312         name=GetNextImageProfile(image);
1313         while (name != (const char *) NULL)
1314         {
1315           profile=GetImageProfile(image,name);
1316           (void) WriteBlob(image,GetStringInfoLength(profile),
1317             GetStringInfoDatum(profile));
1318           name=GetNextImageProfile(image);
1319         }
1320       }
1321     if (image->storage_class == PseudoClass)
1322       {
1323         size_t
1324           packet_size;
1325
1326         unsigned char
1327           *colormap,
1328           *q;
1329
1330         /*
1331           Allocate colormap.
1332         */
1333         packet_size=(size_t) (3UL*depth/8UL);
1334         colormap=(unsigned char *) AcquireQuantumMemory(image->colors,
1335           packet_size*sizeof(*colormap));
1336         if (colormap == (unsigned char *) NULL)
1337           return(MagickFalse);
1338         /*
1339           Write colormap to file.
1340         */
1341         q=colormap;
1342         for (i=0; i < (ssize_t) image->colors; i++)
1343         {
1344           switch (depth)
1345           {
1346             default:
1347               ThrowWriterException(CorruptImageError,"ImageDepthNotSupported");
1348             case 32:
1349             {
1350               unsigned int
1351                 pixel;
1352
1353               pixel=ScaleQuantumToLong(image->colormap[i].red);
1354               q=PopLongPixel(MSBEndian,pixel,q);
1355               pixel=ScaleQuantumToLong(image->colormap[i].green);
1356               q=PopLongPixel(MSBEndian,pixel,q);
1357               pixel=ScaleQuantumToLong(image->colormap[i].blue);
1358               q=PopLongPixel(MSBEndian,pixel,q);
1359             }
1360             case 16:
1361             {
1362               unsigned short
1363                 pixel;
1364
1365               pixel=ScaleQuantumToShort(image->colormap[i].red);
1366               q=PopShortPixel(MSBEndian,pixel,q);
1367               pixel=ScaleQuantumToShort(image->colormap[i].green);
1368               q=PopShortPixel(MSBEndian,pixel,q);
1369               pixel=ScaleQuantumToShort(image->colormap[i].blue);
1370               q=PopShortPixel(MSBEndian,pixel,q);
1371               break;
1372             }
1373             case 8:
1374             {
1375               unsigned char
1376                 pixel;
1377
1378               pixel=(unsigned char) ScaleQuantumToChar(image->colormap[i].red);
1379               q=PopCharPixel(pixel,q);
1380               pixel=(unsigned char) ScaleQuantumToChar(
1381                 image->colormap[i].green);
1382               q=PopCharPixel(pixel,q);
1383               pixel=(unsigned char) ScaleQuantumToChar(image->colormap[i].blue);
1384               q=PopCharPixel(pixel,q);
1385               break;
1386             }
1387           }
1388         }
1389         (void) WriteBlob(image,packet_size*image->colors,colormap);
1390         colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1391       }
1392     /*
1393       Initialize persistent pixel cache.
1394     */
1395     status=PersistPixelCache(image,cache_filename,MagickFalse,&offset,
1396       &image->exception);
1397     if (status == MagickFalse)
1398       ThrowWriterException(CacheError,"UnableToPersistPixelCache");
1399     if (GetNextImageInList(image) == (Image *) NULL)
1400       break;
1401     image=SyncNextImageInList(image);
1402     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1403       {
1404         status=image->progress_monitor(SaveImagesTag,scene,
1405           GetImageListLength(image),image->client_data);
1406         if (status == MagickFalse)
1407           break;
1408       }
1409     scene++;
1410   } while (image_info->adjoin != MagickFalse);
1411   (void) CloseBlob(image);
1412   return(status);
1413 }