ext/RMagick/rmutil.c in rmagick-1.10.1 vs ext/RMagick/rmutil.c in rmagick-1.11.0

- old
+ new

@@ -1,22 +1,23 @@ -/* $Id: rmutil.c,v 1.66 2006/01/20 23:59:46 rmagick Exp $ */ +/* $Id: rmutil.c,v 1.75 2006/05/07 23:40:14 rmagick Exp $ */ /*============================================================================\ | Copyright (C) 2006 by Timothy P. Hunter | Name: rmutil.c | Author: Tim Hunter | Purpose: Utility functions for RMagick \============================================================================*/ #include "rmagick.h" #include <errno.h> -static const char *Compliance_name(ComplianceType *); +static const char *ComplianceType_name(ComplianceType *); static const char *StyleType_name(StyleType); static const char *StretchType_name(StretchType); static void Color_Name_to_PixelPacket(PixelPacket *, VALUE); static VALUE Enum_type_values(VALUE); static VALUE Enum_type_inspect(VALUE); +static void handle_exception(ExceptionInfo *, Image *, ErrorRetention); /* Extern: magick_malloc, magick_free, magick_realloc Purpose: ****Magick versions of standard memory routines. Notes: use when managing memory that ****Magick may have @@ -275,10 +276,75 @@ return pct; } /* + Static: check_num2dbl + Purpose: return 0 if rb_num2dbl doesn't raise an exception + */ +static VALUE +check_num2dbl(VALUE obj) +{ + rb_num2dbl(obj); + return INT2FIX(1); +} + + +/* + Static: rescue_not_dbl + Purpose: called if rb_num2dbl raises an exception + */ +static VALUE +rescue_not_dbl(VALUE ignored) +{ + return INT2FIX(0); +} + + +/* + Extern: rm_check_num2dbl + Purpose: Return 1 if the object can be converted to a double, 0 otherwise. +*/ +int rm_check_num2dbl(VALUE obj) +{ + return FIX2INT(rb_rescue(check_num2dbl, obj, rescue_not_dbl, (VALUE)0)); +} + + +/* + * Extern: rm_str_to_pct + * Purpose: Given a string in the form NN% return the corresponding double. + * +*/ +double rm_str_to_pct(VALUE str) +{ + long pct; + char *pct_str, *end; + + str = rb_rescue(rb_str_to_str, str, rescue_not_str, str); + pct_str = STRING_PTR(str); + errno = 0; + pct = strtol(pct_str, &end, 10); + + if (errno == ERANGE) + { + rb_raise(rb_eRangeError, "`%s' out of range", pct_str); + } + if (*end != '%') + { + rb_raise(rb_eArgError, "expected percentage, got `%s'", pct_str); + } + if (pct < 0L) + { + rb_raise(rb_eArgError, "percentages may not be negative (got `%s')", pct_str); + } + + return pct / 100.0; +} + + +/* * Extern: rm_fuzz_to_dbl(obj) * Purpose: If the argument is a number, convert it to a double. * Otherwise it's supposed to be a string in the form 'NN%'. * Return a percentage of MaxRGB. * Notes: Called from Image#fuzz= and Info#fuzz= @@ -415,11 +481,11 @@ class = class; // defeat "never referenced" message from icc GetExceptionInfo(&exception); okay = QueryColorDatabase(STRING_PTR(name), &pp, &exception); - HANDLE_ERROR + CHECK_EXCEPTION() if (!okay) { rb_raise(rb_eArgError, "invalid color name: %s", STRING_PTR(name)); } @@ -484,11 +550,11 @@ image->matte = matte; DestroyImageInfo(info); GetExceptionInfo(&exception); (void) QueryColorname(image, pixel, compliance, name, &exception); DestroyImage(image); - HANDLE_ERROR + CHECK_EXCEPTION() // Always return a string, even if it's "" return rb_str_new2(name); } @@ -910,11 +976,11 @@ ExceptionInfo exception; GetExceptionInfo(&exception); (void) QueryColorname(image, color, X11Compliance, name, &exception); - HANDLE_ERROR_IMG(image) + CHECK_EXCEPTION() return rb_str_new2(name); } /* @@ -1150,11 +1216,11 @@ { const char *name; // Turn off undefined bits compliance &= (SVGCompliance|X11Compliance|XPMCompliance); - name = Compliance_name(&compliance); + name = ComplianceType_name(&compliance); return rm_enum_new(Class_ComplianceType, ID2SYM(rb_intern(name)), INT2FIX(compliance)); } /* @@ -1288,11 +1354,11 @@ /* Static: DisposeType_name Purpose: Return the name of a DisposeType enum as a string */ static const char * -DisposeType_name(type) +DisposeType_name(DisposeType type) { switch(type) { default: ENUM_TO_NAME(UndefinedDispose) @@ -1365,11 +1431,11 @@ /* Static: EndianType_name Purpose: Return the name of a EndianType enum as a string */ static const char * -EndianType_name(type) +EndianType_name(EndianType type) { switch(type) { default: ENUM_TO_NAME(UndefinedEndian) @@ -1396,11 +1462,11 @@ /* Static: ImageType_name Purpose: Return the name of a ImageType enum as a string */ static char * -ImageType_name(type) +ImageType_name(ImageType type) { switch(type) { default: ENUM_TO_NAME(UndefinedType) ENUM_TO_NAME(BilevelType) @@ -1435,11 +1501,11 @@ /* Static: InterlaceType_name Purpose: Return the name of a InterlaceType enum as a string */ static const char * -InterlaceType_name(interlace) +InterlaceType_name(InterlaceType interlace) { switch(interlace) { default: ENUM_TO_NAME(UndefinedInterlace) @@ -1463,17 +1529,51 @@ name = InterlaceType_name(interlace); return rm_enum_new(Class_InterlaceType, ID2SYM(rb_intern(name)), INT2FIX(interlace)); } +/* + External: MagickLayerMethod_new + Purpose: Construct an MagickLayerMethod enum object for the specified value. +*/ +#if defined(HAVE_COMPAREIMAGELAYERS) +static const char * +MagickLayerMethod_name(MagickLayerMethod method) +{ + switch(method) + { + default: + ENUM_TO_NAME(UndefinedLayer) + ENUM_TO_NAME(CompareAnyLayer) + ENUM_TO_NAME(CompareClearLayer) + ENUM_TO_NAME(CompareOverlayLayer) + ENUM_TO_NAME(OptimizeLayer) + ENUM_TO_NAME(OptimizePlusLayer) +#if defined(HAVE_COALESCELAYER) + ENUM_TO_NAME(CoalesceLayer) + ENUM_TO_NAME(DisposeLayer) +#endif + } +} +VALUE +MagickLayerMethod_new(MagickLayerMethod method) +{ + const char *name; + + name = MagickLayerMethod_name(method); + return rm_enum_new(Class_MagickLayerMethod, ID2SYM(rb_intern(name)), INT2FIX(method)); +} +#endif + + /* Static: RenderingIntent_name Purpose: Return the name of a RenderingIntent enum as a string */ static const char * -RenderingIntent_name(intent) +RenderingIntent_name(RenderingIntent intent) { switch(intent) { default: ENUM_TO_NAME(UndefinedIntent) @@ -1504,11 +1604,11 @@ /* Static: ResolutionType_name Purpose: Return the name of a ResolutionType enum as a string */ static const char * -ResolutionType_name(type) +ResolutionType_name(ResolutionType type) { switch(type) { default: ENUM_TO_NAME(UndefinedResolution) @@ -1538,11 +1638,11 @@ /* Static: OrientationType_name Purpose: Return the name of a OrientationType enum as a string */ static const char * -OrientationType_name(type) +OrientationType_name(OrientationType type) { switch(type) { default: ENUM_TO_NAME(UndefinedOrientation) @@ -1657,11 +1757,11 @@ Color_to_ColorInfo(&ci, self); sprintf(buff, "name=%s, compliance=%s, " "color.red=%d, color.green=%d, color.blue=%d, color.opacity=%d ", ci.name, - Compliance_name(&ci.compliance), + ComplianceType_name(&ci.compliance), ci.color.red, ci.color.green, ci.color.blue, ci.color.opacity); destroy_ColorInfo(&ci); return rb_str_new2(buff); } @@ -1991,18 +2091,18 @@ volatile VALUE name, description, family; volatile VALUE style, stretch, weight; volatile VALUE encoding, foundry, format; name = rb_str_new2(ti->name); - description = rb_str_new2(ti->description); family = rb_str_new2(ti->family); style = StyleType_new(ti->style); stretch = StretchType_new(ti->stretch); weight = INT2NUM(ti->weight); - encoding = ti->encoding ? rb_str_new2(ti->encoding) : Qnil; - foundry = ti->foundry ? rb_str_new2(ti->foundry) : Qnil; - format = ti->format ? rb_str_new2(ti->format) : Qnil; + description = ti->description ? rb_str_new2(ti->description) : Qnil; + encoding = ti->encoding ? rb_str_new2(ti->encoding) : Qnil; + foundry = ti->foundry ? rb_str_new2(ti->foundry) : Qnil; + format = ti->format ? rb_str_new2(ti->format) : Qnil; return rb_funcall(Class_Font, ID_new, 9 , name, description, family, style , stretch, weight, encoding, foundry, format); } @@ -2286,12 +2386,15 @@ Purpose: Enum class alloc function */ VALUE Enum_alloc(VALUE class) { MagickEnum *magick_enum; + volatile VALUE enumr; - return Data_Make_Struct(class, MagickEnum, NULL, NULL, magick_enum); + enumr = Data_Make_Struct(class, MagickEnum, NULL, NULL, magick_enum); + OBJ_FREEZE(enumr); + return enumr; } #else /* @@ -2309,18 +2412,19 @@ Notes: `class' can be an Enum subclass */ VALUE Enum_new(VALUE class, VALUE sym, VALUE val) { volatile VALUE new_enum; - VALUE argv[2]; + volatile VALUE argv[2]; MagickEnum *magick_enum; new_enum = Data_Make_Struct(class, MagickEnum, NULL, NULL, magick_enum); argv[0] = sym; argv[1] = val; rb_obj_call_init(new_enum, 2, argv); + OBJ_FREEZE(new_enum); return new_enum; } #endif @@ -2456,39 +2560,54 @@ return rb_str_new2(str); } /* - * Method: xxx.each - * Purpose: singleton iterator over enumerators list + * Method: xxx.values + * Purpose: Behaves like #each if a block is present, otherwise like #to_a. * Notes: defined for each Enum subclass */ static VALUE Enum_type_values(VALUE class) { - volatile VALUE enumerators; + volatile VALUE enumerators, copy; + volatile VALUE rv; int x; enumerators = rb_cvar_get(class, ID_enumerators); - for (x = 0; x < RARRAY(enumerators)->len; x++) + if (rb_block_given_p()) { - rb_yield(rb_ary_entry(enumerators, x)); + for (x = 0; x < RARRAY(enumerators)->len; x++) + { + rb_yield(rb_ary_entry(enumerators, x)); + } + rv = class; } + else + { + copy = rb_ary_new2(RARRAY(enumerators)->len); + for (x = 0; x < RARRAY(enumerators)->len; x++) + { + rb_ary_push(copy, rb_ary_entry(enumerators, x)); + } + OBJ_FREEZE(copy); + rv = copy; + } - return class; + return rv; } /* - Static: Compliance_name + Static: ComplianceType_name Purpose: Return the string representation of a ComplianceType value Notes: xMagick will OR multiple compliance types so we have to arbitrarily pick one name. Set the compliance argument to the selected value. */ static const char * -Compliance_name(ComplianceType *c) +ComplianceType_name(ComplianceType *c) { if ((*c & (SVGCompliance|X11Compliance|XPMCompliance)) == (SVGCompliance|X11Compliance|XPMCompliance)) { return "AllCompliance"; @@ -2660,11 +2779,11 @@ registry_id = SetMagickRegistry(ImageRegistryType, image, sizeof(Image), &image->exception); if (registry_id < 0) { rb_raise(rb_eRuntimeError, "SetMagickRegistry failed."); } - HANDLE_ERROR_IMG(image) + rm_check_image_exception(image, RetainOnError); sprintf(tmpnam, "mpri:%ld", registry_id); } /* @@ -2804,231 +2923,381 @@ } /* - Static: magick_error_handler - Purpose: Build error or warning message string. If the error - is severe, raise the ImageMagickError exception, - otherwise print an optional warning. -*/ -static void -magick_error_handler( - ExceptionType severity, - const char *reason, - const char *description -#if defined(HAVE_EXCEPTIONINFO_MODULE) - , - const char *module, - const char *function, - unsigned long line -#endif - ) + * Extern: rm_clone_image + * Purpose: clone an image, handle errors + */ +Image *rm_clone_image(Image *image) { - char msg[1024]; + Image *clone; + ExceptionInfo exception; - if (severity >= ErrorException) + GetExceptionInfo(&exception); + clone = CloneImage(image, 0, 0, True, &exception); + if (!clone) { -#if defined(HAVE_SNPRINTF) - snprintf(msg, sizeof(msg)-1, -#else - sprintf(msg, -#endif - "%s%s%s", - GetLocaleExceptionMessage(severity, reason), - description ? ": " : "", - description ? GetLocaleExceptionMessage(severity, description) : ""); + rb_raise(rb_eNoMemError, "not enough memory to continue"); + } + rm_check_exception(&exception, clone, DestroyOnError); -#if defined(HAVE_EXCEPTIONINFO_MODULE) - { - char extra[100]; + return clone; +} -#if defined(HAVE_SNPRINTF) - snprintf(extra, sizeof(extra)-1, "%s at %s:%lu", function, module, line); + +/* + Extern: rm_progress_monitor + Purpose: SetImage(Info)ProgressMonitor exit + Notes: ImageMagick's "tag" argument is unused. We pass along the method name instead. +*/ +#if defined(HAVE_SETIMAGEPROGRESSMONITOR) +MagickBooleanType rm_progress_monitor( + const char *tag, + const MagickOffsetType of, + const MagickSizeType sp, + void *client_data) +{ + volatile VALUE rval; + volatile VALUE method, offset, span; + +#if defined(HAVE_LONG_LONG) // defined in Ruby's defines.h + offset = rb_ll2inum(of); + span = rb_ull2inum(sp); #else - sprintf(extra, "%s at %s:%lu", function, module, line); + offset = rb_int2big((long)of); + span = rb_uint2big((unsigned long)sp); #endif - raise_error(msg, extra); - } -#else - raise_error(msg, NULL); -#endif - } - else if (severity != UndefinedException) - { -#if defined(HAVE_SNPRINTF) - snprintf(msg, sizeof(msg)-1, -#else - sprintf(msg, -#endif - "RMagick: %s%s%s", - GetLocaleExceptionMessage(severity, reason), - description ? ": " : "", - description ? GetLocaleExceptionMessage(severity, description) : ""); - rb_warning(msg); - } + + method = rb_str_new2(rb_id2name(rb_frame_last_func())); + + rval = rb_funcall((VALUE)client_data, ID_call, 3, method, offset, span); + + return RTEST(rval) ? MagickTrue : MagickFalse; } +#endif /* - Extern: handle_error - Purpose: Called from RMagick routines to issue warning messages - and raise the ImageMagickError exception. - Notes: In order to free up memory before calling raise, this - routine copies the ExceptionInfo data to local storage - and then calls DestroyExceptionInfo before raising - the error. - - If the exception is an error, DOES NOT RETURN! + Extern: rm_split + Purpose: Remove the ImageMagick links between images in an scene + sequence. + Notes: The images remain grouped via the ImageList */ void -rm_handle_error(ExceptionInfo *ex) +rm_split(Image *image) { -#define RM_MAX_ERROR_CLAUSE 250 - ExceptionType sev = ex->severity; - char reason[RM_MAX_ERROR_CLAUSE+1]; - char desc[RM_MAX_ERROR_CLAUSE+1]; -#if defined(HAVE_EXCEPTIONINFO_MODULE) - char module[RM_MAX_ERROR_CLAUSE+1], function[RM_MAX_ERROR_CLAUSE+1]; - unsigned long line; -#endif - - reason[0] = '\0'; - desc[0] = '\0'; - - if (sev == UndefinedException) + if (!image) { - return; + rb_bug("RMagick FATAL: unseq called with NULL argument."); } - if (ex->reason) + while (image) { - strncpy(reason, ex->reason, RM_MAX_ERROR_CLAUSE); - reason[RM_MAX_ERROR_CLAUSE] = '\0'; + (void) RemoveFirstImageFromList(&image); } - if (ex->description) +} + + + +/* + * Static: copy_exception + * clear_exception + * Purpose: Define alternative implementations of ImageMagick's + * InheritException and ClearMagickException. + * Notes: InheritException is ALWAYS defined in ImageMagick and + * NEVER defined in GraphicsMagick. ClearMagickException + * is defined in ImageMagick 6.2.6 and later and NEVER + * defined in GraphicsMagick. + * + * The purpose of InheritException is to copy the exception + * information from a "related" exception to a destination + * exception if the related exception is more severe than the + * destination exception. + * + * The purpose of ClearException is to reset the ExceptionInfo + * structure to its initial format. + */ + +#if defined(HAVE_INHERITEXCEPTION) + + // This is ImageMagick - InheritException is always defined + static void copy_exception(ExceptionInfo *exception, ExceptionInfo *relative) { - strncpy(desc, ex->description, RM_MAX_ERROR_CLAUSE); - desc[RM_MAX_ERROR_CLAUSE] = '\0'; + InheritException(exception, relative); } -#if defined(HAVE_EXCEPTIONINFO_MODULE) - module[0] = '\0'; - function[0] = '\0'; + // ImageMagick < 6.2.6 had a different kind of ExceptionInfo struct + // and doesn't define ClearMagickException. + #if defined(HAVE_CLEARMAGICKEXCEPTION) + static void clear_exception(ExceptionInfo *exception) + { + ClearMagickException(exception); + } + #else - if (ex->module) + static void clear_exception(ExceptionInfo *exception) + { + DestroyExceptionInfo(exception); + GetExceptionInfo(exception); + } + + #endif + + +#else // !defined(HAVE_INHERITEXCEPTION) + + // This is GraphicsMagick. Very old versions don't support the + // module, function, and line fields in the ExceptionInfo struct. + + static void copy_exception(ExceptionInfo *exception, ExceptionInfo *relative) { - strncpy(module, ex->module, RM_MAX_ERROR_CLAUSE); - module[RM_MAX_ERROR_CLAUSE] = '\0'; + assert(exception != NULL); + assert(exception->signature == MagickSignature); + assert(relative != NULL); + + if (relative->severity < exception->severity) + { + return; + } + + DestroyExceptionInfo(exception); + GetExceptionInfo(exception); + + exception->severity = relative->severity; + if (relative->reason) + { + magick_clone_string(&exception->reason, relative->reason); + } + + if (relative->description) + { + magick_clone_string(&exception->description, relative->description); + } + + + #if defined(HAVE_EXCEPTIONINFO_MODULE) + if (relative->module) + { + magick_clone_string(&exception->module, relative->module); + } + if (relative->function) + { + magick_clone_string(&exception->function, relative->function); + } + + exception->line = relative->line; + #endif } - if (ex->function) + + + static void clear_exception(ExceptionInfo *exception) { - strncpy(function, ex->function, RM_MAX_ERROR_CLAUSE); - function[RM_MAX_ERROR_CLAUSE] = '\0'; + DestroyExceptionInfo(exception); + GetExceptionInfo(exception); } - line = ex->line; -#endif - // Let ImageMagick reclaim its storage - DestroyExceptionInfo(ex); - // Reset the severity. If the exception structure is in an - // Image and this exception is rescued and the Image reused, - // we need the Image to be pristine! - GetExceptionInfo(ex); - -#if !defined(HAVE_EXCEPTIONINFO_MODULE) - magick_error_handler(sev, reason, desc); -#else - magick_error_handler(sev, reason, desc, module, function, line); #endif -} + /* - Extern: handle_all_errors - Purpose: Examine all the images in a sequence. If any - image has an error, raise an exception. Otherwise - if any image has a warning, issue a warning message. -*/ -void rm_handle_all_errors(Image *seq) + Extern: rm_check_image_exception + Purpose: If an ExceptionInfo struct in a list of images indicates a warning, + issue a warning message. If an ExceptionInfo struct indicates an + error, raise an exception and optionally destroy the images. + */ +void +rm_check_image_exception(Image *imglist, ErrorRetention retention) { + ExceptionInfo exception; Image *badboy = NULL; - Image *image = seq; + Image *image; + if (imglist == NULL) + { + return; + } + + GetExceptionInfo(&exception); + + // Find the image with the highest severity + image = GetFirstImageInList(imglist); while (image) { if (image->exception.severity != UndefinedException) { - // Stop at the 1st image with an error - if (image->exception.severity > WarningException) + if (!badboy || image->exception.severity > badboy->exception.severity) { badboy = image; - break; + copy_exception(&exception, &badboy->exception); } - else if (!badboy) - { - badboy = image; - } + + clear_exception(&image->exception); } image = GetNextImageInList(image); } if (badboy) { - if (badboy->exception.severity > WarningException) - { - rm_split(seq); - } - rm_handle_error(&badboy->exception); + rm_check_exception(&exception, imglist, retention); } } + /* - Extern: rm_progress_monitor - Purpose: SetImage(Info)ProgressMonitor exit - Notes: ImageMagick's "tag" argument is unused. We pass along the method name instead. + * Extern: rm_check_exception + * Purpose: Call handle_exception if there is an exception to handle. + */ +void +rm_check_exception(ExceptionInfo *exception, Image *imglist, ErrorRetention retention) +{ + if (exception->severity == UndefinedException) + { + return; + } + + handle_exception(exception, imglist, retention); +} + + +/* + * Static: handle_exception + * Purpose: called when rm_check_exception determines that we need + * to either issue a warning message or raise an exception. + * This function allocates a bunch of stack so we don't call + * it unless we have to. */ -#if defined(HAVE_SETIMAGEPROGRESSMONITOR) -MagickBooleanType rm_progress_monitor( - const char *tag, - const MagickOffsetType of, - const MagickSizeType sp, - void *client_data) +static void +handle_exception(ExceptionInfo *exception, Image *imglist, ErrorRetention retention) { - volatile VALUE rval; - volatile VALUE method, offset, span; -#if defined(HAVE_LONG_LONG) // defined in Ruby's defines.h - offset = rb_ll2inum(of); - span = rb_ull2inum(sp); + char reason[500]; + char desc[500]; + char msg[sizeof(reason)+sizeof(desc)+20]; + +#if defined(HAVE_EXCEPTIONINFO_MODULE) + char module[200], function[200]; + char extra[sizeof(module)+sizeof(function)+20]; + unsigned long line; +#endif + + + memset(msg, 0, sizeof(msg)); + + + // Handle simple warning + if (exception->severity < ErrorException) + { +#if defined(HAVE_SNPRINTF) + snprintf(msg, sizeof(msg)-1, "RMagick: %s%s%s", #else - offset = rb_int2big((long)of); - span = rb_uint2big((unsigned long)sp); + sprintf(msg, "RMagick: %.500s%s%.500s", #endif + GetLocaleExceptionMessage(exception->severity, exception->reason), + exception->description ? ": " : "", + exception->description ? GetLocaleExceptionMessage(exception->severity, exception->description) : ""); + msg[sizeof(msg)-1] = '\0'; + rb_warning(msg); - method = rb_str_new2(rb_id2name(rb_frame_last_func())); + DestroyExceptionInfo(exception); + return; + } - rval = rb_funcall((VALUE)client_data, ID_call, 3, method, offset, span); + // Raise an exception. We're not coming back... - return RTEST(rval) ? MagickTrue : MagickFalse; -} + + // Newly-created images should be destroyed, images that are part + // of image objects should be retained but split. + if (imglist) + { + if (retention == DestroyOnError) + { + (void) DestroyImageList(imglist); + imglist = NULL; + } + else + { + rm_split(imglist); + } + } + + + // Clone the ExceptionInfo with all arguments on the stack. + memset(reason, 0, sizeof(reason)); + memset(desc, 0, sizeof(desc)); + + if (exception->reason) + { + strncpy(reason, exception->reason, sizeof(reason)-1); + reason[sizeof(reason)-1] = '\0'; + } + if (exception->description) + { + strncpy(desc, exception->description, sizeof(desc)-1); + desc[sizeof(desc)-1] = '\0'; + } + + +#if defined(HAVE_SNPRINTF) + snprintf(msg, sizeof(msg)-1, "%s%s%s", + GetLocaleExceptionMessage(exception->severity, reason), + desc[0] ? ": " : "", + desc[0] ? GetLocaleExceptionMessage(exception->severity, desc) : ""); +#else + sprintf(msg, "%.*s%s%.*s", + sizeof(reason)-1, GetLocaleExceptionMessage(exception->severity, reason), + desc[0] ? ": " : "", + sizeof(desc)-1, desc[0] ? GetLocaleExceptionMessage(exception->severity, desc) : ""); #endif + msg[sizeof(msg)-1] = '\0'; -/* - Extern: rm_split - Purpose: Remove the ImageMagick links between images in an scene - sequence. - Notes: The images remain grouped via the ImageList -*/ -void -rm_split(Image *image) -{ - if (!image) +#if defined(HAVE_EXCEPTIONINFO_MODULE) + + memset(module, 0, sizeof(module)); + memset(function, 0, sizeof(function)); + memset(extra, 0, sizeof(extra)); + + if (exception->module) { - rb_bug("RMagick FATAL: unseq called with NULL argument."); + strncpy(module, exception->module, sizeof(module)-1); + module[sizeof(module)-1] = '\0'; } - while (image) + if (exception->function) { - (void) RemoveFirstImageFromList(&image); + strncpy(function, exception->function, sizeof(function)-1); + function[sizeof(function)-1] = '\0'; } + line = exception->line; + +#if defined(HAVE_SNPRINTF) + snprintf(extra, sizeof(extra)-1, "%s at %s:%lu", function, module, line); +#else + sprintf(extra, "%.*s at %.*s:%lu", sizeof(function), function, sizeof(module), module, line); +#endif + + extra[sizeof(extra)-1] = '\0'; + + DestroyExceptionInfo(exception); + raise_error(msg, extra); + +#else + DestroyExceptionInfo(exception); + raise_error(msg, NULL); +#endif + } + + +/* + * Extern: rm_ensure_result + * Purpose: RMagick expected a result. If it got NULL instead raise an exception. + */ +void rm_ensure_result(Image *image) +{ + if (!image) + { + rb_raise(rb_eRuntimeError, MagickPackageName " library function failed to return a result."); + } +} +