initCIELabConversion(TIFFRGBAImage* img)
{
	static const char module[] = "initCIELabConversion";

	float   *whitePoint;
	float   refWhite[3];

	TIFFGetFieldDefaulted(img->tif, TIFFTAG_WHITEPOINT, &whitePoint);
	if (whitePoint[1] == 0.0f ) {
		TIFFErrorExt(img->tif->tif_clientdata, module,
		    "Invalid value for WhitePoint tag.");
		return NULL;
        }

	if (!img->cielab) {
		img->cielab = (TIFFCIELabToRGB *)
			_TIFFmalloc(sizeof(TIFFCIELabToRGB));
		if (!img->cielab) {
			TIFFErrorExt(img->tif->tif_clientdata, module,
			    "No space for CIE L*a*b*->RGB conversion state.");
			return NULL;
		}
	}

	refWhite[1] = 100.0F;
	refWhite[0] = whitePoint[0] / whitePoint[1] * refWhite[1];
	refWhite[2] = (1.0F - whitePoint[0] - whitePoint[1])
		      / whitePoint[1] * refWhite[1];
	if (TIFFCIELabToRGBInit(img->cielab, &display_sRGB, refWhite) < 0) {
		TIFFErrorExt(img->tif->tif_clientdata, module,
		    "Failed to initialize CIE L*a*b*->RGB conversion state.");
		_TIFFfree(img->cielab);
		return NULL;
	}

	return putcontig8bitCIELab;
}


DECLARESepPutFunc(putCMYKseparate8bittile)
{
	(void) img; (void) y;
	for( ; h > 0; --h) {
		uint32 rv, gv, bv, kv;
		for (x = w; x > 0; --x) {
			kv = 255 - *a++;
			rv = (kv*(255-*r++))/255;
			gv = (kv*(255-*g++))/255;
			bv = (kv*(255-*b++))/255;
			*cp++ = PACK4(rv,gv,bv,255);
		}
		SKEW4(r, g, b, a, fromskew);
		cp += toskew;
	}
}


TIFFRGBAImageBegin(TIFFRGBAImage* img, TIFF* tif, int stop, char emsg[1024])
{
	uint16* sampleinfo;
	uint16 extrasamples;
	uint16 planarconfig;
	uint16 compress;
	int colorchannels;
	uint16 *red_orig, *green_orig, *blue_orig;
	int n_color;

	if( !TIFFRGBAImageOK(tif, emsg) )
		return 0;

	/* Initialize to normal values */
	img->row_offset = 0;
	img->col_offset = 0;
	img->redcmap = NULL;
	img->greencmap = NULL;
	img->bluecmap = NULL;
	img->Map = NULL;
	img->BWmap = NULL;
	img->PALmap = NULL;
	img->ycbcr = NULL;
	img->cielab = NULL;
	img->UaToAa = NULL;
	img->Bitdepth16To8 = NULL;
	img->req_orientation = ORIENTATION_BOTLEFT;     /* It is the default */

	img->tif = tif;
	img->stoponerr = stop;
	TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &img->bitspersample);
	switch (img->bitspersample) {
		case 1:
		case 2:
		case 4:
		case 8:
		case 16:
			break;
		default:
			sprintf(emsg, "Sorry, can not handle images with %d-bit samples",
			    img->bitspersample);
			goto fail_return;
	}
	img->alpha = 0;
	TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &img->samplesperpixel);
	TIFFGetFieldDefaulted(tif, TIFFTAG_EXTRASAMPLES,
	    &extrasamples, &sampleinfo);
	if (extrasamples >= 1)
	{
		switch (sampleinfo[0]) {
			case EXTRASAMPLE_UNSPECIFIED:          /* Workaround for some images without */
				if (img->samplesperpixel > 3)  /* correct info about alpha channel */
					img->alpha = EXTRASAMPLE_ASSOCALPHA;
				break;
			case EXTRASAMPLE_ASSOCALPHA:           /* data is pre-multiplied */
			case EXTRASAMPLE_UNASSALPHA:           /* data is not pre-multiplied */
				img->alpha = sampleinfo[0];
				break;
		}
	}

#ifdef DEFAULT_EXTRASAMPLE_AS_ALPHA
	if( !TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &img->photometric))
		img->photometric = PHOTOMETRIC_MINISWHITE;

	if( extrasamples == 0
	    && img->samplesperpixel == 4
	    && img->photometric == PHOTOMETRIC_RGB )
	{
		img->alpha = EXTRASAMPLE_ASSOCALPHA;
		extrasamples = 1;
	}
#endif

	colorchannels = img->samplesperpixel - extrasamples;
	TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &compress);
	TIFFGetFieldDefaulted(tif, TIFFTAG_PLANARCONFIG, &planarconfig);
	if (!TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &img->photometric)) {
		switch (colorchannels) {
			case 1:
				if (isCCITTCompression(tif))
					img->photometric = PHOTOMETRIC_MINISWHITE;
				else
					img->photometric = PHOTOMETRIC_MINISBLACK;
				break;
			case 3:
				img->photometric = PHOTOMETRIC_RGB;
				break;
			default:
				sprintf(emsg, "Missing needed %s tag", photoTag);
                                goto fail_return;
		}
	}
	switch (img->photometric) {
		case PHOTOMETRIC_PALETTE:
			if (!TIFFGetField(tif, TIFFTAG_COLORMAP,
			    &red_orig, &green_orig, &blue_orig)) {
				sprintf(emsg, "Missing required \"Colormap\" tag");
                                goto fail_return;
			}

			/* copy the colormaps so we can modify them */
			n_color = (1U << img->bitspersample);
			img->redcmap = (uint16 *) _TIFFmalloc(sizeof(uint16)*n_color);
			img->greencmap = (uint16 *) _TIFFmalloc(sizeof(uint16)*n_color);
			img->bluecmap = (uint16 *) _TIFFmalloc(sizeof(uint16)*n_color);
			if( !img->redcmap || !img->greencmap || !img->bluecmap ) {
				sprintf(emsg, "Out of memory for colormap copy");
                                goto fail_return;
			}

			_TIFFmemcpy( img->redcmap, red_orig, n_color * 2 );
			_TIFFmemcpy( img->greencmap, green_orig, n_color * 2 );
			_TIFFmemcpy( img->bluecmap, blue_orig, n_color * 2 );

			/* fall through... */
		case PHOTOMETRIC_MINISWHITE:
		case PHOTOMETRIC_MINISBLACK:
			if (planarconfig == PLANARCONFIG_CONTIG
			    && img->samplesperpixel != 1
			    && img->bitspersample < 8 ) {
				sprintf(emsg,
				    "Sorry, can not handle contiguous data with %s=%d, "
				    "and %s=%d and Bits/Sample=%d",
				    photoTag, img->photometric,
				    "Samples/pixel", img->samplesperpixel,
				    img->bitspersample);
                                goto fail_return;
			}
			break;
		case PHOTOMETRIC_YCBCR:
			/* It would probably be nice to have a reality check here. */
			if (planarconfig == PLANARCONFIG_CONTIG)
				/* can rely on libjpeg to convert to RGB */
				/* XXX should restore current state on exit */
				switch (compress) {
					case COMPRESSION_JPEG:
						/*
						 * TODO: when complete tests verify complete desubsampling
						 * and YCbCr handling, remove use of TIFFTAG_JPEGCOLORMODE in
						 * favor of tif_getimage.c native handling
						 */
						TIFFSetField(tif, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
						img->photometric = PHOTOMETRIC_RGB;
						break;
					default:
						/* do nothing */;
						break;
				}
			/*
			 * TODO: if at all meaningful and useful, make more complete
			 * support check here, or better still, refactor to let supporting
			 * code decide whether there is support and what meaningful
			 * error to return
			 */
			break;
		case PHOTOMETRIC_RGB:
			if (colorchannels < 3) {
				sprintf(emsg, "Sorry, can not handle RGB image with %s=%d",
				    "Color channels", colorchannels);
                                goto fail_return;
			}
			break;
		case PHOTOMETRIC_SEPARATED:
			{
				uint16 inkset;
				TIFFGetFieldDefaulted(tif, TIFFTAG_INKSET, &inkset);
				if (inkset != INKSET_CMYK) {
					sprintf(emsg, "Sorry, can not handle separated image with %s=%d",
					    "InkSet", inkset);
                                        goto fail_return;
				}
				if (img->samplesperpixel < 4) {
					sprintf(emsg, "Sorry, can not handle separated image with %s=%d",
					    "Samples/pixel", img->samplesperpixel);
                                        goto fail_return;
				}
			}
			break;
		case PHOTOMETRIC_LOGL:
			if (compress != COMPRESSION_SGILOG) {
				sprintf(emsg, "Sorry, LogL data must have %s=%d",
				    "Compression", COMPRESSION_SGILOG);
                                goto fail_return;
			}
			TIFFSetField(tif, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_8BIT);
			img->photometric = PHOTOMETRIC_MINISBLACK;	/* little white lie */
			img->bitspersample = 8;
			break;
		case PHOTOMETRIC_LOGLUV:
			if (compress != COMPRESSION_SGILOG && compress != COMPRESSION_SGILOG24) {
				sprintf(emsg, "Sorry, LogLuv data must have %s=%d or %d",
				    "Compression", COMPRESSION_SGILOG, COMPRESSION_SGILOG24);
                                goto fail_return;
			}
			if (planarconfig != PLANARCONFIG_CONTIG) {
				sprintf(emsg, "Sorry, can not handle LogLuv images with %s=%d",
				    "Planarconfiguration", planarconfig);
				return (0);
			}
			TIFFSetField(tif, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_8BIT);
			img->photometric = PHOTOMETRIC_RGB;		/* little white lie */
			img->bitspersample = 8;
			break;
		case PHOTOMETRIC_CIELAB:
			break;
		default:
			sprintf(emsg, "Sorry, can not handle image with %s=%d",
			    photoTag, img->photometric);
                        goto fail_return;
	}
	TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &img->width);
	TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &img->height);
	TIFFGetFieldDefaulted(tif, TIFFTAG_ORIENTATION, &img->orientation);
	img->isContig =
	    !(planarconfig == PLANARCONFIG_SEPARATE && img->samplesperpixel > 1);
	if (img->isContig) {
		if (!PickContigCase(img)) {
			sprintf(emsg, "Sorry, can not handle image");
			goto fail_return;
		}
	} else {
		if (!PickSeparateCase(img)) {
			sprintf(emsg, "Sorry, can not handle image");
			goto fail_return;
		}
	}
	return 1;

  fail_return:
        TIFFRGBAImageEnd( img );
        return 0;
}


setupMap(TIFFRGBAImage* img)
{
    int32 x, range;

    range = (int32)((1L<<img->bitspersample)-1);

    /* treat 16 bit the same as eight bit */
    if( img->bitspersample == 16 )
        range = (int32) 255;

    img->Map = (TIFFRGBValue*) _TIFFmalloc((range+1) * sizeof (TIFFRGBValue));
    if (img->Map == NULL) {
		TIFFErrorExt(img->tif->tif_clientdata, TIFFFileName(img->tif),
			"No space for photometric conversion table");
		return (0);
    }
    if (img->photometric == PHOTOMETRIC_MINISWHITE) {
	for (x = 0; x <= range; x++)
	    img->Map[x] = (TIFFRGBValue) (((range - x) * 255) / range);
    } else {
	for (x = 0; x <= range; x++)
	    img->Map[x] = (TIFFRGBValue) ((x * 255) / range);
    }
    if (img->bitspersample <= 16 &&
	(img->photometric == PHOTOMETRIC_MINISBLACK ||
	 img->photometric == PHOTOMETRIC_MINISWHITE)) {
	/*
	 * Use photometric mapping table to construct
	 * unpacking tables for samples <= 8 bits.
	 */
	if (!makebwmap(img))
	    return (0);
	/* no longer need Map, free it */
	_TIFFfree(img->Map);
	img->Map = NULL;
    }
    return (1);
}


DECLAREContigPutFunc(put1bitcmaptile)
{
    uint32** PALmap = img->PALmap;

    (void) x; (void) y;
    fromskew /= 8;
    for( ; h > 0; --h) {
	uint32* bw;
	UNROLL8(w, bw = PALmap[*pp++], *cp++ = *bw++);
	cp += toskew;
	pp += fromskew;
    }
}


DECLAREContigPutFunc(put2bitcmaptile)
{
    uint32** PALmap = img->PALmap;

    (void) x; (void) y;
    fromskew /= 4;
    for( ; h > 0; --h) {
	uint32* bw;
	UNROLL4(w, bw = PALmap[*pp++], *cp++ = *bw++);
	cp += toskew;
	pp += fromskew;
    }
}


DECLAREContigPutFunc(put16bitbwtile)
{
    int samplesperpixel = img->samplesperpixel;
    uint32** BWmap = img->BWmap;

    (void) y;
    for( ; h > 0; --h) {
        uint16 *wp = (uint16 *) pp;

	for (x = w; x > 0; --x)
        {
            /* use high order byte of 16bit value */

	    *cp++ = BWmap[*wp >> 8][0];
            pp += 2 * samplesperpixel;
            wp += samplesperpixel;
        }
	cp += toskew;
	pp += fromskew;
    }
}


  PosibErr<void> Config::set(Entry * entry0, bool do_unescape)
  {
    StackPtr<Entry> entry(entry0);

    if (entry->action == NoOp)
      entry->key = base_name(entry->key.str(), &entry->action);

    if (num_parms(entry->action) == 0 && !entry->value.empty())
    {
      if (entry->place_holder == -1) {
        switch (entry->action) {
        case Reset:
          return make_err(no_value_reset, entry->key);
        case Enable:
          return make_err(no_value_enable, entry->key);
        case Disable:
          return make_err(no_value_disable, entry->key);
        case ListClear:
          return make_err(no_value_clear, entry->key);
        default:
          abort(); // this shouldn't happen
        }
      } else {
        entry->place_holder = -1;
      }
    }

    if (entry->action != ListSet) {

      switch (entry->action) {
      case Enable:
        entry->value = "true";
        entry->action = Set;
        break;
      case Disable:
        entry->value = "false";
        entry->action = Set;
        break;
      default:
        ;
      }
      if (do_unescape) unescape(entry->value.mstr());

      entry->next = *insert_point_;
      *insert_point_ = entry;
      insert_point_ = &entry->next;
      entry.release();
      if (committed_) RET_ON_ERR(commit(entry0)); // entry0 == entry

    } else { // action == ListSet

      Entry * ent = new Entry;
      ent->key = entry->key;
      ent->action = ListClear;
      set(ent);

      ListAddHelper helper;
      helper.config = this;
      helper.orig_entry = entry;

      separate_list(entry->value.str(), helper, do_unescape);
    }
    return no_err;
  }


  PosibErr<int> Config::commit(Entry * entry, Conv * conv)
  {
    PosibErr<const KeyInfo *> pe = keyinfo(entry->key);
    {
      if (pe.has_err()) goto error;

      const KeyInfo * ki = pe;

      entry->key = ki->name;

      // FIXME: This is the correct thing to do but it causes problems
      //        with changing a filter mode in "pipe" mode and probably
      //        elsewhere.
      //if (attached_ && !(ki->flags & KEYINFO_MAY_CHANGE)) {
      //  pe = make_err(cant_change_value, entry->key);
      //  goto error;
      //}

      int place_holder = entry->place_holder;

      if (conv && ki->flags & KEYINFO_UTF8)
        entry->value = (*conv)(entry->value);

      if (ki->type != KeyInfoList && list_action(entry->action)) {
        pe = make_err(key_not_list, entry->key);
        goto error;
      }

      assert(ki->def != 0); // if null this key should never have values
      // directly added to it
      String value(entry->action == Reset ? get_default(ki) : entry->value);

      switch (ki->type) {

      case KeyInfoBool: {

        bool val;

        if  (value.empty() || entry->place_holder != -1) {
          // if entry->place_holder != -1 than IGNORE the value no
          // matter what it is
          entry->value = "true";
          val = true;
          place_holder = -1;
        } else if (value == "true") {
          val = true;
        } else if (value == "false") {
          val = false;
        } else {
          pe = make_err(bad_value, entry->key, value,
                        /* TRANSLATORS: "true" and "false" are literal
                         * values and should not be translated.*/
                        _("either \"true\" or \"false\""));
          goto error;
        }

        NOTIFY_ALL(item_updated(ki, val));
        break;

      } case KeyInfoString:

        NOTIFY_ALL(item_updated(ki, value));
        break;

      case KeyInfoInt:
      {
        int num;

        if (sscanf(value.str(), "%i", &num) == 1 && num >= 0) {
          NOTIFY_ALL(item_updated(ki, num));
        } else {
          pe = make_err(bad_value, entry->key, value, _("a positive integer"));
          goto error;
        }

        break;
      }
      case KeyInfoList:

        NOTIFY_ALL(list_updated(ki));
        break;

      }
      return place_holder;
    }
  error:
    entry->action = NoOp;
    if (!entry->file.empty())
      return pe.with_file(entry->file, entry->line_num);
    else
      return (PosibErrBase &)pe;
  }


  bool getline(IStream & in, DataPair & d, String & buf)
  {
    if (!in.getline(buf)) return false;
    d.value.str  = buf.mstr();
    d.value.size = buf.size();
    return true;
  }


  static inline void get_lang(String & str)
  {
    get_lang_env(str);
  }


  PosibErr<bool> ListAddHelper::add(ParmStr val)
  {
    Config::Entry * entry = new Config::Entry(*orig_entry);
    entry->value = val;
    entry->action = Config::ListAdd;
    config->set(entry);
    return true;
  }


    PossibleElementsEmul(const Config * d, bool ic, bool im)
      : include_extra(ic), include_modules(im),
        module_changed(false), cd(d), i(d->keyinfo_begin), m(0) {}


  PathBrowser::~PathBrowser()
  {
    delete els;
    if (dir_handle) closedir((DIR *)dir_handle);
  }


  const char * Config::base_name(const char * s, Action * action)
  {
    if (action) *action = Set;
    const char * c = strchr(s, '-');
    if (!c) return s;
    unsigned len = c - s;
    TEST("reset",   5, Reset);
    TEST("enable",  6, Enable);
    TEST("dont",    4, Disable);
    TEST("disable", 7, Disable);
    TEST("lset",    4, ListSet);
    TEST("rem",     3, ListRemove);
    TEST("remove",  6, ListRemove);
    TEST("add",     3, ListAdd);
    TEST("clear",   5, ListClear);
    return s;
  }


  static void get_lang(String & final_str)
  {
    // FIXME: THIS IS NOT THREAD SAFE
    String locale = setlocale (LC_ALL, NULL);
    if (locale == "C")
      setlocale (LC_ALL, "");
    const char * lang = setlocale (LC_MESSAGES, NULL);
    bool res = proc_locale_str(lang, final_str);
    if (locale == "C")
      setlocale(LC_MESSAGES, locale.c_str());
    if (!res)
      get_lang_env(final_str);
  }


  PosibErr<String> Config::retrieve(ParmStr key) const
  {
    RET_ON_ERR_SET(keyinfo(key), const KeyInfo *, ki);
    if (ki->type == KeyInfoList) return make_err(key_not_string, ki->name);

    const Entry * cur = lookup(ki->name);

    return cur ? cur->value : get_default(ki);
  }


set_obsolete_termcaps(TERMTYPE2 *tp)
{
#include "capdefaults.c"
}


_nc_parse_entry(ENTRY * entryp, int literal, bool silent)
{
    int token_type;
    struct name_table_entry const *entry_ptr;
    char *ptr, *base;
    const char *name;
    bool bad_tc_usage = FALSE;

    token_type = _nc_get_token(silent);

    if (token_type == EOF)
	return (EOF);
    if (token_type != NAMES)
	_nc_err_abort("Entry does not start with terminal names in column one");

    _nc_init_entry(entryp);

    entryp->cstart = _nc_comment_start;
    entryp->cend = _nc_comment_end;
    entryp->startline = _nc_start_line;
    DEBUG(2, ("Comment range is %ld to %ld", entryp->cstart, entryp->cend));

    /*
     * Strip off the 2-character termcap name, if present.  Originally termcap
     * used that as an indexing aid.  We can retain 2-character terminfo names,
     * but note that they would be lost if we translate to/from termcap.  This
     * feature is supposedly obsolete since "newer" BSD implementations do not
     * use it; however our reference for this feature is SunOS 4.x, which
     * implemented it.  Note that the resulting terminal type was never the
     * 2-character name, but was instead the first alias after that.
     */
#define ok_TC2(s) (isgraph(UChar(s)) && (s) != '|')
    ptr = _nc_curr_token.tk_name;
    if (_nc_syntax == SYN_TERMCAP
#if NCURSES_XNAMES
	&& !_nc_user_definable
#endif
	) {
	if (ok_TC2(ptr[0]) && ok_TC2(ptr[1]) && (ptr[2] == '|')) {
	    ptr += 3;
	    _nc_curr_token.tk_name[2] = '\0';
	}
    }

    entryp->tterm.str_table = entryp->tterm.term_names = _nc_save_str(ptr);

    if (entryp->tterm.str_table == 0)
	return (ERR);

    DEBUG(1, ("Starting '%s'", ptr));

    /*
     * We do this because the one-token lookahead in the parse loop
     * results in the terminal type getting prematurely set to correspond
     * to that of the next entry.
     */
    name = _nc_first_name(entryp->tterm.term_names);
    if (!valid_entryname(name)) {
	_nc_warning("invalid entry name \"%s\"", name);
	name = "invalid";
    }
    _nc_set_type(name);

    /* check for overly-long names and aliases */
    for (base = entryp->tterm.term_names; (ptr = strchr(base, '|')) != 0;
	 base = ptr + 1) {
	if (ptr - base > MAX_ALIAS) {
	    _nc_warning("%s `%.*s' may be too long",
			(base == entryp->tterm.term_names)
			? "primary name"
			: "alias",
			(int) (ptr - base), base);
	}
    }

    entryp->nuses = 0;

    for (token_type = _nc_get_token(silent);
	 token_type != EOF && token_type != NAMES;
	 token_type = _nc_get_token(silent)) {
	bool is_use = (strcmp(_nc_curr_token.tk_name, "use") == 0);
	bool is_tc = !is_use && (strcmp(_nc_curr_token.tk_name, "tc") == 0);
	if (is_use || is_tc) {
	    if (!VALID_STRING(_nc_curr_token.tk_valstring)
		|| _nc_curr_token.tk_valstring[0] == '\0') {
		_nc_warning("missing name for use-clause");
		continue;
	    } else if (!valid_entryname(_nc_curr_token.tk_valstring)) {
		_nc_warning("invalid name for use-clause \"%s\"",
			    _nc_curr_token.tk_valstring);
		continue;
	    } else if (entryp->nuses >= MAX_USES) {
		_nc_warning("too many use-clauses, ignored \"%s\"",
			    _nc_curr_token.tk_valstring);
		continue;
	    }
	    entryp->uses[entryp->nuses].name = _nc_save_str(_nc_curr_token.tk_valstring);
	    entryp->uses[entryp->nuses].line = _nc_curr_line;
	    entryp->nuses++;
	    if (entryp->nuses > 1 && is_tc) {
		BAD_TC_USAGE
	    }
	} else {
	    /* normal token lookup */
	    entry_ptr = _nc_find_entry(_nc_curr_token.tk_name,
				       _nc_get_hash_table(_nc_syntax));

	    /*
	     * Our kluge to handle aliasing.  The reason it's done
	     * this ugly way, with a linear search, is so the hashing
	     * machinery doesn't have to be made really complicated
	     * (also we get better warnings this way).  No point in
	     * making this case fast, aliased caps aren't common now
	     * and will get rarer.
	     */
	    if (entry_ptr == NOTFOUND) {
		const struct alias *ap;

		if (_nc_syntax == SYN_TERMCAP) {
		    if (entryp->nuses != 0) {
			BAD_TC_USAGE
		    }
		    for (ap = _nc_get_alias_table(TRUE); ap->from; ap++)
			if (strcmp(ap->from, _nc_curr_token.tk_name) == 0) {
			    if (ap->to == (char *) 0) {
				_nc_warning("%s (%s termcap extension) ignored",
					    ap->from, ap->source);
				goto nexttok;
			    }

			    entry_ptr = _nc_find_entry(ap->to,
						       _nc_get_hash_table(TRUE));
			    if (entry_ptr && !silent)
				_nc_warning("%s (%s termcap extension) aliased to %s",
					    ap->from, ap->source, ap->to);
			    break;
			}
		} else {	/* if (_nc_syntax == SYN_TERMINFO) */
		    for (ap = _nc_get_alias_table(FALSE); ap->from; ap++)
			if (strcmp(ap->from, _nc_curr_token.tk_name) == 0) {
			    if (ap->to == (char *) 0) {
				_nc_warning("%s (%s terminfo extension) ignored",
					    ap->from, ap->source);
				goto nexttok;
			    }

			    entry_ptr = _nc_find_entry(ap->to,
						       _nc_get_hash_table(FALSE));
			    if (entry_ptr && !silent)
				_nc_warning("%s (%s terminfo extension) aliased to %s",
					    ap->from, ap->source, ap->to);
			    break;
			}

		    if (entry_ptr == NOTFOUND) {
			entry_ptr = lookup_fullname(_nc_curr_token.tk_name);
		    }
		}
	    }
#if NCURSES_XNAMES
	    /*
	     * If we have extended-names active, we will automatically
	     * define a name based on its context.
	     */
	    if (entry_ptr == NOTFOUND
		&& _nc_user_definable) {
		if (expected_type(_nc_curr_token.tk_name, token_type, silent)) {
		    if ((entry_ptr = _nc_extend_names(entryp,
						      _nc_curr_token.tk_name,
						      token_type)) != 0) {
			if (_nc_tracing >= DEBUG_LEVEL(1)) {
			    _nc_warning("extended capability '%s'",
					_nc_curr_token.tk_name);
			}
		    }
		} else {
		    /* ignore it: we have already printed error message */
		    continue;
		}
	    }
#endif /* NCURSES_XNAMES */

	    /* can't find this cap name, not even as an alias */
	    if (entry_ptr == NOTFOUND) {
		if (!silent)
		    _nc_warning("unknown capability '%s'",
				_nc_curr_token.tk_name);
		continue;
	    }

	    /* deal with bad type/value combinations. */
	    if (token_type == CANCEL) {
		/*
		 * Prefer terminfo in this (long-obsolete) ambiguity:
		 */
		if (!strcmp("ma", _nc_curr_token.tk_name)) {
		    entry_ptr = _nc_find_type_entry("ma", NUMBER,
						    _nc_syntax != 0);
		    assert(entry_ptr != 0);
		}
	    } else if (entry_ptr->nte_type != token_type) {
		/*
		 * Nasty special cases here handle situations in which type
		 * information can resolve name clashes.  Normal lookup
		 * finds the last instance in the capability table of a
		 * given name, regardless of type.  find_type_entry looks
		 * for a first matching instance with given type.  So as
		 * long as all ambiguous names occur in pairs of distinct
		 * type, this will do the job.
		 */

		if (token_type == NUMBER
		    && !strcmp("ma", _nc_curr_token.tk_name)) {
		    /* tell max_attributes from arrow_key_map */
		    entry_ptr = _nc_find_type_entry("ma", NUMBER,
						    _nc_syntax != 0);
		    assert(entry_ptr != 0);

		} else if (token_type == STRING
			   && !strcmp("MT", _nc_curr_token.tk_name)) {
		    /* map terminfo's string MT to MT */
		    entry_ptr = _nc_find_type_entry("MT", STRING,
						    _nc_syntax != 0);
		    assert(entry_ptr != 0);

		} else if (token_type == BOOLEAN
			   && entry_ptr->nte_type == STRING) {
		    /* treat strings without following "=" as empty strings */
		    token_type = STRING;
		} else {
		    /* we couldn't recover; skip this token */
		    if (!silent) {
			const char *type_name;
			switch (entry_ptr->nte_type) {
			case BOOLEAN:
			    type_name = "boolean";
			    break;
			case STRING:
			    type_name = "string";
			    break;
			case NUMBER:
			    type_name = "numeric";
			    break;
			default:
			    type_name = "unknown";
			    break;
			}
			_nc_warning("wrong type used for %s capability '%s'",
				    type_name, _nc_curr_token.tk_name);
		    }
		    continue;
		}
	    }

	    /* now we know that the type/value combination is OK */
	    switch (token_type) {
	    case CANCEL:
		switch (entry_ptr->nte_type) {
		case BOOLEAN:
		    entryp->tterm.Booleans[entry_ptr->nte_index] = CANCELLED_BOOLEAN;
		    break;

		case NUMBER:
		    entryp->tterm.Numbers[entry_ptr->nte_index] = CANCELLED_NUMERIC;
		    break;

		case STRING:
		    entryp->tterm.Strings[entry_ptr->nte_index] = CANCELLED_STRING;
		    break;
		}
		break;

	    case BOOLEAN:
		entryp->tterm.Booleans[entry_ptr->nte_index] = TRUE;
		break;

	    case NUMBER:
#if !NCURSES_EXT_NUMBERS
		if (_nc_curr_token.tk_valnumber > MAX_NUMBER) {
		    entryp->tterm.Numbers[entry_ptr->nte_index] = MAX_NUMBER;
		} else
#endif
		{
		    entryp->tterm.Numbers[entry_ptr->nte_index] =
			(NCURSES_INT2) _nc_curr_token.tk_valnumber;
		}
		break;

	    case STRING:
		ptr = _nc_curr_token.tk_valstring;
		if (_nc_syntax == SYN_TERMCAP)
		    ptr = _nc_captoinfo(_nc_curr_token.tk_name,
					ptr,
					parametrized[entry_ptr->nte_index]);
		entryp->tterm.Strings[entry_ptr->nte_index] = _nc_save_str(ptr);
		break;

	    default:
		if (!silent)
		    _nc_warning("unknown token type");
		_nc_panic_mode((char) ((_nc_syntax == SYN_TERMCAP) ? ':' : ','));
		continue;
	    }
	}			/* end else cur_token.name != "use" */
      nexttok:
	continue;		/* cannot have a label w/o statement */
    }				/* endwhile (not EOF and not NAMES) */

    _nc_push_token(token_type);
    _nc_set_type(_nc_first_name(entryp->tterm.term_names));

    /*
     * Try to deduce as much as possible from extension capabilities
     * (this includes obsolete BSD capabilities).  Sigh...it would be more
     * space-efficient to call this after use resolution, but it has
     * to be done before entry allocation is wrapped up.
     */
    if (!literal) {
	if (_nc_syntax == SYN_TERMCAP) {
	    bool has_base_entry = FALSE;

	    /*
	     * Don't insert defaults if this is a `+' entry meant only
	     * for inclusion in other entries (not sure termcap ever
	     * had these, actually).
	     */
	    if (strchr(entryp->tterm.term_names, '+')) {
		has_base_entry = TRUE;
	    } else {
		unsigned i;
		/*
		 * Otherwise, look for a base entry that will already
		 * have picked up defaults via translation.
		 */
		for (i = 0; i < entryp->nuses; i++) {
		    if (entryp->uses[i].name != 0
			&& !strchr(entryp->uses[i].name, '+'))
			has_base_entry = TRUE;
		}
	    }

	    postprocess_termcap(&entryp->tterm, has_base_entry);
	} else
	    postprocess_terminfo(&entryp->tterm);
    }
    _nc_wrap_entry(entryp, FALSE);

    return (OK);
}


static PolygonInfo **AcquirePolygonThreadSet(
  const PrimitiveInfo *primitive_info)
{
  PathInfo
    *magick_restrict path_info;

  PolygonInfo
    **polygon_info;

  register ssize_t
    i;

  size_t
    number_threads;

  number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
  polygon_info=(PolygonInfo **) AcquireQuantumMemory(number_threads,
    sizeof(*polygon_info));
  if (polygon_info == (PolygonInfo **) NULL)
    return((PolygonInfo **) NULL);
  (void) memset(polygon_info,0,number_threads*sizeof(*polygon_info));
  path_info=ConvertPrimitiveToPath(primitive_info);
  if (path_info == (PathInfo *) NULL)
    return(DestroyPolygonThreadSet(polygon_info));
  for (i=0; i < (ssize_t) number_threads; i++)
  {
    polygon_info[i]=ConvertPathToPolygon(path_info);
    if (polygon_info[i] == (PolygonInfo *) NULL)
      return(DestroyPolygonThreadSet(polygon_info));
  }
  path_info=(PathInfo *) RelinquishMagickMemory(path_info);
  return(polygon_info);
}


MagickExport void GetDrawInfo(const ImageInfo *image_info,DrawInfo *draw_info)
{
  char
    *next_token;

  const char
    *option;

  ExceptionInfo
    *exception;

  ImageInfo
    *clone_info;

  /*
    Initialize draw attributes.
  */
  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
  assert(draw_info != (DrawInfo *) NULL);
  (void) memset(draw_info,0,sizeof(*draw_info));
  clone_info=CloneImageInfo(image_info);
  GetAffineMatrix(&draw_info->affine);
  exception=AcquireExceptionInfo();
  (void) QueryColorCompliance("#000F",AllCompliance,&draw_info->fill,
    exception);
  (void) QueryColorCompliance("#FFF0",AllCompliance,&draw_info->stroke,
    exception);
  draw_info->stroke_antialias=clone_info->antialias;
  draw_info->stroke_width=1.0;
  draw_info->fill_rule=EvenOddRule;
  draw_info->alpha=OpaqueAlpha;
  draw_info->fill_alpha=OpaqueAlpha;
  draw_info->stroke_alpha=OpaqueAlpha;
  draw_info->linecap=ButtCap;
  draw_info->linejoin=MiterJoin;
  draw_info->miterlimit=10;
  draw_info->decorate=NoDecoration;
  draw_info->pointsize=12.0;
  draw_info->undercolor.alpha=(MagickRealType) TransparentAlpha;
  draw_info->compose=OverCompositeOp;
  draw_info->render=MagickTrue;
  draw_info->clip_path=MagickFalse;
  draw_info->debug=IsEventLogging();
  if (clone_info->font != (char *) NULL)
    draw_info->font=AcquireString(clone_info->font);
  if (clone_info->density != (char *) NULL)
    draw_info->density=AcquireString(clone_info->density);
  draw_info->text_antialias=clone_info->antialias;
  if (fabs(clone_info->pointsize) >= MagickEpsilon)
    draw_info->pointsize=clone_info->pointsize;
  draw_info->border_color=clone_info->border_color;
  if (clone_info->server_name != (char *) NULL)
    draw_info->server_name=AcquireString(clone_info->server_name);
  option=GetImageOption(clone_info,"direction");
  if (option != (const char *) NULL)
    draw_info->direction=(DirectionType) ParseCommandOption(
      MagickDirectionOptions,MagickFalse,option);
  else
    draw_info->direction=UndefinedDirection;
  option=GetImageOption(clone_info,"encoding");
  if (option != (const char *) NULL)
    (void) CloneString(&draw_info->encoding,option);
  option=GetImageOption(clone_info,"family");
  if (option != (const char *) NULL)
    (void) CloneString(&draw_info->family,option);
  option=GetImageOption(clone_info,"fill");
  if (option != (const char *) NULL)
    (void) QueryColorCompliance(option,AllCompliance,&draw_info->fill,
      exception);
  option=GetImageOption(clone_info,"gravity");
  if (option != (const char *) NULL)
    draw_info->gravity=(GravityType) ParseCommandOption(MagickGravityOptions,
      MagickFalse,option);
  option=GetImageOption(clone_info,"interline-spacing");
  if (option != (const char *) NULL)
    draw_info->interline_spacing=StringToDouble(option,&next_token);
  option=GetImageOption(clone_info,"interword-spacing");
  if (option != (const char *) NULL)
    draw_info->interword_spacing=StringToDouble(option,&next_token);
  option=GetImageOption(clone_info,"kerning");
  if (option != (const char *) NULL)
    draw_info->kerning=StringToDouble(option,&next_token);
  option=GetImageOption(clone_info,"stroke");
  if (option != (const char *) NULL)
    (void) QueryColorCompliance(option,AllCompliance,&draw_info->stroke,
      exception);
  option=GetImageOption(clone_info,"strokewidth");
  if (option != (const char *) NULL)
    draw_info->stroke_width=StringToDouble(option,&next_token);
  option=GetImageOption(clone_info,"style");
  if (option != (const char *) NULL)
    draw_info->style=(StyleType) ParseCommandOption(MagickStyleOptions,
      MagickFalse,option);
  option=GetImageOption(clone_info,"undercolor");
  if (option != (const char *) NULL)
    (void) QueryColorCompliance(option,AllCompliance,&draw_info->undercolor,
      exception);
  option=GetImageOption(clone_info,"weight");
  if (option != (const char *) NULL)
    {
      ssize_t
        weight;

      weight=ParseCommandOption(MagickWeightOptions,MagickFalse,option);
      if (weight == -1)
        weight=(ssize_t) StringToUnsignedLong(option);
      draw_info->weight=(size_t) weight;
    }
  exception=DestroyExceptionInfo(exception);
  draw_info->signature=MagickCoreSignature;
  clone_info=DestroyImageInfo(clone_info);
}


static MagickBooleanType DrawRoundLinecap(Image *image,
  const DrawInfo *draw_info,const PrimitiveInfo *primitive_info,
  ExceptionInfo *exception)
{
  PrimitiveInfo
    linecap[5];

  register ssize_t
    i;

  for (i=0; i < 4; i++)
    linecap[i]=(*primitive_info);
  linecap[0].coordinates=4;
  linecap[1].point.x+=2.0*MagickEpsilon;
  linecap[2].point.x+=2.0*MagickEpsilon;
  linecap[2].point.y+=2.0*MagickEpsilon;
  linecap[3].point.y+=2.0*MagickEpsilon;
  linecap[4].primitive=UndefinedPrimitive;
  return(DrawPolygonPrimitive(image,draw_info,linecap,exception));
}


static MagickBooleanType TraceEllipse(MVGInfo *mvg_info,const PointInfo center,
  const PointInfo radii,const PointInfo arc)
{
  double
    coordinates,
    delta,
    step,
    x,
    y;

  PointInfo
    angle,
    point;

  PrimitiveInfo
    *primitive_info;

  register PrimitiveInfo
    *p;

  register ssize_t
    i;

  /*
    Ellipses are just short segmented polys.
  */
  primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
  primitive_info->coordinates=0;
  if ((fabs(radii.x) < MagickEpsilon) || (fabs(radii.y) < MagickEpsilon))
    return(MagickTrue);
  delta=2.0*PerceptibleReciprocal(MagickMax(radii.x,radii.y));
  step=MagickPI/8.0;
  if ((delta >= 0.0) && (delta < (MagickPI/8.0)))
    step=MagickPI/4.0/(MagickPI*PerceptibleReciprocal(delta)/2.0);
  angle.x=DegreesToRadians(arc.x);
  y=arc.y;
  while (y < arc.x)
    y+=360.0;
  angle.y=DegreesToRadians(y);
  coordinates=ceil((angle.y-angle.x)/step+1.0);
  if ((coordinates > (double) SSIZE_MAX) ||
      (coordinates > (double) GetMaxMemoryRequest()))
    {
      (void) ThrowMagickException(mvg_info->exception,GetMagickModule(),
        ResourceLimitError,"MemoryAllocationFailed","`%s'","");
      return(MagickFalse);
    }
  if (CheckPrimitiveExtent(mvg_info,(size_t) coordinates) == MagickFalse)
    return(MagickFalse);
  primitive_info=(*mvg_info->primitive_info)+mvg_info->offset;
  for (p=primitive_info; angle.x < angle.y; angle.x+=step)
  {
    point.x=cos(fmod(angle.x,DegreesToRadians(360.0)))*radii.x+center.x;
    point.y=sin(fmod(angle.x,DegreesToRadians(360.0)))*radii.y+center.y;
    if (TracePoint(p,point) == MagickFalse)
      return(MagickFalse);
    p+=p->coordinates;
  }
  point.x=cos(fmod(angle.y,DegreesToRadians(360.0)))*radii.x+center.x;
  point.y=sin(fmod(angle.y,DegreesToRadians(360.0)))*radii.y+center.y;
  if (TracePoint(p,point) == MagickFalse)
    return(MagickFalse);
  p+=p->coordinates;
  primitive_info->coordinates=(size_t) (p-primitive_info);
  primitive_info->closed_subpath=MagickFalse;
  x=fabs(primitive_info[0].point.x-
    primitive_info[primitive_info->coordinates-1].point.x);
  y=fabs(primitive_info[0].point.y-
    primitive_info[primitive_info->coordinates-1].point.y);
  if ((x < MagickEpsilon) && (y < MagickEpsilon))
    primitive_info->closed_subpath=MagickTrue;
  for (i=0; i < (ssize_t) primitive_info->coordinates; i++)
  {
    p->primitive=primitive_info->primitive;
    p--;
  }
  return(MagickTrue);
}


MagickExport MagickBooleanType DrawClipPath(Image *image,
  const DrawInfo *draw_info,const char *id,ExceptionInfo *exception)
{
  const char
    *clip_path;

  Image
    *clipping_mask;

  MagickBooleanType
    status;

  clip_path=GetImageArtifact(image,id);
  if (clip_path == (const char *) NULL)
    return(MagickFalse);
  clipping_mask=DrawClippingMask(image,draw_info,draw_info->clip_mask,clip_path,
    exception);
  if (clipping_mask == (Image *) NULL)
    return(MagickFalse);
  status=SetImageMask(image,WritePixelMask,clipping_mask,exception);
  clipping_mask=DestroyImage(clipping_mask);
  return(status);
}


static boolean ReadProfile(j_decompress_ptr jpeg_info)
{
  char
    name[MagickPathExtent];

  const StringInfo
    *previous_profile;

  ErrorManager
    *error_manager;

  ExceptionInfo
    *exception;

  Image
    *image;

  int
    marker;

  MagickBooleanType
    status;

  register ssize_t
    i;

  register unsigned char
    *p;

  size_t
    length;

  StringInfo
    *profile;

  /*
    Read generic profile.
  */
  length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
  length+=(size_t) GetCharacter(jpeg_info);
  if (length <= 2)
    return(TRUE);
  length-=2;
  marker=jpeg_info->unread_marker-JPEG_APP0;
  (void) FormatLocaleString(name,MagickPathExtent,"APP%d",marker);
  error_manager=(ErrorManager *) jpeg_info->client_data;
  exception=error_manager->exception;
  image=error_manager->image;
  profile=BlobToStringInfo((const void *) NULL,length);
  if (profile == (StringInfo *) NULL)
    {
      (void) ThrowMagickException(exception,GetMagickModule(),
        ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
      return(FALSE);
    }
  error_manager->profile=profile;
  p=GetStringInfoDatum(profile);
  for (i=0; i < (ssize_t) length; i++)
  {
    int
      c;

    c=GetCharacter(jpeg_info);
    if (c == EOF)
      break;
    *p++=(unsigned char) c;
  }
  error_manager->profile=NULL;
  if (i != (ssize_t) length)
    {
      profile=DestroyStringInfo(profile);
      (void) ThrowMagickException(exception,GetMagickModule(),
        CorruptImageError,"InsufficientImageDataInFile","`%s'",
        image->filename);
      return(FALSE);
    }
  if (marker == 1)
    {
      p=GetStringInfoDatum(profile);
      if ((length > 4) && (LocaleNCompare((char *) p,"exif",4) == 0))
        (void) CopyMagickString(name,"exif",MagickPathExtent);
      else if ((length > XmpNamespaceExtent) &&
          (LocaleNCompare((char *) p,xmp_namespace,XmpNamespaceExtent-1) == 0))
        {
          ssize_t
            j;

          /*
            Extract namespace from XMP profile.
          */
          p=GetStringInfoDatum(profile)+XmpNamespaceExtent;
          for (j=XmpNamespaceExtent; j < (ssize_t) GetStringInfoLength(profile); j++)
          {
            if (*p == '\0')
              break;
            p++;
          }
          if (j < (ssize_t) GetStringInfoLength(profile))
            (void) DestroyStringInfo(SplitStringInfo(profile,(size_t) (j+1)));
          (void) CopyMagickString(name,"xmp",MagickPathExtent);
        }
    }
  previous_profile=GetImageProfile(image,name);
  if ((previous_profile != (const StringInfo *) NULL) &&
      (CompareStringInfo(previous_profile,profile) != 0))
    {
      size_t
        profile_length;

      profile_length=GetStringInfoLength(profile);
      SetStringInfoLength(profile,GetStringInfoLength(profile)+
        GetStringInfoLength(previous_profile));
      (void) memmove(GetStringInfoDatum(profile)+
        GetStringInfoLength(previous_profile),GetStringInfoDatum(profile),
        profile_length);
      (void) memcpy(GetStringInfoDatum(profile),
        GetStringInfoDatum(previous_profile),
        GetStringInfoLength(previous_profile));
    }
  status=SetImageProfile(image,name,profile,exception);
  profile=DestroyStringInfo(profile);
  if (status == MagickFalse)
    {
      (void) ThrowMagickException(exception,GetMagickModule(),
        ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
      return(FALSE);
    }
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
      "Profile: %s, %.20g bytes",name,(double) length);
  return(TRUE);
}


static boolean ReadIPTCProfile(j_decompress_ptr jpeg_info)
{
  char
    magick[MagickPathExtent];

  ErrorManager
    *error_manager;

  ExceptionInfo
    *exception;

  Image
    *image;

  MagickBooleanType
    status;

  register ssize_t
    i;

  register unsigned char
    *p;

  size_t
    length;

  StringInfo
    *iptc_profile,
    *profile;

  /*
    Determine length of binary data stored here.
  */
  length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
  length+=(size_t) GetCharacter(jpeg_info);
  length-=2;
  if (length <= 14)
    {
      while (length-- > 0)
        if (GetCharacter(jpeg_info) == EOF)
          break;
      return(TRUE);
    }
  /*
    Validate that this was written as a Photoshop resource format slug.
  */
  for (i=0; i < 10; i++)
    magick[i]=(char) GetCharacter(jpeg_info);
  magick[10]='\0';
  length-=10;
  if (length <= 10)
    return(TRUE);
  if (LocaleCompare(magick,"Photoshop ") != 0)
    {
      /*
        Not a IPTC profile, return.
      */
      for (i=0; i < (ssize_t) length; i++)
        if (GetCharacter(jpeg_info) == EOF)
          break;
      return(TRUE);
    }
  /*
    Remove the version number.
  */
  for (i=0; i < 4; i++)
    if (GetCharacter(jpeg_info) == EOF)
      break;
  if (length <= 11)
    return(TRUE);
  length-=4;
  error_manager=(ErrorManager *) jpeg_info->client_data;
  exception=error_manager->exception;
  image=error_manager->image;
  profile=BlobToStringInfo((const void *) NULL,length);
  if (profile == (StringInfo *) NULL)
    {
      (void) ThrowMagickException(exception,GetMagickModule(),
        ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
      return(FALSE);
    }
  error_manager->profile=profile;
  p=GetStringInfoDatum(profile);
  for (i=0; i < (ssize_t) length; i++)
  {
    int
      c;

    c=GetCharacter(jpeg_info);
    if (c == EOF)
      break;
    *p++=(unsigned char) c;
  }
  error_manager->profile=NULL;
  if (i != (ssize_t) length)
    {
      profile=DestroyStringInfo(profile);
      (void) ThrowMagickException(exception,GetMagickModule(),
        CorruptImageError,"InsufficientImageDataInFile","`%s'",
        image->filename);
      return(FALSE);
    }
  /*
    The IPTC profile is actually an 8bim.
  */
  iptc_profile=(StringInfo *) GetImageProfile(image,"8bim");
  if (iptc_profile != (StringInfo *) NULL)
    {
      ConcatenateStringInfo(iptc_profile,profile);
      profile=DestroyStringInfo(profile);
    }
  else
    {
      status=SetImageProfile(image,"8bim",profile,exception);
      profile=DestroyStringInfo(profile);
      if (status == MagickFalse)
        {
          (void) ThrowMagickException(exception,GetMagickModule(),
            ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
          return(FALSE);
        }
    }
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
      "Profile: iptc, %.20g bytes",(double) length);
  return(TRUE);
}


static boolean ReadComment(j_decompress_ptr jpeg_info)
{
  ErrorManager
    *error_manager;

  ExceptionInfo
    *exception;

  Image
    *image;

  register unsigned char
    *p;

  register ssize_t
    i;

  size_t
    length;

  StringInfo
    *comment;

  /*
    Determine length of comment.
  */
  error_manager=(ErrorManager *) jpeg_info->client_data;
  exception=error_manager->exception;
  image=error_manager->image;
  length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
  length+=GetCharacter(jpeg_info);
  if (length <= 2)
    return(TRUE);
  length-=2;
  comment=BlobToStringInfo((const void *) NULL,length);
  if (comment == (StringInfo *) NULL)
    {
      (void) ThrowMagickException(exception,GetMagickModule(),
        ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
      return(FALSE);
    }
  /*
    Read comment.
  */
  error_manager->profile=comment;
  p=GetStringInfoDatum(comment);
  for (i=0; i < (ssize_t) length; i++)
  {
    int
      c;

    c=GetCharacter(jpeg_info);
    if (c == EOF)
      break;
    *p++=(unsigned char) c;
  }
  *p='\0';
  error_manager->profile=NULL;
  if (i != (ssize_t) length)
    {
      comment=DestroyStringInfo(comment);
      (void) ThrowMagickException(exception,GetMagickModule(),
        CorruptImageError,"InsufficientImageDataInFile","`%s'",
        image->filename);
      return(FALSE);
    }
  p=GetStringInfoDatum(comment);
  (void) SetImageProperty(image,"comment",(const char *) p,exception);
  comment=DestroyStringInfo(comment);
  return(TRUE);
}


static Image *ReadJPEGImage(const ImageInfo *image_info,
  ExceptionInfo *exception)
{
  char
    value[MagickPathExtent];

  const char
    *dct_method,
    *option;

  ErrorManager
    error_manager;

  Image
    *image;

  JSAMPLE
    *volatile jpeg_pixels;

  JSAMPROW
    scanline[1];

  MagickBooleanType
    debug,
    status;

  MagickSizeType
    number_pixels;

  MemoryInfo
    *memory_info;

  Quantum
    index;

  register ssize_t
    i;

  struct jpeg_decompress_struct
    jpeg_info;

  struct jpeg_error_mgr
    jpeg_error;

  struct jpeg_progress_mgr
    jpeg_progress;

  register JSAMPLE
    *p;

  size_t
    units;

  ssize_t
    y;

  /*
    Open image file.
  */
  assert(image_info != (const ImageInfo *) NULL);
  assert(image_info->signature == MagickCoreSignature);
  if (image_info->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
      image_info->filename);
  assert(exception != (ExceptionInfo *) NULL);
  assert(exception->signature == MagickCoreSignature);
  debug=IsEventLogging();
  (void) debug;
  image=AcquireImage(image_info,exception);
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
  if (status == MagickFalse)
    {
      image=DestroyImageList(image);
      return((Image *) NULL);
    }
  /*
    Verify that file size large enough to contain a JPEG datastream.
  */
  if (GetBlobSize(image) < 107)
    ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
  /*
    Initialize JPEG parameters.
  */
  (void) memset(&error_manager,0,sizeof(error_manager));
  (void) memset(&jpeg_info,0,sizeof(jpeg_info));
  (void) memset(&jpeg_error,0,sizeof(jpeg_error));
  (void) memset(&jpeg_progress,0,sizeof(jpeg_progress));
  jpeg_info.err=jpeg_std_error(&jpeg_error);
  jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
  jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
  memory_info=(MemoryInfo *) NULL;
  error_manager.exception=exception;
  error_manager.image=image;
  if (setjmp(error_manager.error_recovery) != 0)
    {
      jpeg_destroy_decompress(&jpeg_info);
      if (error_manager.profile != (StringInfo *) NULL)
        error_manager.profile=DestroyStringInfo(error_manager.profile);
      (void) CloseBlob(image);
      number_pixels=(MagickSizeType) image->columns*image->rows;
      if (number_pixels != 0)
        return(GetFirstImageInList(image));
      return(DestroyImage(image));
    }
  jpeg_info.client_data=(void *) &error_manager;
  jpeg_create_decompress(&jpeg_info);
  if (GetMaxMemoryRequest() != ~0UL)
    jpeg_info.mem->max_memory_to_use=(long) GetMaxMemoryRequest();
  jpeg_progress.progress_monitor=(void (*)(j_common_ptr)) JPEGProgressHandler;
  jpeg_info.progress=(&jpeg_progress);
  JPEGSourceManager(&jpeg_info,image);
  jpeg_set_marker_processor(&jpeg_info,JPEG_COM,ReadComment);
  option=GetImageOption(image_info,"profile:skip");
  if (IsOptionMember("ICC",option) == MagickFalse)
    jpeg_set_marker_processor(&jpeg_info,ICC_MARKER,ReadICCProfile);
  if (IsOptionMember("IPTC",option) == MagickFalse)
    jpeg_set_marker_processor(&jpeg_info,IPTC_MARKER,ReadIPTCProfile);
  for (i=1; i < 16; i++)
    if ((i != 2) && (i != 13) && (i != 14))
      if (IsOptionMember("APP",option) == MagickFalse)
        jpeg_set_marker_processor(&jpeg_info,(int) (JPEG_APP0+i),ReadProfile);
  i=(ssize_t) jpeg_read_header(&jpeg_info,TRUE);
  if ((image_info->colorspace == YCbCrColorspace) ||
      (image_info->colorspace == Rec601YCbCrColorspace) ||
      (image_info->colorspace == Rec709YCbCrColorspace))
    jpeg_info.out_color_space=JCS_YCbCr;
  /*
    Set image resolution.
  */
  units=0;
  if ((jpeg_info.saw_JFIF_marker != 0) && (jpeg_info.X_density != 1) &&
      (jpeg_info.Y_density != 1))
    {
      image->resolution.x=(double) jpeg_info.X_density;
      image->resolution.y=(double) jpeg_info.Y_density;
      units=(size_t) jpeg_info.density_unit;
    }
  if (units == 1)
    image->units=PixelsPerInchResolution;
  if (units == 2)
    image->units=PixelsPerCentimeterResolution;
  number_pixels=(MagickSizeType) image->columns*image->rows;
  option=GetImageOption(image_info,"jpeg:size");
  if ((option != (const char *) NULL) &&
      (jpeg_info.out_color_space != JCS_YCbCr))
    {
      double
        scale_factor;

      GeometryInfo
        geometry_info;

      MagickStatusType
        flags;

      /*
        Scale the image.
      */
      flags=ParseGeometry(option,&geometry_info);
      if ((flags & SigmaValue) == 0)
        geometry_info.sigma=geometry_info.rho;
      jpeg_calc_output_dimensions(&jpeg_info);
      image->magick_columns=jpeg_info.output_width;
      image->magick_rows=jpeg_info.output_height;
      scale_factor=1.0;
      if (geometry_info.rho != 0.0)
        scale_factor=jpeg_info.output_width/geometry_info.rho;
      if ((geometry_info.sigma != 0.0) &&
          (scale_factor > (jpeg_info.output_height/geometry_info.sigma)))
        scale_factor=jpeg_info.output_height/geometry_info.sigma;
      jpeg_info.scale_num=1U;
      jpeg_info.scale_denom=(unsigned int) scale_factor;
      jpeg_calc_output_dimensions(&jpeg_info);
      if (image->debug != MagickFalse)
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
          "Scale factor: %.20g",(double) scale_factor);
    }
#if (JPEG_LIB_VERSION >= 61) && defined(D_PROGRESSIVE_SUPPORTED)
#if defined(D_LOSSLESS_SUPPORTED)
  image->interlace=jpeg_info.process == JPROC_PROGRESSIVE ?
    JPEGInterlace : NoInterlace;
  image->compression=jpeg_info.process == JPROC_LOSSLESS ?
    LosslessJPEGCompression : JPEGCompression;
  if (jpeg_info.data_precision > 8)
    (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
      "12-bit JPEG not supported. Reducing pixel data to 8 bits","`%s'",
      image->filename);
  if (jpeg_info.data_precision == 16)
    jpeg_info.data_precision=12;
#else
  image->interlace=jpeg_info.progressive_mode != 0 ? JPEGInterlace :
    NoInterlace;
  image->compression=JPEGCompression;
#endif
#else
  image->compression=JPEGCompression;
  image->interlace=JPEGInterlace;
#endif
  option=GetImageOption(image_info,"jpeg:colors");
  if (option != (const char *) NULL)
    {
      /*
        Let the JPEG library quantize the image.
      */
      jpeg_info.quantize_colors=TRUE;
      jpeg_info.desired_number_of_colors=(int) StringToUnsignedLong(option);
    }
  option=GetImageOption(image_info,"jpeg:block-smoothing");
  if (option != (const char *) NULL)
    jpeg_info.do_block_smoothing=IsStringTrue(option) != MagickFalse ? TRUE :
      FALSE;
  dct_method=GetImageOption(image_info,"jpeg:dct-method");
  if (dct_method != (const char *) NULL)
    switch (*dct_method)
    {
      case 'D':
      case 'd':
      {
        if (LocaleCompare(dct_method,"default") == 0)
          jpeg_info.dct_method=JDCT_DEFAULT;
        break;
      }
      case 'F':
      case 'f':
      {
        if (LocaleCompare(dct_method,"fastest") == 0)
          jpeg_info.dct_method=JDCT_FASTEST;
        if (LocaleCompare(dct_method,"float") == 0)
          jpeg_info.dct_method=JDCT_FLOAT;
        break;
      }
      case 'I':
      case 'i':
      {
        if (LocaleCompare(dct_method,"ifast") == 0)
          jpeg_info.dct_method=JDCT_IFAST;
        if (LocaleCompare(dct_method,"islow") == 0)
          jpeg_info.dct_method=JDCT_ISLOW;
        break;
      }
    }
  option=GetImageOption(image_info,"jpeg:fancy-upsampling");
  if (option != (const char *) NULL)
    jpeg_info.do_fancy_upsampling=IsStringTrue(option) != MagickFalse ? TRUE :
      FALSE;
  jpeg_calc_output_dimensions(&jpeg_info);
  image->columns=jpeg_info.output_width;
  image->rows=jpeg_info.output_height;
  image->depth=(size_t) jpeg_info.data_precision;
  switch (jpeg_info.out_color_space)
  {
    case JCS_RGB:
    default:
    {
      (void) SetImageColorspace(image,sRGBColorspace,exception);
      break;
    }
    case JCS_GRAYSCALE:
    {
      (void) SetImageColorspace(image,GRAYColorspace,exception);
      break;
    }
    case JCS_YCbCr:
    {
      (void) SetImageColorspace(image,YCbCrColorspace,exception);
      break;
    }
    case JCS_CMYK:
    {
      (void) SetImageColorspace(image,CMYKColorspace,exception);
      break;
    }
  }
  if (IsITUFaxImage(image) != MagickFalse)
    {
      (void) SetImageColorspace(image,LabColorspace,exception);
      jpeg_info.out_color_space=JCS_YCbCr;
    }
  option=GetImageOption(image_info,"jpeg:colors");
  if (option != (const char *) NULL)
    if (AcquireImageColormap(image,StringToUnsignedLong(option),exception) == MagickFalse)
      {
        jpeg_destroy_decompress(&jpeg_info);
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
      }
  if ((jpeg_info.output_components == 1) && (jpeg_info.quantize_colors == 0))
    {
      size_t
        colors;

      colors=(size_t) GetQuantumRange(image->depth)+1;
      if (AcquireImageColormap(image,colors,exception) == MagickFalse)
        {
          jpeg_destroy_decompress(&jpeg_info);
          ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
        }
    }
  if (image->debug != MagickFalse)
    {
      if (image->interlace != NoInterlace)
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
          "Interlace: progressive");
      else
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
          "Interlace: nonprogressive");
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Data precision: %d",
        (int) jpeg_info.data_precision);
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Geometry: %dx%d",
        (int) jpeg_info.output_width,(int) jpeg_info.output_height);
    }
  JPEGSetImageQuality(&jpeg_info,image);
  JPEGSetImageSamplingFactor(&jpeg_info,image,exception);
  (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
    jpeg_info.out_color_space);
  (void) SetImageProperty(image,"jpeg:colorspace",value,exception);
#if defined(D_ARITH_CODING_SUPPORTED)
  if (jpeg_info.arith_code == TRUE)
    (void) SetImageProperty(image,"jpeg:coding","arithmetic",exception);
#endif
  if (image_info->ping != MagickFalse)
    {
      jpeg_destroy_decompress(&jpeg_info);
      (void) CloseBlob(image);
      return(GetFirstImageInList(image));
    }
  status=SetImageExtent(image,image->columns,image->rows,exception);
  if (status == MagickFalse)
    {
      jpeg_destroy_decompress(&jpeg_info);
      return(DestroyImageList(image));
    }
  (void) jpeg_start_decompress(&jpeg_info);
  if ((jpeg_info.output_components != 1) &&
      (jpeg_info.output_components != 3) && (jpeg_info.output_components != 4))
    {
      jpeg_destroy_decompress(&jpeg_info);
      ThrowReaderException(CorruptImageError,"ImageTypeNotSupported");
    }
  memory_info=AcquireVirtualMemory((size_t) image->columns,
    jpeg_info.output_components*sizeof(*jpeg_pixels));
  if (memory_info == (MemoryInfo *) NULL)
    {
      jpeg_destroy_decompress(&jpeg_info);
      ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
    }
  jpeg_pixels=(JSAMPLE *) GetVirtualMemoryBlob(memory_info);
  (void) memset(jpeg_pixels,0,image->columns*
    jpeg_info.output_components*sizeof(*jpeg_pixels));
  /*
    Convert JPEG pixels to pixel packets.
  */
  if (setjmp(error_manager.error_recovery) != 0)
    {
      if (memory_info != (MemoryInfo *) NULL)
        memory_info=RelinquishVirtualMemory(memory_info);
      jpeg_destroy_decompress(&jpeg_info);
      (void) CloseBlob(image);
      number_pixels=(MagickSizeType) image->columns*image->rows;
      if (number_pixels != 0)
        return(GetFirstImageInList(image));
      return(DestroyImage(image));
    }
  if (jpeg_info.quantize_colors != 0)
    {
      image->colors=(size_t) jpeg_info.actual_number_of_colors;
      if (jpeg_info.out_color_space == JCS_GRAYSCALE)
        for (i=0; i < (ssize_t) image->colors; i++)
        {
          image->colormap[i].red=(double) ScaleCharToQuantum(
            jpeg_info.colormap[0][i]);
          image->colormap[i].green=image->colormap[i].red;
          image->colormap[i].blue=image->colormap[i].red;
          image->colormap[i].alpha=(MagickRealType) OpaqueAlpha;
        }
      else
        for (i=0; i < (ssize_t) image->colors; i++)
        {
          image->colormap[i].red=(double) ScaleCharToQuantum(
            jpeg_info.colormap[0][i]);
          image->colormap[i].green=(double) ScaleCharToQuantum(
            jpeg_info.colormap[1][i]);
          image->colormap[i].blue=(double) ScaleCharToQuantum(
            jpeg_info.colormap[2][i]);
          image->colormap[i].alpha=(MagickRealType) OpaqueAlpha;
        }
    }
  scanline[0]=(JSAMPROW) jpeg_pixels;
  for (y=0; y < (ssize_t) image->rows; y++)
  {
    register ssize_t
      x;

    register Quantum
      *magick_restrict q;

    if (jpeg_read_scanlines(&jpeg_info,scanline,1) != 1)
      {
        (void) ThrowMagickException(exception,GetMagickModule(),
          CorruptImageWarning,"SkipToSyncByte","`%s'",image->filename);
        continue;
      }
    p=jpeg_pixels;
    q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
    if (q == (Quantum *) NULL)
      break;
    if (jpeg_info.data_precision > 8)
      {
        unsigned short
          scale;

        scale=65535/(unsigned short) GetQuantumRange((size_t)
          jpeg_info.data_precision);
        if (jpeg_info.output_components == 1)
          for (x=0; x < (ssize_t) image->columns; x++)
          {
            ssize_t
              pixel;

            pixel=(ssize_t) (scale*GETJSAMPLE(*p));
            index=(Quantum) ConstrainColormapIndex(image,pixel,exception);
            SetPixelIndex(image,index,q);
            SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
            p++;
            q+=GetPixelChannels(image);
          }
        else
          if (image->colorspace != CMYKColorspace)
            for (x=0; x < (ssize_t) image->columns; x++)
            {
              SetPixelRed(image,ScaleShortToQuantum(
                (unsigned short) (scale*GETJSAMPLE(*p++))),q);
              SetPixelGreen(image,ScaleShortToQuantum(
                (unsigned short) (scale*GETJSAMPLE(*p++))),q);
              SetPixelBlue(image,ScaleShortToQuantum(
                (unsigned short) (scale*GETJSAMPLE(*p++))),q);
              SetPixelAlpha(image,OpaqueAlpha,q);
              q+=GetPixelChannels(image);
            }
          else
            for (x=0; x < (ssize_t) image->columns; x++)
            {
              SetPixelCyan(image,QuantumRange-ScaleShortToQuantum(
                (unsigned short) (scale*GETJSAMPLE(*p++))),q);
              SetPixelMagenta(image,QuantumRange-ScaleShortToQuantum(
                (unsigned short) (scale*GETJSAMPLE(*p++))),q);
              SetPixelYellow(image,QuantumRange-ScaleShortToQuantum(
                (unsigned short) (scale*GETJSAMPLE(*p++))),q);
              SetPixelBlack(image,QuantumRange-ScaleShortToQuantum(
                (unsigned short) (scale*GETJSAMPLE(*p++))),q);
              SetPixelAlpha(image,OpaqueAlpha,q);
              q+=GetPixelChannels(image);
            }
      }
    else
      if (jpeg_info.output_components == 1)
        for (x=0; x < (ssize_t) image->columns; x++)
        {
          ssize_t
            pixel;

          pixel=(ssize_t) GETJSAMPLE(*p);
          index=(Quantum) ConstrainColormapIndex(image,pixel,exception);
          SetPixelIndex(image,index,q);
          SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
          p++;
          q+=GetPixelChannels(image);
        }
      else
        if (image->colorspace != CMYKColorspace)
          for (x=0; x < (ssize_t) image->columns; x++)
          {
            SetPixelRed(image,ScaleCharToQuantum((unsigned char)
              GETJSAMPLE(*p++)),q);
            SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
              GETJSAMPLE(*p++)),q);
            SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
              GETJSAMPLE(*p++)),q);
            SetPixelAlpha(image,OpaqueAlpha,q);
            q+=GetPixelChannels(image);
          }
        else
          for (x=0; x < (ssize_t) image->columns; x++)
          {
            SetPixelCyan(image,QuantumRange-ScaleCharToQuantum(
              (unsigned char) GETJSAMPLE(*p++)),q);
            SetPixelMagenta(image,QuantumRange-ScaleCharToQuantum(
              (unsigned char) GETJSAMPLE(*p++)),q);
            SetPixelYellow(image,QuantumRange-ScaleCharToQuantum(
              (unsigned char) GETJSAMPLE(*p++)),q);
            SetPixelBlack(image,QuantumRange-ScaleCharToQuantum(
              (unsigned char) GETJSAMPLE(*p++)),q);
            SetPixelAlpha(image,OpaqueAlpha,q);
            q+=GetPixelChannels(image);
          }
    if (SyncAuthenticPixels(image,exception) == MagickFalse)
      break;
    status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
      image->rows);
    if (status == MagickFalse)
      {
        jpeg_abort_decompress(&jpeg_info);
        break;
      }
  }
  if (status != MagickFalse)
    {
      error_manager.finished=MagickTrue;
      if (setjmp(error_manager.error_recovery) == 0)
        (void) jpeg_finish_decompress(&jpeg_info);
    }
  /*
    Free jpeg resources.
  */
  jpeg_destroy_decompress(&jpeg_info);
  memory_info=RelinquishVirtualMemory(memory_info);
  (void) CloseBlob(image);
  return(GetFirstImageInList(image));
}


static void InsertComplexFloatRow(Image *image,float *p,int y,double MinVal,
  double MaxVal,ExceptionInfo *exception)
{
  double f;
  int x;
  register Quantum *q;

  if (MinVal >= 0)
    MinVal = -1;
  if (MaxVal <= 0)
    MaxVal = 1;

  q = QueueAuthenticPixels(image, 0, y, image->columns, 1,exception);
  if (q == (Quantum *) NULL)
    return;
  for (x = 0; x < (ssize_t) image->columns; x++)
  {
    if (*p > 0)
      {
        f=(*p/MaxVal)*(Quantum) (QuantumRange-GetPixelRed(image,q));
        if ((f+GetPixelRed(image,q)) < QuantumRange)
          SetPixelRed(image,GetPixelRed(image,q)+ClampToQuantum(f),q);
        else
          SetPixelRed(image,QuantumRange,q);
        f/=2.0;
        if (f < GetPixelGreen(image,q))
          {
            SetPixelBlue(image,GetPixelBlue(image,q)-ClampToQuantum(f),q);
            SetPixelGreen(image,GetPixelBlue(image,q),q);
          }
        else
          {
            SetPixelGreen(image,0,q);
            SetPixelBlue(image,0,q);
          }
      }
    if (*p < 0)
      {
        f=(*p/MaxVal)*(Quantum) (QuantumRange-GetPixelBlue(image,q));
        if ((f+GetPixelBlue(image,q)) < QuantumRange)
          SetPixelBlue(image,GetPixelBlue(image,q)+ClampToQuantum(f),q);
        else
          SetPixelBlue(image,QuantumRange,q);
        f/=2.0;
        if (f < GetPixelGreen(image,q))
          {
            SetPixelRed(image,GetPixelRed(image,q)-ClampToQuantum(f),q);
            SetPixelGreen(image,GetPixelRed(image,q),q);
          }
        else
          {
            SetPixelGreen(image,0,q);
            SetPixelRed(image,0,q);
          }
      }
    p++;
    q++;
  }
  if (!SyncAuthenticPixels(image,exception))
    return;
  return;
}


static void InsertComplexDoubleRow(Image *image,double *p,int y,double MinVal,
  double MaxVal,ExceptionInfo *exception)
{
  double f;
  int x;
  register Quantum *q;

  if (MinVal >= 0)
    MinVal = -1;
  if (MaxVal <= 0)
    MaxVal = 1;

  q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
  if (q == (Quantum *) NULL)
    return;
  for (x = 0; x < (ssize_t) image->columns; x++)
  {
    if (*p > 0)
      {
        f=(*p/MaxVal)*(Quantum) (QuantumRange-GetPixelRed(image,q));
        if ((f+GetPixelRed(image,q)) >= QuantumRange)
          SetPixelRed(image,QuantumRange,q);
        else
          SetPixelRed(image,GetPixelRed(image,q)+ClampToQuantum(f),q);
        f=GetPixelGreen(image,q)-f/2.0;
        if (f <= 0.0)
          {
            SetPixelGreen(image,0,q);
            SetPixelBlue(image,0,q);
          }
        else
          {
            SetPixelBlue(image,ClampToQuantum(f),q);
            SetPixelGreen(image,ClampToQuantum(f),q);
          }
      }
    if (*p < 0)
      {
        f=(*p/MinVal)*(Quantum) (QuantumRange-GetPixelBlue(image,q));
        if ((f+GetPixelBlue(image,q)) >= QuantumRange)
          SetPixelBlue(image,QuantumRange,q);
        else
          SetPixelBlue(image,GetPixelBlue(image,q)+ClampToQuantum(f),q);
        f=GetPixelGreen(image,q)-f/2.0;
        if (f <= 0.0)
          {
            SetPixelRed(image,0,q);
            SetPixelGreen(image,0,q);
          }
        else
          {
            SetPixelRed(image,ClampToQuantum(f),q);
            SetPixelGreen(image,ClampToQuantum(f),q);
          }
      }
    p++;
    q++;
  }
  if (!SyncAuthenticPixels(image,exception))
    return;
  return;
}


char *mutt_gecos_name (char *dest, size_t destlen, struct passwd *pw)
{
  regmatch_t pat_match[1];
  size_t pwnl;
  int idx;
  char *p;

  if (!pw || !pw->pw_gecos)
    return NULL;

  memset (dest, 0, destlen);

  if (GecosMask.rx)
  {
    if (regexec (GecosMask.rx, pw->pw_gecos, 1, pat_match, 0) == 0)
      strfcpy (dest, pw->pw_gecos + pat_match[0].rm_so,
	       MIN (pat_match[0].rm_eo - pat_match[0].rm_so + 1, destlen));
  }
  else if ((p = strchr (pw->pw_gecos, ',')))
    strfcpy (dest, pw->pw_gecos, MIN (destlen, p - pw->pw_gecos + 1));
  else
    strfcpy (dest, pw->pw_gecos, destlen);

  pwnl = strlen (pw->pw_name);

  for (idx = 0; dest[idx]; idx++)
  {
    if (dest[idx] == '&')
    {
      memmove (&dest[idx + pwnl], &dest[idx + 1],
	       MAX((ssize_t)(destlen - idx - pwnl - 1), 0));
      memcpy (&dest[idx], pw->pw_name, MIN(destlen - idx - 1, pwnl));
      dest[idx] = toupper ((unsigned char) dest[idx]);
    }
  }

  return dest;
}


static int parse_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
{
  do
  {
    mutt_extract_token (buf, s, 0);
    /*
     * Check for deletion of entire list
     */
    if (mutt_strcmp (buf->data, "*") == 0)
    {
      mutt_free_list ((LIST **) data);
      break;
    }
    remove_from_list ((LIST **) data, buf->data);
  }
  while (MoreArgs (s));

  return 0;
}


static void _attachments_clean (void)
{
  int i;
  if (Context && Context->msgcount)
  {
    for (i = 0; i < Context->msgcount; i++)
      Context->hdrs[i]->attach_valid = 0;
  }
}


static int parse_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
{
  do
  {
    mutt_extract_token (buf, s, 0);
    add_to_list ((LIST **) data, buf->data);
  }
  while (MoreArgs (s));

  return 0;
}


int mutt_var_value_complete (char *buffer, size_t len, int pos)
{
  char var[STRING], *pt = buffer;
  int spaces;

  if (buffer[0] == 0)
    return 0;

  SKIPWS (buffer);
  spaces = buffer - pt;

  pt = buffer + pos - spaces;
  while ((pt > buffer) && !isspace ((unsigned char) *pt))
    pt--;
  pt++; /* move past the space */
  if (*pt == '=') /* abort if no var before the '=' */
    return 0;

  if (mutt_strncmp (buffer, "set", 3) == 0)
  {
    int idx;
    char val[LONG_STRING];
    const char *myvarval;

    strfcpy (var, pt, sizeof (var));
    /* ignore the trailing '=' when comparing */
    var[mutt_strlen (var) - 1] = 0;
    if ((idx = mutt_option_index (var)) == -1)
    {
      if ((myvarval = myvar_get(var)) != NULL)
      {
	pretty_var (pt, len - (pt - buffer), var, myvarval);
	return 1;
      }
      return 0; /* no such variable. */
    }
    else if (var_to_string (idx, val, sizeof (val)))
    {
      snprintf (pt, len - (pt - buffer), "%s=\"%s\"", var, val);
      return 1;
    }
  }
  return 0;
}


void mutt_delete_parameter (const char *attribute, PARAMETER **p)
{
  PARAMETER *q;

  for (q = *p; q; p = &q->next, q = q->next)
  {
    if (ascii_strcasecmp (attribute, q->attribute) == 0)
    {
      *p = q->next;
      q->next = NULL;
      mutt_free_parameter (&q);
      return;
    }
  }
}


static int parse_subscribe (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
{
  group_context_t *gc = NULL;

  do
  {
    mutt_extract_token (buf, s, 0);

    if (parse_group_context (&gc, buf, s, data, err) == -1)
      goto bail;

    mutt_remove_from_rx_list (&UnMailLists, buf->data);
    mutt_remove_from_rx_list (&UnSubscribedLists, buf->data);

    if (mutt_add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
      goto bail;
    if (mutt_add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
      goto bail;
    if (mutt_group_context_add_rx (gc, buf->data, REG_ICASE, err) != 0)
      goto bail;
  }
  while (MoreArgs (s));

  mutt_group_context_destroy (&gc);
  return 0;

 bail:
  mutt_group_context_destroy (&gc);
  return -1;
}


void mutt_free_header (HEADER **h)
{
  if(!h || !*h) return;
  mutt_free_envelope (&(*h)->env);
  mutt_free_body (&(*h)->content);
  FREE (&(*h)->maildir_flags);
  FREE (&(*h)->tree);
  FREE (&(*h)->path);
#ifdef MIXMASTER
  mutt_free_list (&(*h)->chain);
#endif
#if defined USE_POP || defined USE_IMAP
  FREE (&(*h)->data);
#endif
  FREE (h);		/* __FREE_CHECKED__ */
}


int mutt_is_text_part (BODY *b)
{
  int t = b->type;
  char *s = b->subtype;

  if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b))
    return 0;

  if (t == TYPETEXT)
    return 1;

  if (t == TYPEMESSAGE)
  {
    if (!ascii_strcasecmp ("delivery-status", s))
      return 1;
  }

  if ((WithCrypto & APPLICATION_PGP) && t == TYPEAPPLICATION)
  {
    if (!ascii_strcasecmp ("pgp-keys", s))
      return 1;
  }

  return 0;
}


static int v2_check_file(struct quota_handle *h, int type, int fmt)
{
	struct v2_disk_dqheader dqh;
	int file_magics[] = INITQMAGICS;
	int be_magic;

	if (fmt != QFMT_VFS_V1)
		return 0;

	if (!v2_read_header(h, &dqh))
		return 0;

	be_magic = ext2fs_be32_to_cpu((__force __be32)dqh.dqh_magic);
	if (be_magic == file_magics[type]) {
		log_err("Your quota file is stored in wrong endianity");
		return 0;
	}
	if (V2_VERSION != ext2fs_le32_to_cpu(dqh.dqh_version))
		return 0;
	return 1;
}


void quota_release_context(quota_ctx_t *qctx)
{
	errcode_t err;
	dict_t	*dict;
	enum quota_type	qtype;
	quota_ctx_t ctx;

	if (!qctx)
		return;

	ctx = *qctx;
	for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
		dict = ctx->quota_dict[qtype];
		ctx->quota_dict[qtype] = 0;
		if (dict) {
			dict_free_nodes(dict);
			free(dict);
		}
		if (ctx->quota_file[qtype]) {
			err = quota_file_close(ctx, ctx->quota_file[qtype]);
			if (err) {
				log_err("Cannot close quotafile: %s",
					strerror(errno));
				ext2fs_free_mem(&ctx->quota_file[qtype]);
			}
		}
	}
	*qctx = NULL;
	free(ctx);
}


void quota_data_add(quota_ctx_t qctx, struct ext2_inode_large *inode,
		    ext2_ino_t ino EXT2FS_ATTR((unused)),
		    qsize_t space)
{
	struct dquot	*dq;
	dict_t		*dict;
	enum quota_type	qtype;

	if (!qctx)
		return;

	log_debug("ADD_DATA: Inode: %u, UID/GID: %u/%u, space: %ld", ino,
			inode_uid(*inode),
			inode_gid(*inode), space);
	for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
		if (qtype == PRJQUOTA && !project_quota_valid(qctx))
			continue;
		dict = qctx->quota_dict[qtype];
		if (dict) {
			dq = get_dq(dict, get_qid(inode, qtype));
			if (dq)
				dq->dq_dqb.dqb_curspace += space;
		}
	}
}


static void remove_free_dqentry(struct quota_handle *h, dqbuf_t buf,
				unsigned int blk)
{
	dqbuf_t tmpbuf = getdqbuf();
	struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
	unsigned int nextblk = ext2fs_le32_to_cpu(dh->dqdh_next_free), prevblk =

		ext2fs_le32_to_cpu(dh->dqdh_prev_free);

	if (!tmpbuf)
		return;

	if (nextblk) {
		read_blk(h, nextblk, tmpbuf);
		((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free =
				dh->dqdh_prev_free;
		write_blk(h, nextblk, tmpbuf);
	}
	if (prevblk) {
		read_blk(h, prevblk, tmpbuf);
		((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_next_free =
				dh->dqdh_next_free;
		write_blk(h, prevblk, tmpbuf);
	} else {
		h->qh_info.u.v2_mdqi.dqi_qtree.dqi_free_entry = nextblk;
		mark_quotafile_info_dirty(h);
	}
	freedqbuf(tmpbuf);
	dh->dqdh_next_free = dh->dqdh_prev_free = ext2fs_cpu_to_le32(0);
	write_blk(h, blk, buf);	/* No matter whether write succeeds
				 * block is out of list */
}


static int get_free_dqblk(struct quota_handle *h)
{
	dqbuf_t buf = getdqbuf();
	struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
	struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
	int blk;

	if (!buf)
		return -ENOMEM;

	if (info->dqi_free_blk) {
		blk = info->dqi_free_blk;
		read_blk(h, blk, buf);
		info->dqi_free_blk = ext2fs_le32_to_cpu(dh->dqdh_next_free);
	} else {
		memset(buf, 0, QT_BLKSIZE);
		/* Assure block allocation... */
		if (write_blk(h, info->dqi_blocks, buf) < 0) {
			freedqbuf(buf);
			log_err("Cannot allocate new quota block "
				"(out of disk space).");
			return -ENOSPC;
		}
		blk = info->dqi_blocks++;
	}
	mark_quotafile_info_dirty(h);
	freedqbuf(buf);
	return blk;
}


static void print_dquot(const char *desc EXT2FS_ATTR((unused)),
			struct dquot *dq EXT2FS_ATTR((unused)))
{
}


static void print_pairing_nodes(FriBidiPairingNode *nodes)
{
  MSG ("Pairs: ");
  while (nodes)
    {
      MSG3 ("(%d, %d) ", nodes->open->pos, nodes->close->pos);
      nodes = nodes->next;
    }
  MSG ("\n");
}


static FriBidiRun *get_adjacent_run(FriBidiRun *list, fribidi_boolean forward, fribidi_boolean skip_neutral)
{
  FriBidiRun *ppp = forward ? list->next_isolate : list->prev_isolate;
  if (!ppp)
    return &sentinel;

  while (ppp)
    {
      FriBidiCharType ppp_type = RL_TYPE (ppp);

      if (ppp_type == FRIBIDI_TYPE_SENTINEL)
        break;

      /* Note that when sweeping forward we continue one run
         beyond the PDI to see what lies behind. When looking
         backwards, this is not necessary as the leading isolate
         run has already been assigned the resolved level. */
      if (ppp->isolate_level > list->isolate_level   /* <- How can this be true? */
          || (forward && ppp_type == FRIBIDI_TYPE_PDI)
          || (skip_neutral && !FRIBIDI_IS_STRONG(ppp_type)))
        {
          ppp = forward ? ppp->next_isolate : ppp->prev_isolate;
          if (!ppp)
            ppp = &sentinel;

          continue;
        }
      break;
    }

  return ppp;
}


static FriBidiRun *get_adjacent_run(FriBidiRun *list, fribidi_boolean forward, fribidi_boolean skip_neutral)
{
  FriBidiRun *ppp = forward ? list->next_isolate : list->prev_isolate;
  if (!ppp)
    return &sentinel;

  while (ppp)
    {
      FriBidiCharType ppp_type = RL_TYPE (ppp);

      if (ppp_type == _FRIBIDI_TYPE_SENTINEL)
        break;

      /* Note that when sweeping forward we continue one run
         beyond the PDI to see what lies behind. When looking
         backwards, this is not necessary as the leading isolate
         run has already been assigned the resolved level. */
      if (ppp->isolate_level > list->isolate_level   /* <- How can this be true? */
          || (forward && ppp_type == FRIBIDI_TYPE_PDI)
          || (skip_neutral && !FRIBIDI_IS_STRONG(ppp_type)))
        {
          ppp = forward ? ppp->next_isolate : ppp->prev_isolate;
          if (!ppp)
            ppp = &sentinel;

          continue;
        }
      break;
    }

  return ppp;
}


fetch_name_with_level(OnigCodePoint start_code, UChar** src, UChar* end,
                      UChar** rname_end, ScanEnv* env,
                      int* rback_num, int* rlevel, enum REF_NUM* num_type)
{
  int r, sign, exist_level;
  int digit_count;
  OnigCodePoint end_code;
  OnigCodePoint c = 0;
  OnigEncoding enc = env->enc;
  UChar *name_end;
  UChar *pnum_head;
  UChar *p = *src;
  PFETCH_READY;

  *rback_num = 0;
  exist_level = 0;
  *num_type = IS_NOT_NUM;
  sign = 1;
  pnum_head = *src;

  end_code = get_name_end_code_point(start_code);

  digit_count = 0;
  name_end = end;
  r = 0;
  if (PEND) {
    return ONIGERR_EMPTY_GROUP_NAME;
  }
  else {
    PFETCH(c);
    if (c == end_code)
      return ONIGERR_EMPTY_GROUP_NAME;

    if (IS_CODE_DIGIT_ASCII(enc, c)) {
      *num_type = IS_ABS_NUM;
      digit_count++;
    }
    else if (c == '-') {
      *num_type = IS_REL_NUM;
      sign = -1;
      pnum_head = p;
    }
    else if (c == '+') {
      *num_type = IS_REL_NUM;
      sign = 1;
      pnum_head = p;
    }
    else if (!ONIGENC_IS_CODE_WORD(enc, c)) {
      r = ONIGERR_INVALID_CHAR_IN_GROUP_NAME;
    }
  }

  while (!PEND) {
    name_end = p;
    PFETCH(c);
    if (c == end_code || c == ')' || c == '+' || c == '-') {
      if (*num_type != IS_NOT_NUM && digit_count == 0)
        r = ONIGERR_INVALID_GROUP_NAME;
      break;
    }

    if (*num_type != IS_NOT_NUM) {
      if (IS_CODE_DIGIT_ASCII(enc, c)) {
        digit_count++;
      }
      else {
        r = ONIGERR_INVALID_GROUP_NAME;
        *num_type = IS_NOT_NUM;
      }
    }
    else if (!ONIGENC_IS_CODE_WORD(enc, c)) {
      r = ONIGERR_INVALID_CHAR_IN_GROUP_NAME;
    }
  }

  if (r == 0 && c != end_code) {
    if (c == '+' || c == '-') {
      int level;
      int flag = (c == '-' ? -1 : 1);

      if (PEND) {
        r = ONIGERR_INVALID_CHAR_IN_GROUP_NAME;
        goto end;
      }
      PFETCH(c);
      if (! IS_CODE_DIGIT_ASCII(enc, c)) goto err;
      PUNFETCH;
      level = onig_scan_unsigned_number(&p, end, enc);
      if (level < 0) return ONIGERR_TOO_BIG_NUMBER;
      *rlevel = (level * flag);
      exist_level = 1;

      if (!PEND) {
        PFETCH(c);
        if (c == end_code)
          goto end;
      }
    }

  err:
    name_end = end;
  err2:
    r = ONIGERR_INVALID_GROUP_NAME;
  }

 end:
  if (r == 0) {
    if (*num_type != IS_NOT_NUM) {
      *rback_num = onig_scan_unsigned_number(&pnum_head, name_end, enc);
      if (*rback_num < 0) return ONIGERR_TOO_BIG_NUMBER;
      else if (*rback_num == 0) {
        if (*num_type == IS_REL_NUM)
          goto err2;
      }

      *rback_num *= sign;
    }

    *rname_end = name_end;
    *src = p;
    return (exist_level ? 1 : 0);
  }
  else {
    onig_scan_env_set_error_string(env, r, *src, name_end);
    return r;
  }
}


node_new_backref_checker(int back_num, int* backrefs, int by_name,
#ifdef USE_BACKREF_WITH_LEVEL
                         int exist_level, int nest_level,
#endif
                         ScanEnv* env)
{
  Node* node;

  node = node_new_backref(back_num, backrefs, by_name,
#ifdef USE_BACKREF_WITH_LEVEL
                          exist_level, nest_level,
#endif
                          env);
  CHECK_NULL_RETURN(node);

  NODE_STATUS_ADD(node, CHECKER);
  return node;
}


node_new_call(UChar* name, UChar* name_end, int gnum, int by_number)
{
  Node* node = node_new();
  CHECK_NULL_RETURN(node);

  NODE_SET_TYPE(node, NODE_CALL);
  CALL_(node)->by_number   = by_number;
  CALL_(node)->name        = name;
  CALL_(node)->name_end    = name_end;
  CALL_(node)->group_num   = gnum;
  CALL_(node)->entry_count = 1;
  return node;
}


onig_reg_callout_list_at(regex_t* reg, int num)
{
  RegexExt* ext = reg->extp;
  CHECK_NULL_RETURN(ext);

  if (num <= 0 || num > ext->callout_num)
    return 0;

  num--;
  return ext->callout_list + num;
}


onig_get_callout_num_by_tag(regex_t* reg,
                            const UChar* tag, const UChar* tag_end)
{
  int r;
  RegexExt* ext;
  CalloutTagVal e;

  ext = reg->extp;
  if (IS_NULL(ext) || IS_NULL(ext->tag_table))
    return ONIGERR_INVALID_CALLOUT_TAG_NAME;

  r = onig_st_lookup_strend(ext->tag_table, tag, tag_end,
                            (HashDataType* )((void* )(&e)));
  if (r == 0) return ONIGERR_INVALID_CALLOUT_TAG_NAME;
  return (int )e;
}


set_quantifier(Node* qnode, Node* target, int group, ScanEnv* env)
{
  QuantNode* qn;

  qn = QUANT_(qnode);
  if (qn->lower == 1 && qn->upper == 1)
    return 1;

  switch (NODE_TYPE(target)) {
  case NODE_STRING:
    if (group == 0) {
      if (str_node_can_be_split(target, env->enc)) {
        Node* n = str_node_split_last_char(target, env->enc);
        if (IS_NOT_NULL(n)) {
          NODE_BODY(qnode) = n;
          return 2;
        }
      }
    }
    break;

  case NODE_QUANT:
    { /* check redundant double repeat. */
      /* verbose warn (?:.?)? etc... but not warn (.?)? etc... */
      QuantNode* qnt   = QUANT_(target);
      int nestq_num   = quantifier_type_num(qn);
      int targetq_num = quantifier_type_num(qnt);

#ifdef USE_WARNING_REDUNDANT_NESTED_REPEAT_OPERATOR
      if (targetq_num >= 0 && nestq_num >= 0 &&
          IS_SYNTAX_BV(env->syntax, ONIG_SYN_WARN_REDUNDANT_NESTED_REPEAT)) {
        UChar buf[WARN_BUFSIZE];

        switch(ReduceTypeTable[targetq_num][nestq_num]) {
        case RQ_ASIS:
          break;

        case RQ_DEL:
          if (onig_verb_warn != onig_null_warn) {
            onig_snprintf_with_pattern(buf, WARN_BUFSIZE, env->enc,
                                  env->pattern, env->pattern_end,
                                  (UChar* )"redundant nested repeat operator");
            (*onig_verb_warn)((char* )buf);
          }
          goto warn_exit;
          break;

        default:
          if (onig_verb_warn != onig_null_warn) {
            onig_snprintf_with_pattern(buf, WARN_BUFSIZE, env->enc,
                                       env->pattern, env->pattern_end,
            (UChar* )"nested repeat operator %s and %s was replaced with '%s'",
            PopularQStr[targetq_num], PopularQStr[nestq_num],
            ReduceQStr[ReduceTypeTable[targetq_num][nestq_num]]);
            (*onig_verb_warn)((char* )buf);
          }
          goto warn_exit;
          break;
        }
      }

    warn_exit:
#endif
      if (targetq_num >= 0 && nestq_num < 0) {
        if (targetq_num == 1 || targetq_num == 2) { /* * or + */
          /* (?:a*){n,m}, (?:a+){n,m} => (?:a*){n,n}, (?:a+){n,n} */
          if (! IS_INFINITE_REPEAT(qn->upper) && qn->upper > 1 && qn->greedy) {
            qn->upper = (qn->lower == 0 ? 1 : qn->lower);
          }
        }
      }
      else {
        NODE_BODY(qnode) = target;
        onig_reduce_nested_quantifier(qnode, target);
        goto q_exit;
      }
    }
    break;

  default:
    break;
  }

  NODE_BODY(qnode) = target;
 q_exit:
  return 0;
}


or_cclass(CClassNode* dest, CClassNode* cc, OnigEncoding enc)
{
  int r, not1, not2;
  BBuf *buf1, *buf2, *pbuf;
  BitSetRef bsr1, bsr2;
  BitSet bs1, bs2;

  not1 = IS_NCCLASS_NOT(dest);
  bsr1 = dest->bs;
  buf1 = dest->mbuf;
  not2 = IS_NCCLASS_NOT(cc);
  bsr2 = cc->bs;
  buf2 = cc->mbuf;

  if (not1 != 0) {
    bitset_invert_to(bsr1, bs1);
    bsr1 = bs1;
  }
  if (not2 != 0) {
    bitset_invert_to(bsr2, bs2);
    bsr2 = bs2;
  }
  bitset_or(bsr1, bsr2);
  if (bsr1 != dest->bs) {
    bitset_copy(dest->bs, bsr1);
  }
  if (not1 != 0) {
    bitset_invert(dest->bs);
  }

  if (! ONIGENC_IS_SINGLEBYTE(enc)) {
    if (not1 != 0 && not2 != 0) {
      r = and_code_range_buf(buf1, 0, buf2, 0, &pbuf);
    }
    else {
      r = or_code_range_buf(enc, buf1, not1, buf2, not2, &pbuf);
      if (r == 0 && not1 != 0) {
        BBuf *tbuf;
        r = not_code_range_buf(enc, pbuf, &tbuf);
        if (r != 0) {
          bbuf_free(pbuf);
          return r;
        }
        bbuf_free(pbuf);
        pbuf = tbuf;
      }
    }
    if (r != 0) return r;

    dest->mbuf = pbuf;
    bbuf_free(buf1);
    return r;
  }
  else
    return 0;
}


callout_name_table_hash(st_callout_name_key* x)
{
  UChar *p;
  int val = 0;

  p = x->s;
  while (p < x->end) {
    val = val * 997 + (int )*p++;
  }

  /* use intptr_t for escape warning in Windows */
  return val + (val >> 5) + ((intptr_t )x->enc & 0xffff) + x->type;
}


node_new_no_newline(Node** node, ScanEnv* env)
{
  Node* n;

  n = node_new_anychar_with_fixed_option(ONIG_OPTION_NONE);
  CHECK_NULL_RETURN_MEMERR(n);
  *node = n;
  return 0;
}


scan_env_add_mem_entry(ScanEnv* env)
{
  int i, need, alloc;
  MemEnv* p;

  need = env->num_mem + 1;
  if (need > MaxCaptureNum && MaxCaptureNum != 0)
    return ONIGERR_TOO_MANY_CAPTURES;

  if (need >= SCANENV_MEMENV_SIZE) {
    if (env->mem_alloc <= need) {
      if (IS_NULL(env->mem_env_dynamic)) {
        alloc = INIT_SCANENV_MEMENV_ALLOC_SIZE;
        p = (MemEnv* )xmalloc(sizeof(MemEnv) * alloc);
        CHECK_NULL_RETURN_MEMERR(p);
        xmemcpy(p, env->mem_env_static, sizeof(env->mem_env_static));
      }
      else {
        alloc = env->mem_alloc * 2;
        p = (MemEnv* )xrealloc(env->mem_env_dynamic, sizeof(MemEnv) * alloc);
        CHECK_NULL_RETURN_MEMERR(p);
      }

      for (i = env->num_mem + 1; i < alloc; i++) {
        p[i].node = NULL_NODE;
#if 0
        p[i].in   = 0;
        p[i].recursion = 0;
#endif
      }

      env->mem_env_dynamic = p;
      env->mem_alloc = alloc;
    }
  }

  env->num_mem++;
  return env->num_mem;
}


onig_noname_group_capture_is_active(regex_t* reg)
{
  if (ONIG_IS_OPTION_ON(reg->options, ONIG_OPTION_DONT_CAPTURE_GROUP))
    return 0;

  if (onig_number_of_names(reg) > 0 &&
      IS_SYNTAX_BV(reg->syntax, ONIG_SYN_CAPTURE_ONLY_NAMED_GROUP) &&
      !ONIG_IS_OPTION_ON(reg->options, ONIG_OPTION_CAPTURE_GROUP)) {
    return 0;
  }

  return 1;
}


onig_get_callout_start_func(regex_t* reg, int callout_num)
{
  /* If used for callouts of contents, return 0. */
  CalloutListEntry* e;

  e = onig_reg_callout_list_at(reg, callout_num);
  CHECK_NULL_RETURN(e);
  return e->start_func;
}


fetch_token_in_cc(PToken* tok, UChar** src, UChar* end, ScanEnv* env)
{
  int num;
  OnigCodePoint c, c2;
  OnigSyntaxType* syn = env->syntax;
  OnigEncoding enc = env->enc;
  UChar* prev;
  UChar* p = *src;
  PFETCH_READY;

  if (PEND) {
    tok->type = TK_EOT;
    return tok->type;
  }

  PFETCH(c);
  tok->type = TK_CHAR;
  tok->base = 0;
  tok->u.c  = c;
  tok->escaped = 0;

  if (c == ']') {
    tok->type = TK_CC_CLOSE;
  }
  else if (c == '-') {
    tok->type = TK_CC_RANGE;
  }
  else if (c == MC_ESC(syn)) {
    if (! IS_SYNTAX_BV(syn, ONIG_SYN_BACKSLASH_ESCAPE_IN_CC))
      goto end;

    if (PEND) return ONIGERR_END_PATTERN_AT_ESCAPE;

    PFETCH(c);
    tok->escaped = 1;
    tok->u.c = c;
    switch (c) {
    case 'w':
      tok->type = TK_CHAR_TYPE;
      tok->u.prop.ctype = ONIGENC_CTYPE_WORD;
      tok->u.prop.not   = 0;
      break;
    case 'W':
      tok->type = TK_CHAR_TYPE;
      tok->u.prop.ctype = ONIGENC_CTYPE_WORD;
      tok->u.prop.not   = 1;
      break;
    case 'd':
      tok->type = TK_CHAR_TYPE;
      tok->u.prop.ctype = ONIGENC_CTYPE_DIGIT;
      tok->u.prop.not   = 0;
      break;
    case 'D':
      tok->type = TK_CHAR_TYPE;
      tok->u.prop.ctype = ONIGENC_CTYPE_DIGIT;
      tok->u.prop.not   = 1;
      break;
    case 's':
      tok->type = TK_CHAR_TYPE;
      tok->u.prop.ctype = ONIGENC_CTYPE_SPACE;
      tok->u.prop.not   = 0;
      break;
    case 'S':
      tok->type = TK_CHAR_TYPE;
      tok->u.prop.ctype = ONIGENC_CTYPE_SPACE;
      tok->u.prop.not   = 1;
      break;
    case 'h':
      if (! IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_H_XDIGIT)) break;
      tok->type = TK_CHAR_TYPE;
      tok->u.prop.ctype = ONIGENC_CTYPE_XDIGIT;
      tok->u.prop.not   = 0;
      break;
    case 'H':
      if (! IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_H_XDIGIT)) break;
      tok->type = TK_CHAR_TYPE;
      tok->u.prop.ctype = ONIGENC_CTYPE_XDIGIT;
      tok->u.prop.not   = 1;
      break;

    case 'p':
    case 'P':
      if (PEND) break;

      c2 = PPEEK;
      if (c2 == '{' &&
          IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_P_BRACE_CHAR_PROPERTY)) {
        PINC;
        tok->type = TK_CHAR_PROPERTY;
        tok->u.prop.not = c == 'P';

        if (!PEND && IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_P_BRACE_CIRCUMFLEX_NOT)) {
          PFETCH(c2);
          if (c2 == '^') {
            tok->u.prop.not = tok->u.prop.not == 0;
          }
          else
            PUNFETCH;
        }
      }
      break;

    case 'o':
      if (PEND) break;

      prev = p;
      if (PPEEK_IS('{') && IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_O_BRACE_OCTAL)) {
        PINC;
        num = scan_unsigned_octal_number(&p, end, 11, enc);
        if (num < 0) return ONIGERR_TOO_BIG_WIDE_CHAR_VALUE;
        if (!PEND) {
          c2 = PPEEK;
          if (IS_CODE_DIGIT_ASCII(enc, c2))
            return ONIGERR_TOO_LONG_WIDE_CHAR_VALUE;
        }

        if (p > prev + enclen(enc, prev) && !PEND && (PPEEK_IS('}'))) {
          PINC;
          tok->type   = TK_CODE_POINT;
          tok->base   = 8;
          tok->u.code = (OnigCodePoint )num;
        }
        else {
          /* can't read nothing or invalid format */
          p = prev;
        }
      }
      break;

    case 'x':
      if (PEND) break;

      prev = p;
      if (PPEEK_IS('{') && IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_X_BRACE_HEX8)) {
        PINC;
        num = scan_unsigned_hexadecimal_number(&p, end, 0, 8, enc);
        if (num < 0) {
          if (num == ONIGERR_TOO_BIG_NUMBER)
            return ONIGERR_TOO_BIG_WIDE_CHAR_VALUE;
          else
            return num;
        }
        if (!PEND) {
          c2 = PPEEK;
          if (IS_CODE_XDIGIT_ASCII(enc, c2))
            return ONIGERR_TOO_LONG_WIDE_CHAR_VALUE;
        }

        if (p > prev + enclen(enc, prev) && !PEND && (PPEEK_IS('}'))) {
          PINC;
          tok->type   = TK_CODE_POINT;
          tok->base   = 16;
          tok->u.code = (OnigCodePoint )num;
        }
        else {
          /* can't read nothing or invalid format */
          p = prev;
        }
      }
      else if (IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_X_HEX2)) {
        num = scan_unsigned_hexadecimal_number(&p, end, 0, 2, enc);
        if (num < 0) return num;
        if (p == prev) {  /* can't read nothing. */
          num = 0; /* but, it's not error */
        }
        tok->type = TK_RAW_BYTE;
        tok->base = 16;
        tok->u.c  = num;
      }
      break;

    case 'u':
      if (PEND) break;

      prev = p;
      if (IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_U_HEX4)) {
        num = scan_unsigned_hexadecimal_number(&p, end, 4, 4, enc);
        if (num < 0) return num;
        if (p == prev) {  /* can't read nothing. */
          num = 0; /* but, it's not error */
        }
        tok->type   = TK_CODE_POINT;
        tok->base   = 16;
        tok->u.code = (OnigCodePoint )num;
      }
      break;

    case '0':
    case '1': case '2': case '3': case '4': case '5': case '6': case '7':
      if (IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_OCTAL3)) {
        PUNFETCH;
        prev = p;
        num = scan_unsigned_octal_number(&p, end, 3, enc);
        if (num < 0 || num >= 256) return ONIGERR_TOO_BIG_NUMBER;
        if (p == prev) {  /* can't read nothing. */
          num = 0; /* but, it's not error */
        }
        tok->type = TK_RAW_BYTE;
        tok->base = 8;
        tok->u.c  = num;
      }
      break;

    default:
      PUNFETCH;
      num = fetch_escaped_value(&p, end, env, &c2);
      if (num < 0) return num;
      if (tok->u.c != c2) {
        tok->u.code = c2;
        tok->type   = TK_CODE_POINT;
      }
      break;
    }
  }
  else if (c == '[') {
    if (IS_SYNTAX_OP(syn, ONIG_SYN_OP_POSIX_BRACKET) && (PPEEK_IS(':'))) {
      OnigCodePoint send[] = { (OnigCodePoint )':', (OnigCodePoint )']' };
      tok->backp = p; /* point at '[' is read */
      PINC;
      if (str_exist_check_with_esc(send, 2, p, end,
                                   (OnigCodePoint )']', enc, syn)) {
        tok->type = TK_POSIX_BRACKET_OPEN;
      }
      else {
        PUNFETCH;
        goto cc_in_cc;
      }
    }
    else {
    cc_in_cc:
      if (IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_CCLASS_SET_OP)) {
        tok->type = TK_CC_CC_OPEN;
      }
      else {
        CC_ESC_WARN(env, (UChar* )"[");
      }
    }
  }
  else if (c == '&') {
    if (IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_CCLASS_SET_OP) &&
        !PEND && (PPEEK_IS('&'))) {
      PINC;
      tok->type = TK_CC_AND;
    }
  }

 end:
  *src = p;
  return tok->type;
}


make_callout_func_list(CalloutNameListType** rs, int init_size)
{
  CalloutNameListType* s;
  CalloutNameListEntry* v;

  *rs = 0;

  s = xmalloc(sizeof(*s));
  if (IS_NULL(s)) return ONIGERR_MEMORY;

  v = (CalloutNameListEntry* )xmalloc(sizeof(CalloutNameListEntry) * init_size);
  if (IS_NULL(v)) {
    xfree(s);
    return ONIGERR_MEMORY;
  }

  s->n = 0;
  s->alloc = init_size;
  s->v = v;

  *rs = s;
  return ONIG_NORMAL;
}


onig_callout_tag_table_free(void* table)
{
  CalloutTagTable* t = (CalloutTagTable* )table;

  if (IS_NOT_NULL(t)) {
    int r = callout_tag_table_clear(t);
    if (r != 0) return r;

    onig_st_free_table(t);
  }

  return 0;
}


node_new_str_raw_char(UChar c)
{
  int i;
  UChar p[1];
  Node* node;

  p[0] = c;
  node = node_new_str_raw(p, p + 1);

  /* clear buf tail */
  for (i = 1; i < NODE_STRING_BUF_SIZE; i++)
    STR_(node)->buf[i] = '\0';

  return node;
}