cpTag(TIFF* in, TIFF* out, uint16 tag, uint16 count, TIFFDataType type)
{
	switch (type) {
	case TIFF_SHORT:
		if (count == 1) {
			uint16 shortv;
			CopyField(tag, shortv);
		} else if (count == 2) {
			uint16 shortv1, shortv2;
			CopyField2(tag, shortv1, shortv2);
		} else if (count == 4) {
			uint16 *tr, *tg, *tb, *ta;
			CopyField4(tag, tr, tg, tb, ta);
		} else if (count == (uint16) -1) {
			uint16 shortv1;
			uint16* shortav;
			CopyField2(tag, shortv1, shortav);
		}
		break;
	case TIFF_LONG:
		{ uint32 longv;
		  CopyField(tag, longv);
		}
		break;
	case TIFF_RATIONAL:
		if (count == 1) {
			float floatv;
			CopyField(tag, floatv);
		} else if (count == (uint16) -1) {
			float* floatav;
			CopyField(tag, floatav);
		}
		break;
	case TIFF_ASCII:
		{ char* stringv;
		  CopyField(tag, stringv);
		}
		break;
	case TIFF_DOUBLE:
		if (count == 1) {
			double doublev;
			CopyField(tag, doublev);
		} else if (count == (uint16) -1) {
			double* doubleav;
			CopyField(tag, doubleav);
		}
		break;
          default:
                TIFFError(TIFFFileName(in),
                          "Data type %d is not supported, tag %d skipped",
                          tag, type);
	}
}


computeOutputPixelOffsets (struct crop_mask *crop, struct image_data *image,
                           struct pagedef *page, struct pageseg *sections,
                           struct dump_opts* dump)
  {
  double scale;
  double pwidth, plength;          /* Output page width and length in user units*/
  uint32 iwidth, ilength;          /* Input image width and length in pixels*/
  uint32 owidth, olength;          /* Output image width and length in pixels*/
  uint32 orows, ocols;             /* rows and cols for output */
  uint32 hmargin, vmargin;         /* Horizontal and vertical margins */
  uint32 x1, x2, y1, y2, line_bytes;
  /* unsigned int orientation; */
  uint32 i, j, k;

  scale = 1.0;
  if (page->res_unit == RESUNIT_NONE)
    page->res_unit = image->res_unit;

  switch (image->res_unit) {
    case RESUNIT_CENTIMETER:
         if (page->res_unit == RESUNIT_INCH)
	   scale = 1.0/2.54;
	 break;
    case RESUNIT_INCH:
	 if (page->res_unit == RESUNIT_CENTIMETER)
	     scale = 2.54;
	 break;
    case RESUNIT_NONE: /* Dimensions in pixels */
    default:
    break;
    }

  /* get width, height, resolutions of input image selection */
  if (crop->combined_width > 0)
    iwidth = crop->combined_width;
  else
    iwidth = image->width;
  if (crop->combined_length > 0)
    ilength = crop->combined_length;
  else
    ilength = image->length;

  if (page->hres <= 1.0)
    page->hres = image->xres;
  if (page->vres <= 1.0)
    page->vres = image->yres;

  if ((page->hres < 1.0) || (page->vres < 1.0))
    {
    TIFFError("computeOutputPixelOffsets",
    "Invalid horizontal or vertical resolution specified or read from input image");
    return (1);
    }

  /* If no page sizes are being specified, we just use the input image size to
   * calculate maximum margins that can be taken from image.
   */
  if (page->width <= 0)
    pwidth = iwidth;
  else
    pwidth = page->width;

  if (page->length <= 0)
    plength = ilength;
  else
    plength = page->length;

  if (dump->debug)
    {
    TIFFError("", "Page size: %s, Vres: %3.2f, Hres: %3.2f, "
                   "Hmargin: %3.2f, Vmargin: %3.2f",
	     page->name, page->vres, page->hres,
             page->hmargin, page->vmargin);
    TIFFError("", "Res_unit: %d, Scale: %3.2f, Page width: %3.2f, length: %3.2f",
           page->res_unit, scale, pwidth, plength);
    }

  /* compute margins at specified unit and resolution */
  if (page->mode & PAGE_MODE_MARGINS)
    {
    if (page->res_unit == RESUNIT_INCH || page->res_unit == RESUNIT_CENTIMETER)
      { /* inches or centimeters specified */
      hmargin = (uint32)(page->hmargin * scale * page->hres * ((image->bps + 7)/ 8));
      vmargin = (uint32)(page->vmargin * scale * page->vres * ((image->bps + 7)/ 8));
      }
    else
      { /* Otherwise user has specified pixels as reference unit */
      hmargin = (uint32)(page->hmargin * scale * ((image->bps + 7)/ 8));
      vmargin = (uint32)(page->vmargin * scale * ((image->bps + 7)/ 8));
      }

    if ((hmargin * 2.0) > (pwidth * page->hres))
      {
      TIFFError("computeOutputPixelOffsets",
                "Combined left and right margins exceed page width");
      hmargin = (uint32) 0;
      return (-1);
      }
    if ((vmargin * 2.0) > (plength * page->vres))
      {
      TIFFError("computeOutputPixelOffsets",
                "Combined top and bottom margins exceed page length");
      vmargin = (uint32) 0;
      return (-1);
      }
    }
  else
    {
    hmargin = 0;
    vmargin = 0;
    }

  if (page->mode & PAGE_MODE_ROWSCOLS )
    {
    /* Maybe someday but not for now */
    if (page->mode & PAGE_MODE_MARGINS)
      TIFFError("computeOutputPixelOffsets",
      "Output margins cannot be specified with rows and columns");

    owidth  = TIFFhowmany(iwidth, page->cols);
    olength = TIFFhowmany(ilength, page->rows);
    }
  else
    {
    if (page->mode & PAGE_MODE_PAPERSIZE )
      {
      owidth  = (uint32)((pwidth * page->hres) - (hmargin * 2));
      olength = (uint32)((plength * page->vres) - (vmargin * 2));
      }
    else
      {
      owidth = (uint32)(iwidth - (hmargin * 2 * page->hres));
      olength = (uint32)(ilength - (vmargin * 2 * page->vres));
      }
    }

  if (owidth > iwidth)
    owidth = iwidth;
  if (olength > ilength)
    olength = ilength;

  /* Compute the number of pages required for Portrait or Landscape */
  switch (page->orient)
    {
    case ORIENTATION_NONE:
    case ORIENTATION_PORTRAIT:
         ocols = TIFFhowmany(iwidth, owidth);
         orows = TIFFhowmany(ilength, olength);
         /* orientation = ORIENTATION_PORTRAIT; */
         break;

    case ORIENTATION_LANDSCAPE:
         ocols = TIFFhowmany(iwidth, olength);
         orows = TIFFhowmany(ilength, owidth);
         x1 = olength;
         olength = owidth;
         owidth = x1;
         /* orientation = ORIENTATION_LANDSCAPE; */
         break;

    case ORIENTATION_AUTO:
    default:
         x1 = TIFFhowmany(iwidth, owidth);
         x2 = TIFFhowmany(ilength, olength);
         y1 = TIFFhowmany(iwidth, olength);
         y2 = TIFFhowmany(ilength, owidth);

         if ( (x1 * x2) < (y1 * y2))
           { /* Portrait */
           ocols = x1;
           orows = x2;
           /* orientation = ORIENTATION_PORTRAIT; */
	   }
         else
           { /* Landscape */
           ocols = y1;
           orows = y2;
           x1 = olength;
           olength = owidth;
           owidth = x1;
           /* orientation = ORIENTATION_LANDSCAPE; */
           }
    }

  if (ocols < 1)
    ocols = 1;
  if (orows < 1)
    orows = 1;

  /* If user did not specify rows and cols, set them from calcuation */
  if (page->rows < 1)
    page->rows = orows;
  if (page->cols < 1)
    page->cols = ocols;

  line_bytes = TIFFhowmany8(owidth * image->bps) * image->spp;

  if ((page->rows * page->cols) > MAX_SECTIONS)
   {
   TIFFError("computeOutputPixelOffsets",
	     "Rows and Columns exceed maximum sections\nIncrease resolution or reduce sections");
   return (-1);
   }

  /* build the list of offsets for each output section */
  for (k = 0, i = 0 && k <= MAX_SECTIONS; i < orows; i++)
    {
    y1 = (uint32)(olength * i);
    y2 = (uint32)(olength * (i +  1) - 1);
    if (y2 >= ilength)
      y2 = ilength - 1;
    for (j = 0; j < ocols; j++, k++)
      {
      x1 = (uint32)(owidth * j);
      x2 = (uint32)(owidth * (j + 1) - 1);
      if (x2 >= iwidth)
        x2 = iwidth - 1;
      sections[k].x1 = x1;
      sections[k].x2 = x2;
      sections[k].y1 = y1;
      sections[k].y2 = y2;
      sections[k].buffsize = line_bytes * olength;
      sections[k].position = k + 1;
      sections[k].total = orows * ocols;
      }
    }
  return (0);
  } /* end computeOutputPixelOffsets */


static int dump_byte (FILE *dumpfile, int format, char *dump_tag, unsigned char data)
  {
  int j, k;
  char  dump_array[10];
  unsigned char bitset;

  if (dumpfile == NULL)
    {
    TIFFError ("", "Invalid FILE pointer for dump file");
    return (1);
    }

  if (format == DUMP_TEXT)
    {
    fprintf (dumpfile," %s  ", dump_tag);
    for (j = 0, k = 7; j < 8; j++, k--)
      {
      bitset = data & (((unsigned char)1 << k)) ? 1 : 0;
      sprintf(&dump_array[j], (bitset) ? "1" : "0");
      }
    dump_array[8] = '\0';
    fprintf (dumpfile," %s\n", dump_array);
    }
  else
    {
    if ((fwrite (&data, 1, 1, dumpfile)) != 1)
      {
      TIFFError ("", "Unable to write binary data to dump file");
      return (1);
      }
    }

  return (0);
  }


createCroppedImage(struct image_data *image, struct crop_mask *crop,
                   unsigned char **read_buff_ptr, unsigned char **crop_buff_ptr)
  {
  tsize_t   cropsize;
  unsigned  char *read_buff = NULL;
  unsigned  char *crop_buff = NULL;
  unsigned  char *new_buff  = NULL;
  static    tsize_t  prev_cropsize = 0;

  read_buff = *read_buff_ptr;

  /* process full image, no crop buffer needed */
  crop_buff = read_buff;
  *crop_buff_ptr = read_buff;
  crop->combined_width = image->width;
  crop->combined_length = image->length;

  cropsize = crop->bufftotal;
  crop_buff = *crop_buff_ptr;
  if (!crop_buff)
    {
    crop_buff = (unsigned char *)_TIFFmalloc(cropsize);
    *crop_buff_ptr = crop_buff;
    _TIFFmemset(crop_buff, 0, cropsize);
    prev_cropsize = cropsize;
    }
  else
    {
    if (prev_cropsize < cropsize)
      {
      new_buff = _TIFFrealloc(crop_buff, cropsize);
      if (!new_buff)
        {
	free (crop_buff);
        crop_buff = (unsigned char *)_TIFFmalloc(cropsize);
        }
      else
        crop_buff = new_buff;
      _TIFFmemset(crop_buff, 0, cropsize);
      }
    }

  if (!crop_buff)
    {
    TIFFError("createCroppedImage", "Unable to allocate/reallocate crop buffer");
    return (-1);
    }
  *crop_buff_ptr = crop_buff;

  if (crop->crop_mode & CROP_INVERT)
    {
    switch (crop->photometric)
      {
      /* Just change the interpretation */
      case PHOTOMETRIC_MINISWHITE:
      case PHOTOMETRIC_MINISBLACK:
	   image->photometric = crop->photometric;
	   break;
      case INVERT_DATA_ONLY:
      case INVERT_DATA_AND_TAG:
           if (invertImage(image->photometric, image->spp, image->bps,
                           crop->combined_width, crop->combined_length, crop_buff))
             {
             TIFFError("createCroppedImage",
                       "Failed to invert colorspace for image or cropped selection");
             return (-1);
             }
           if (crop->photometric == INVERT_DATA_AND_TAG)
             {
             switch (image->photometric)
               {
               case PHOTOMETRIC_MINISWHITE:
 	            image->photometric = PHOTOMETRIC_MINISBLACK;
	            break;
               case PHOTOMETRIC_MINISBLACK:
 	            image->photometric = PHOTOMETRIC_MINISWHITE;
	            break;
               default:
	            break;
	       }
	     }
           break;
      default: break;
      }
    }

  if (crop->crop_mode & CROP_MIRROR)
    {
    if (mirrorImage(image->spp, image->bps, crop->mirror,
                    crop->combined_width, crop->combined_length, crop_buff))
      {
      TIFFError("createCroppedImage", "Failed to mirror image or cropped selection %s",
	       (crop->rotation == MIRROR_HORIZ) ? "horizontally" : "vertically");
      return (-1);
      }
    }

  if (crop->crop_mode & CROP_ROTATE) /* rotate should be last as it can reallocate the buffer */
    {
    if (rotateImage(crop->rotation, image, &crop->combined_width,
                    &crop->combined_length, crop_buff_ptr))
      {
      TIFFError("createCroppedImage",
                "Failed to rotate image or cropped selection by %d degrees", crop->rotation);
      return (-1);
      }
    }

  if (crop_buff == read_buff) /* we used the read buffer for the crop buffer */
    *read_buff_ptr = NULL;    /* so we don't try to free it later */

  return (0);
  } /* end createCroppedImage */


rotateContigSamples32bits(uint16 rotation, uint16 spp, uint16 bps, uint32 width,
                          uint32 length,   uint32 col, uint8 *src, uint8 *dst)
  {
  int    ready_bits = 0 /*, shift_width = 0 */;
  /* int    bytes_per_sample, bytes_per_pixel; */
  uint32 row, rowsize, bit_offset;
  uint32 src_byte, src_bit;
  uint32 longbuff1 = 0, longbuff2 = 0;
  uint64 maskbits = 0, matchbits = 0;
  uint64 buff1 = 0, buff2 = 0, buff3 = 0;
  uint8  bytebuff1 = 0, bytebuff2 = 0, bytebuff3 = 0, bytebuff4 = 0;
  uint8   *next;
  tsample_t sample;

  if ((src == NULL) || (dst == NULL))
    {
    TIFFError("rotateContigSamples24bits","Invalid src or destination buffer");
    return (1);
    }

  /* bytes_per_sample = (bps + 7) / 8; */
  /* bytes_per_pixel  = ((bps * spp) + 7) / 8; */
  /* if (bytes_per_pixel < (bytes_per_sample + 1)) */
  /*   shift_width = bytes_per_pixel; */
  /* else */
  /*   shift_width = bytes_per_sample + 1; */

  rowsize = ((bps * spp * width) + 7) / 8;
  ready_bits = 0;
  maskbits =  (uint64)-1 >> (64 - bps);
  buff1 = buff2 = 0;
  for (row = 0; row < length; row++)
    {
    bit_offset = col * bps * spp;
    for (sample = 0; sample < spp; sample++)
      {
      if (sample == 0)
        {
        src_byte = bit_offset / 8;
        src_bit  = bit_offset % 8;
        }
      else
        {
        src_byte = (bit_offset + (sample * bps)) / 8;
        src_bit  = (bit_offset + (sample * bps)) % 8;
        }

      switch (rotation)
	{
        case  90: next = src + src_byte - (row * rowsize);
                  break;
        case 270: next = src + src_byte + (row * rowsize);
	          break;
	default:  TIFFError("rotateContigSamples8bits", "Invalid rotation %d", rotation);
                  return (1);
        }
      matchbits = maskbits << (64 - src_bit - bps);
      if (little_endian)
        {
	longbuff1 = (next[0] << 24) | (next[1] << 16) | (next[2] << 8) | next[3];
        longbuff2 = longbuff1;
        }
      else
        {
	longbuff1 = (next[3] << 24) | (next[2] << 16) | (next[1] << 8) | next[0];
        longbuff2 = longbuff1;
	}

      buff3 = ((uint64)longbuff1 << 32) | longbuff2;
      buff1 = (buff3 & matchbits) << (src_bit);

      if (ready_bits < 32)
        { /* add another bps bits to the buffer */
        bytebuff1 = bytebuff2 = bytebuff3 = bytebuff4 = 0;
        buff2 = (buff2 | (buff1 >> ready_bits));
        }
      else /* If we have a full buffer's worth, write it out */
        {
        bytebuff1 = (buff2 >> 56);
        *dst++ = bytebuff1;
        bytebuff2 = (buff2 >> 48);
        *dst++ = bytebuff2;
        bytebuff3 = (buff2 >> 40);
        *dst++ = bytebuff3;
        bytebuff4 = (buff2 >> 32);
        *dst++ = bytebuff4;
        ready_bits -= 32;

        /* shift in new bits */
        buff2 = ((buff2 << 32) | (buff1 >> ready_bits));
        }
      ready_bits += bps;
      }
    }
  while (ready_bits > 0)
    {
    bytebuff1 = (buff2 >> 56);
    *dst++ = bytebuff1;
    buff2 = (buff2 << 8);
    ready_bits -= 8;
    }

  return (0);
  } /* end rotateContigSamples32bits */


main(int argc, char* argv[])
  {

#if !HAVE_DECL_OPTARG
  extern int optind;
#endif
  uint16 defconfig = (uint16) -1;
  uint16 deffillorder = 0;
  uint32 deftilewidth = (uint32) 0;
  uint32 deftilelength = (uint32) 0;
  uint32 defrowsperstrip = (uint32) 0;
  uint32 dirnum = 0;

  TIFF *in = NULL;
  TIFF *out = NULL;
  char  mode[10];
  char *mp = mode;

  /** RJN additions **/
  struct image_data image;     /* Image parameters for one image */
  struct crop_mask  crop;      /* Cropping parameters for all images */
  struct pagedef    page;      /* Page definition for output pages */
  struct pageseg    sections[MAX_SECTIONS];  /* Sections of one output page */
  struct buffinfo   seg_buffs[MAX_SECTIONS]; /* Segment buffer sizes and pointers */
  struct dump_opts  dump;                  /* Data dump options */
  unsigned char *read_buff    = NULL;      /* Input image data buffer */
  unsigned char *crop_buff    = NULL;      /* Crop area buffer */
  unsigned char *sect_buff    = NULL;      /* Image section buffer */
  unsigned char *sect_src     = NULL;      /* Image section buffer pointer */
  unsigned int  imagelist[MAX_IMAGES + 1]; /* individually specified images */
  unsigned int  image_count  = 0;
  unsigned int  dump_images  = 0;
  unsigned int  next_image   = 0;
  unsigned int  next_page    = 0;
  unsigned int  total_pages  = 0;
  unsigned int  total_images = 0;
  unsigned int  end_of_input = FALSE;
  int    seg, length;
  char   temp_filename[PATH_MAX + 1];

  little_endian = *((unsigned char *)&little_endian) & '1';

  initImageData(&image);
  initCropMasks(&crop);
  initPageSetup(&page, sections, seg_buffs);
  initDumpOptions(&dump);

  process_command_opts (argc, argv, mp, mode, &dirnum, &defconfig,
                        &deffillorder, &deftilewidth, &deftilelength, &defrowsperstrip,
	                &crop, &page, &dump, imagelist, &image_count);

  if (argc - optind < 2)
    usage();

  if ((argc - optind) == 2)
    pageNum = -1;
  else
    total_images = 0;
  /* read multiple input files and write to output file(s) */
  while (optind < argc - 1)
    {
    in = TIFFOpen (argv[optind], "r");
    if (in == NULL)
      return (-3);

    /* If only one input file is specified, we can use directory count */
    total_images = TIFFNumberOfDirectories(in);
    if (image_count == 0)
      {
      dirnum = 0;
      total_pages = total_images; /* Only valid with single input file */
      }
    else
      {
      dirnum = (tdir_t)(imagelist[next_image] - 1);
      next_image++;

      /* Total pages only valid for enumerated list of pages not derived
       * using odd, even, or last keywords.
       */
      if (image_count >  total_images)
	image_count = total_images;

      total_pages = image_count;
      }

    /* MAX_IMAGES is used for special case "last" in selection list */
    if (dirnum == (MAX_IMAGES - 1))
      dirnum = total_images - 1;

    if (dirnum > (total_images))
      {
      TIFFError (TIFFFileName(in),
      "Invalid image number %d, File contains only %d images",
		 (int)dirnum + 1, total_images);
      if (out != NULL)
        (void) TIFFClose(out);
      return (1);
      }

    if (dirnum != 0 && !TIFFSetDirectory(in, (tdir_t)dirnum))
      {
      TIFFError(TIFFFileName(in),"Error, setting subdirectory at %d", dirnum);
      if (out != NULL)
        (void) TIFFClose(out);
      return (1);
      }

    end_of_input = FALSE;
    while (end_of_input == FALSE)
      {
      config = defconfig;
      compression = defcompression;
      predictor = defpredictor;
      fillorder = deffillorder;
      rowsperstrip = defrowsperstrip;
      tilewidth = deftilewidth;
      tilelength = deftilelength;
      g3opts = defg3opts;

      if (dump.format != DUMP_NONE)
        {
        /* manage input and/or output dump files here */
	dump_images++;
        length = strlen(dump.infilename);
        if (length > 0)
          {
          if (dump.infile != NULL)
            fclose (dump.infile);

          /* dump.infilename is guaranteed to be NUL termimated and have 20 bytes
             fewer than PATH_MAX */
          snprintf(temp_filename, sizeof(temp_filename), "%s-read-%03d.%s",
		   dump.infilename, dump_images,
                  (dump.format == DUMP_TEXT) ? "txt" : "raw");
          if ((dump.infile = fopen(temp_filename, dump.mode)) == NULL)
            {
	    TIFFError ("Unable to open dump file for writing", "%s", temp_filename);
	    exit (-1);
            }
          dump_info(dump.infile, dump.format, "Reading image","%d from %s",
                    dump_images, TIFFFileName(in));
          }
        length = strlen(dump.outfilename);
        if (length > 0)
          {
          if (dump.outfile != NULL)
            fclose (dump.outfile);

          /* dump.outfilename is guaranteed to be NUL termimated and have 20 bytes
             fewer than PATH_MAX */
          snprintf(temp_filename, sizeof(temp_filename), "%s-write-%03d.%s",
		   dump.outfilename, dump_images,
                  (dump.format == DUMP_TEXT) ? "txt" : "raw");
          if ((dump.outfile = fopen(temp_filename, dump.mode)) == NULL)
            {
	      TIFFError ("Unable to open dump file for writing", "%s", temp_filename);
	    exit (-1);
            }
          dump_info(dump.outfile, dump.format, "Writing image","%d from %s",
                    dump_images, TIFFFileName(in));
          }
        }

      if (dump.debug)
         TIFFError("main", "Reading image %4d of %4d total pages.", dirnum + 1, total_pages);

      if (loadImage(in, &image, &dump, &read_buff))
        {
        TIFFError("main", "Unable to load source image");
        exit (-1);
        }

      /* Correct the image orientation if it was not ORIENTATION_TOPLEFT.
       */
      if (image.adjustments != 0)
        {
	if (correct_orientation(&image, &read_buff))
	    TIFFError("main", "Unable to correct image orientation");
        }

      if (getCropOffsets(&image, &crop, &dump))
        {
        TIFFError("main", "Unable to define crop regions");
        exit (-1);
	}

      if (crop.selections > 0)
        {
        if (processCropSelections(&image, &crop, &read_buff, seg_buffs))
          {
          TIFFError("main", "Unable to process image selections");
          exit (-1);
	  }
	}
      else  /* Single image segment without zones or regions */
        {
        if (createCroppedImage(&image, &crop, &read_buff, &crop_buff))
          {
          TIFFError("main", "Unable to create output image");
          exit (-1);
	  }
	}
      if (page.mode == PAGE_MODE_NONE)
        {  /* Whole image or sections not based on output page size */
        if (crop.selections > 0)
          {
	  writeSelections(in, &out, &crop, &image, &dump, seg_buffs,
                          mp, argv[argc - 1], &next_page, total_pages);
          }
	else  /* One file all images and sections */
          {
	  if (update_output_file (&out, mp, crop.exp_mode, argv[argc - 1],
                                  &next_page))
             exit (1);
          if (writeCroppedImage(in, out, &image, &dump,crop.combined_width,
                                crop.combined_length, crop_buff, next_page, total_pages))
            {
             TIFFError("main", "Unable to write new image");
             exit (-1);
	    }
          }
	}
      else
        {
	/* If we used a crop buffer, our data is there, otherwise it is
         * in the read_buffer
         */
	if (crop_buff != NULL)
	  sect_src = crop_buff;
        else
          sect_src = read_buff;
        /* Break input image into pages or rows and columns */
        if (computeOutputPixelOffsets(&crop, &image, &page, sections, &dump))
          {
          TIFFError("main", "Unable to compute output section data");
          exit (-1);
	  }
        /* If there are multiple files on the command line, the final one is assumed
         * to be the output filename into which the images are written.
         */
	if (update_output_file (&out, mp, crop.exp_mode, argv[argc - 1], &next_page))
          exit (1);

	if (writeImageSections(in, out, &image, &page, sections, &dump, sect_src, &sect_buff))
          {
          TIFFError("main", "Unable to write image sections");
          exit (-1);
	  }
        }

      /* No image list specified, just read the next image */
      if (image_count == 0)
        dirnum++;
      else
        {
	dirnum = (tdir_t)(imagelist[next_image] - 1);
        next_image++;
        }

      if (dirnum == MAX_IMAGES - 1)
        dirnum = TIFFNumberOfDirectories(in) - 1;

      if (!TIFFSetDirectory(in, (tdir_t)dirnum))
        end_of_input = TRUE;
      }
    TIFFClose(in);
    optind++;
    }

  /* If we did not use the read buffer as the crop buffer */
  if (read_buff)
    _TIFFfree(read_buff);

  if (crop_buff)
    _TIFFfree(crop_buff);

  if (sect_buff)
    _TIFFfree(sect_buff);

   /* Clean up any segment buffers used for zones or regions */
  for (seg = 0; seg < crop.selections; seg++)
    _TIFFfree (seg_buffs[seg].buffer);

  if (dump.format != DUMP_NONE)
    {
    if (dump.infile != NULL)
     fclose (dump.infile);

    if (dump.outfile != NULL)
      {
      dump_info (dump.outfile, dump.format, "", "Completed run for %s", TIFFFileName(out));
      fclose (dump.outfile);
      }
    }

  TIFFClose(out);

  return (0);
  } /* end main */


getCropOffsets(struct image_data *image, struct crop_mask *crop, struct dump_opts *dump)
  {
  struct offset offsets;
  int    i;
  int32  test;
  uint32 seg, total, need_buff = 0;
  uint32 buffsize;
  uint32 zwidth, zlength;

  memset(&offsets, '\0', sizeof(struct offset));
  crop->bufftotal = 0;
  crop->combined_width  = (uint32)0;
  crop->combined_length = (uint32)0;
  crop->selections = 0;

  /* Compute pixel offsets if margins or fixed width or length specified */
  if ((crop->crop_mode & CROP_MARGINS) ||
      (crop->crop_mode & CROP_REGIONS) ||
      (crop->crop_mode & CROP_LENGTH)  ||
      (crop->crop_mode & CROP_WIDTH))
    {
    if (computeInputPixelOffsets(crop, image, &offsets))
      {
      TIFFError ("getCropOffsets", "Unable to compute crop margins");
      return (-1);
      }
    need_buff = TRUE;
    crop->selections = crop->regions;
    /* Regions are only calculated from top and left edges with no margins */
    if (crop->crop_mode & CROP_REGIONS)
      return (0);
    }
  else
    { /* cropped area is the full image */
    offsets.tmargin = 0;
    offsets.lmargin = 0;
    offsets.bmargin = 0;
    offsets.rmargin = 0;
    offsets.crop_width = image->width;
    offsets.crop_length = image->length;
    offsets.startx = 0;
    offsets.endx = image->width - 1;
    offsets.starty = 0;
    offsets.endy = image->length - 1;
    need_buff = FALSE;
    }

  if (dump->outfile != NULL)
    {
    dump_info (dump->outfile, dump->format, "", "Margins: Top: %d  Left: %d  Bottom: %d  Right: %d",
           offsets.tmargin, offsets.lmargin, offsets.bmargin, offsets.rmargin);
    dump_info (dump->outfile, dump->format, "", "Crop region within margins: Adjusted Width:  %6d  Length: %6d",
           offsets.crop_width, offsets.crop_length);
    }

  if (!(crop->crop_mode & CROP_ZONES)) /* no crop zones requested */
    {
    if (need_buff == FALSE)  /* No margins or fixed width or length areas */
      {
      crop->selections = 0;
      crop->combined_width  = image->width;
      crop->combined_length = image->length;
      return (0);
      }
    else
      {
      /* Use one region for margins and fixed width or length areas
       * even though it was not formally declared as a region.
       */
      crop->selections = 1;
      crop->zones = 1;
      crop->zonelist[0].total = 1;
      crop->zonelist[0].position = 1;
      }
    }
  else
    crop->selections = crop->zones;

  for (i = 0; i < crop->zones; i++)
    {
    seg = crop->zonelist[i].position;
    total = crop->zonelist[i].total;

    switch (crop->edge_ref)
      {
      case EDGE_LEFT: /* zones from left to right, length from top */
           zlength = offsets.crop_length;
	   crop->regionlist[i].y1 = offsets.starty;
           crop->regionlist[i].y2 = offsets.endy;

           crop->regionlist[i].x1 = offsets.startx +
                                  (uint32)(offsets.crop_width * 1.0 * (seg - 1) / total);
           test = (int32)offsets.startx +
                  (int32)(offsets.crop_width * 1.0 * seg / total);
           if (test < 1 )
             crop->regionlist[i].x2 = 0;
           else
	     {
	     if (test > (int32)(image->width - 1))
               crop->regionlist[i].x2 = image->width - 1;
             else
	       crop->regionlist[i].x2 = test - 1;
             }
           zwidth = crop->regionlist[i].x2 - crop->regionlist[i].x1  + 1;

	   /* This is passed to extractCropZone or extractCompositeZones */
           crop->combined_length = (uint32)zlength;
           if (crop->exp_mode == COMPOSITE_IMAGES)
             crop->combined_width += (uint32)zwidth;
           else
             crop->combined_width = (uint32)zwidth;
           break;
      case EDGE_BOTTOM: /* width from left, zones from bottom to top */
           zwidth = offsets.crop_width;
	   crop->regionlist[i].x1 = offsets.startx;
           crop->regionlist[i].x2 = offsets.endx;

           test = offsets.endy - (uint32)(offsets.crop_length * 1.0 * seg / total);
           if (test < 1 )
	     crop->regionlist[i].y1 = 0;
           else
	     crop->regionlist[i].y1 = test + 1;

           test = offsets.endy - (offsets.crop_length * 1.0 * (seg - 1) / total);
           if (test < 1 )
             crop->regionlist[i].y2 = 0;
           else
	     {
             if (test > (int32)(image->length - 1))
               crop->regionlist[i].y2 = image->length - 1;
             else
               crop->regionlist[i].y2 = test;
	     }
           zlength = crop->regionlist[i].y2 - crop->regionlist[i].y1 + 1;

	   /* This is passed to extractCropZone or extractCompositeZones */
           if (crop->exp_mode == COMPOSITE_IMAGES)
             crop->combined_length += (uint32)zlength;
           else
             crop->combined_length = (uint32)zlength;
           crop->combined_width = (uint32)zwidth;
           break;
      case EDGE_RIGHT: /* zones from right to left, length from top */
           zlength = offsets.crop_length;
	   crop->regionlist[i].y1 = offsets.starty;
           crop->regionlist[i].y2 = offsets.endy;

           crop->regionlist[i].x1 = offsets.startx +
                                  (uint32)(offsets.crop_width  * (total - seg) * 1.0 / total);
           test = offsets.startx +
	          (offsets.crop_width * (total - seg + 1) * 1.0 / total);
           if (test < 1 )
             crop->regionlist[i].x2 = 0;
           else
	     {
	     if (test > (int32)(image->width - 1))
               crop->regionlist[i].x2 = image->width - 1;
             else
               crop->regionlist[i].x2 = test - 1;
             }
           zwidth = crop->regionlist[i].x2 - crop->regionlist[i].x1  + 1;

	   /* This is passed to extractCropZone or extractCompositeZones */
           crop->combined_length = (uint32)zlength;
           if (crop->exp_mode == COMPOSITE_IMAGES)
             crop->combined_width += (uint32)zwidth;
           else
             crop->combined_width = (uint32)zwidth;
           break;
      case EDGE_TOP: /* width from left, zones from top to bottom */
      default:
           zwidth = offsets.crop_width;
	   crop->regionlist[i].x1 = offsets.startx;
           crop->regionlist[i].x2 = offsets.endx;

           crop->regionlist[i].y1 = offsets.starty + (uint32)(offsets.crop_length * 1.0 * (seg - 1) / total);
           test = offsets.starty + (uint32)(offsets.crop_length * 1.0 * seg / total);
           if (test < 1 )
             crop->regionlist[i].y2 = 0;
           else
	     {
	     if (test > (int32)(image->length - 1))
	       crop->regionlist[i].y2 = image->length - 1;
             else
	       crop->regionlist[i].y2 = test - 1;
	     }
           zlength = crop->regionlist[i].y2 - crop->regionlist[i].y1 + 1;

	   /* This is passed to extractCropZone or extractCompositeZones */
           if (crop->exp_mode == COMPOSITE_IMAGES)
             crop->combined_length += (uint32)zlength;
           else
             crop->combined_length = (uint32)zlength;
           crop->combined_width = (uint32)zwidth;
           break;
      } /* end switch statement */

    buffsize = (uint32)
          ((((zwidth * image->bps * image->spp) + 7 ) / 8) * (zlength + 1));
    crop->regionlist[i].width = (uint32) zwidth;
    crop->regionlist[i].length = (uint32) zlength;
    crop->regionlist[i].buffsize = buffsize;
    crop->bufftotal += buffsize;

  if (dump->outfile != NULL)
    dump_info (dump->outfile, dump->format, "",  "Zone %d, width: %4d, length: %4d, x1: %4d  x2: %4d  y1: %4d  y2: %4d",
                    i + 1, (uint32)zwidth, (uint32)zlength,
		    crop->regionlist[i].x1, crop->regionlist[i].x2,
                    crop->regionlist[i].y1, crop->regionlist[i].y2);
    }

  return (0);
  } /* end getCropOffsets */


static int dump_buffer (FILE* dumpfile, int format, uint32 rows, uint32 width,
                 uint32 row, unsigned char *buff)
  {
  int j, k;
  uint32 i;
  unsigned char * dump_ptr;

  if (dumpfile == NULL)
    {
    TIFFError ("", "Invalid FILE pointer for dump file");
    return (1);
    }

  for (i = 0; i < rows; i++)
    {
    dump_ptr = buff + (i * width);
    if (format == DUMP_TEXT)
      dump_info (dumpfile, format, "",
                 "Row %4d, %d bytes at offset %d",
	         row + i + 1, width, row * width);

    for (j = 0, k = width; k >= 10; j += 10, k -= 10, dump_ptr += 10)
      dump_data (dumpfile, format, "", dump_ptr, 10);
    if (k > 0)
      dump_data (dumpfile, format, "", dump_ptr, k);
    }
  return (0);
  }


extractContigSamplesBytes (uint8 *in, uint8 *out, uint32 cols,
                           tsample_t sample, uint16 spp, uint16 bps,
                           tsample_t count, uint32 start, uint32 end)
  {
  int i, bytes_per_sample, sindex;
  uint32 col, dst_rowsize, bit_offset;
  uint32 src_byte /*, src_bit */;
  uint8 *src = in;
  uint8 *dst = out;

  if ((src == NULL) || (dst == NULL))
    {
    TIFFError("extractContigSamplesBytes","Invalid input or output buffer");
    return (1);
    }

  if ((start > end) || (start > cols))
    {
    TIFFError ("extractContigSamplesBytes",
               "Invalid start column value %d ignored", start);
    start = 0;
    }
  if ((end == 0) || (end > cols))
    {
    TIFFError ("extractContigSamplesBytes",
               "Invalid end column value %d ignored", end);
    end = cols;
    }

  dst_rowsize = (bps * (end - start) * count) / 8;

  bytes_per_sample = (bps + 7) / 8;
  /* Optimize case for copying all samples */
  if (count == spp)
    {
    src = in + (start * spp * bytes_per_sample);
    _TIFFmemcpy (dst, src, dst_rowsize);
    }
  else
    {
    for (col = start; col < end; col++)
      {
      for (sindex = sample; (sindex < spp) && (sindex < (sample + count)); sindex++)
        {
        bit_offset = col * bps * spp;
        if (sindex == 0)
          {
          src_byte = bit_offset / 8;
          /* src_bit  = bit_offset % 8; */
          }
        else
          {
          src_byte = (bit_offset + (sindex * bps)) / 8;
          /* src_bit  = (bit_offset + (sindex * bps)) % 8; */
          }
        src = in + src_byte;
        for (i = 0; i < bytes_per_sample; i++)
            *dst++ = *src++;
        }
      }
    }

  return (0);
  } /* end extractContigSamplesBytes */


PredictorDecodeRow(TIFF* tif, uint8* op0, tmsize_t occ0, uint16 s)
{
	TIFFPredictorState *sp = PredictorState(tif);

	assert(sp != NULL);
	assert(sp->decoderow != NULL);
	assert(sp->decodepfunc != NULL);

	if ((*sp->decoderow)(tif, op0, occ0, s)) {
		return (*sp->decodepfunc)(tif, op0, occ0);
	} else
		return 0;
}


DECLAREcpFunc(cpContig2ContigByRow)
{
	tsize_t scanlinesize = TIFFScanlineSize(in);
	tdata_t buf;
	uint32 row;

	buf = _TIFFmalloc(scanlinesize);
	if (!buf)
		return 0;
	_TIFFmemset(buf, 0, scanlinesize);
	(void) imagewidth; (void) spp;
	for (row = 0; row < imagelength; row++) {
		if (TIFFReadScanline(in, buf, row, 0) < 0 && !ignore) {
			TIFFError(TIFFFileName(in),
				  "Error, can't read scanline %lu",
				  (unsigned long) row);
			goto bad;
		}
		if (TIFFWriteScanline(out, buf, row, 0) < 0) {
			TIFFError(TIFFFileName(out),
				  "Error, can't write scanline %lu",
				  (unsigned long) row);
			goto bad;
		}
	}
	_TIFFfree(buf);
	return 1;
bad:
	_TIFFfree(buf);
	return 0;
}


DECLAREcpFunc(cpContigTiles2SeparateTiles)
{
	return cpImage(in, out,
	    readContigTilesIntoBuffer,
	    writeBufferToSeparateTiles,
	    imagelength, imagewidth, spp);
}


DECLAREreadFunc(readContigStripsIntoBuffer)
{
	tsize_t scanlinesize = TIFFScanlineSize(in);
	uint8* bufp = buf;
	uint32 row;

	(void) imagewidth; (void) spp;
	for (row = 0; row < imagelength; row++) {
		if (TIFFReadScanline(in, (tdata_t) bufp, row, 0) < 0
		    && !ignore) {
			TIFFError(TIFFFileName(in),
			    "Error, can't read scanline %lu",
			    (unsigned long) row);
			return 0;
		}
		bufp += scanlinesize;
	}

	return 1;
}


int auth_policy_attribute_comparator(const struct policy_template_keyvalue *a,
	const struct policy_template_keyvalue *b)
{
	return strcmp(a->key, b->key);
}


void EC_EX_DATA_free_data(EC_EXTRA_DATA **ex_data,
	void *(*dup_func)(void *), void (*free_func)(void *), void (*clear_free_func)(void *))
	{
	EC_EXTRA_DATA **p;

	if (ex_data == NULL)
		return;

	for (p = ex_data; *p != NULL; p = &((*p)->next))
		{
		if ((*p)->dup_func == dup_func && (*p)->free_func == free_func && (*p)->clear_free_func == clear_free_func)
			{
			EC_EXTRA_DATA *next = (*p)->next;

			(*p)->free_func((*p)->data);
			OPENSSL_free(*p);

			*p = next;
			return;
			}
		}
	}


static int ecdsa_sign_setup(EC_KEY *eckey, BN_CTX *ctx_in, BIGNUM **kinvp,
		BIGNUM **rp)
{
	BN_CTX   *ctx = NULL;
	BIGNUM	 *k = NULL, *r = NULL, *order = NULL, *X = NULL;
	EC_POINT *tmp_point=NULL;
	const EC_GROUP *group;
	int 	 ret = 0;

	if (eckey == NULL || (group = EC_KEY_get0_group(eckey)) == NULL)
	{
		ECDSAerr(ECDSA_F_ECDSA_SIGN_SETUP, ERR_R_PASSED_NULL_PARAMETER);
		return 0;
	}

	if (ctx_in == NULL)
	{
		if ((ctx = BN_CTX_new()) == NULL)
		{
			ECDSAerr(ECDSA_F_ECDSA_SIGN_SETUP,ERR_R_MALLOC_FAILURE);
			return 0;
		}
	}
	else
		ctx = ctx_in;

	k     = BN_new();	/* this value is later returned in *kinvp */
	r     = BN_new();	/* this value is later returned in *rp    */
	order = BN_new();
	X     = BN_new();
	if (!k || !r || !order || !X)
	{
		ECDSAerr(ECDSA_F_ECDSA_SIGN_SETUP, ERR_R_MALLOC_FAILURE);
		goto err;
	}
	if ((tmp_point = EC_POINT_new(group)) == NULL)
	{
		ECDSAerr(ECDSA_F_ECDSA_SIGN_SETUP, ERR_R_EC_LIB);
		goto err;
	}
	if (!EC_GROUP_get_order(group, order, ctx))
	{
		ECDSAerr(ECDSA_F_ECDSA_SIGN_SETUP, ERR_R_EC_LIB);
		goto err;
	}

	do
	{
		/* get random k */
		do
			if (!BN_rand_range(k, order))
			{
				ECDSAerr(ECDSA_F_ECDSA_SIGN_SETUP,
				 ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED);
				goto err;
			}
		while (BN_is_zero(k));

		/* We do not want timing information to leak the length of k,
		 * so we compute G*k using an equivalent scalar of fixed
		 * bit-length. */

		if (!BN_add(k, k, order)) goto err;
		if (BN_num_bits(k) <= BN_num_bits(order))
			if (!BN_add(k, k, order)) goto err;

		/* compute r the x-coordinate of generator * k */
		if (!EC_POINT_mul(group, tmp_point, k, NULL, NULL, ctx))
		{
			ECDSAerr(ECDSA_F_ECDSA_SIGN_SETUP, ERR_R_EC_LIB);
			goto err;
		}
		if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) == NID_X9_62_prime_field)
		{
			if (!EC_POINT_get_affine_coordinates_GFp(group,
				tmp_point, X, NULL, ctx))
			{
				ECDSAerr(ECDSA_F_ECDSA_SIGN_SETUP,ERR_R_EC_LIB);
				goto err;
			}
		}
#ifndef OPENSSL_NO_EC2M
		else /* NID_X9_62_characteristic_two_field */
		{
			if (!EC_POINT_get_affine_coordinates_GF2m(group,
				tmp_point, X, NULL, ctx))
			{
				ECDSAerr(ECDSA_F_ECDSA_SIGN_SETUP,ERR_R_EC_LIB);
				goto err;
			}
		}
#endif
		if (!BN_nnmod(r, X, order, ctx))
		{
			ECDSAerr(ECDSA_F_ECDSA_SIGN_SETUP, ERR_R_BN_LIB);
			goto err;
		}
	}
	while (BN_is_zero(r));

	/* compute the inverse of k */
	if (EC_GROUP_get_mont_data(group) != NULL)
		{
		/* We want inverse in constant time, therefore we utilize the
		 * fact order must be prime and use Fermats Little Theorem
		 * instead. */
		if (!BN_set_word(X, 2) )
			{
			ECDSAerr(ECDSA_F_ECDSA_SIGN_SETUP, ERR_R_BN_LIB);
			goto err;
			}
		if (!BN_mod_sub(X, order, X, order, ctx))
			{
			ECDSAerr(ECDSA_F_ECDSA_SIGN_SETUP, ERR_R_BN_LIB);
			goto err;
			}
		BN_set_flags(X, BN_FLG_CONSTTIME);
		if (!BN_mod_exp_mont_consttime(k, k, X, order, ctx, EC_GROUP_get_mont_data(group)))
			{
			ECDSAerr(ECDSA_F_ECDSA_SIGN_SETUP, ERR_R_BN_LIB);
			goto err;
			}
		}
	else
		{
		if (!BN_mod_inverse(k, k, order, ctx))
			{
			ECDSAerr(ECDSA_F_ECDSA_SIGN_SETUP, ERR_R_BN_LIB);
			goto err;
			}
		}

	/* clear old values if necessary */
	if (*rp != NULL)
		BN_clear_free(*rp);
	if (*kinvp != NULL)
		BN_clear_free(*kinvp);
	/* save the pre-computed values  */
	*rp    = r;
	*kinvp = k;
	ret = 1;
err:
	if (!ret)
	{
		if (k != NULL) BN_clear_free(k);
		if (r != NULL) BN_clear_free(r);
	}
	if (ctx_in == NULL)
		BN_CTX_free(ctx);
	if (order != NULL)
		BN_free(order);
	if (tmp_point != NULL)
		EC_POINT_free(tmp_point);
	if (X)
		BN_clear_free(X);
	return(ret);
}


int EC_POINT_set_affine_coordinates_GF2m(const EC_GROUP *group, EC_POINT *point,
	const BIGNUM *x, const BIGNUM *y, BN_CTX *ctx)
	{
	if (group->meth->point_set_affine_coordinates == 0)
		{
		ECerr(EC_F_EC_POINT_SET_AFFINE_COORDINATES_GF2M, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
		return 0;
		}
	if (group->meth != point->meth)
		{
		ECerr(EC_F_EC_POINT_SET_AFFINE_COORDINATES_GF2M, EC_R_INCOMPATIBLE_OBJECTS);
		return 0;
		}
	return group->meth->point_set_affine_coordinates(group, point, x, y, ctx);
	}


void EC_GROUP_set_curve_name(EC_GROUP *group, int nid)
	{
	group->curve_name = nid;
	}


void EC_POINT_clear_free(EC_POINT *point)
	{
	if (!point) return;

	if (point->meth->point_clear_finish != 0)
		point->meth->point_clear_finish(point);
	else if (point->meth->point_finish != 0)
		point->meth->point_finish(point);
	OPENSSL_cleanse(point, sizeof *point);
	OPENSSL_free(point);
	}


int EC_POINT_set_to_infinity(const EC_GROUP *group, EC_POINT *point)
	{
	if (group->meth->point_set_to_infinity == 0)
		{
		ECerr(EC_F_EC_POINT_SET_TO_INFINITY, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
		return 0;
		}
	if (group->meth != point->meth)
		{
		ECerr(EC_F_EC_POINT_SET_TO_INFINITY, EC_R_INCOMPATIBLE_OBJECTS);
		return 0;
		}
	return group->meth->point_set_to_infinity(group, point);
	}


int EC_POINT_get_affine_coordinates_GF2m(const EC_GROUP *group, const EC_POINT *point,
	BIGNUM *x, BIGNUM *y, BN_CTX *ctx)
	{
	if (group->meth->point_get_affine_coordinates == 0)
		{
		ECerr(EC_F_EC_POINT_GET_AFFINE_COORDINATES_GF2M, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
		return 0;
		}
	if (group->meth != point->meth)
		{
		ECerr(EC_F_EC_POINT_GET_AFFINE_COORDINATES_GF2M, EC_R_INCOMPATIBLE_OBJECTS);
		return 0;
		}
	return group->meth->point_get_affine_coordinates(group, point, x, y, ctx);
	}


size_t EC_GROUP_get_seed_len(const EC_GROUP *group)
	{
	return group->seed_len;
	}


int EC_POINT_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *g_scalar,
	const EC_POINT *point, const BIGNUM *p_scalar, BN_CTX *ctx)
	{
	/* just a convenient interface to EC_POINTs_mul() */

	const EC_POINT *points[1];
	const BIGNUM *scalars[1];

	points[0] = point;
	scalars[0] = p_scalar;

	return EC_POINTs_mul(group, r, g_scalar, (point != NULL && p_scalar != NULL), points, scalars, ctx);
	}


int EC_GROUP_check_discriminant(const EC_GROUP *group, BN_CTX *ctx)
	{
	if (group->meth->group_check_discriminant == 0)
		{
		ECerr(EC_F_EC_GROUP_CHECK_DISCRIMINANT, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
		return 0;
		}
	return group->meth->group_check_discriminant(group, ctx);
	}


static int aes_gcm_cleanup(EVP_CIPHER_CTX *c)
{
    EVP_AES_GCM_CTX *gctx = EVP_C_DATA(EVP_AES_GCM_CTX,c);
    OPENSSL_cleanse(&gctx->gcm, sizeof(gctx->gcm));
    if (gctx->iv != EVP_CIPHER_CTX_iv_noconst(c))
        OPENSSL_free(gctx->iv);
    return 1;
}


static int aes_cfb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
                          const unsigned char *in, size_t len)
{
    EVP_AES_KEY *dat = EVP_C_DATA(EVP_AES_KEY,ctx);

    int num = EVP_CIPHER_CTX_num(ctx);
    CRYPTO_cfb128_encrypt(in, out, len, &dat->ks,
                          EVP_CIPHER_CTX_iv_noconst(ctx), &num,
                          EVP_CIPHER_CTX_encrypting(ctx), dat->block);
    EVP_CIPHER_CTX_set_num(ctx, num);
    return 1;
}


static int aes_gcm_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
                          const unsigned char *in, size_t len)
{
    EVP_AES_GCM_CTX *gctx = EVP_C_DATA(EVP_AES_GCM_CTX,ctx);
    /* If not set up, return error */
    if (!gctx->key_set)
        return -1;

    if (gctx->tls_aad_len >= 0)
        return aes_gcm_tls_cipher(ctx, out, in, len);

    if (!gctx->iv_set)
        return -1;
    if (in) {
        if (out == NULL) {
            if (CRYPTO_gcm128_aad(&gctx->gcm, in, len))
                return -1;
        } else if (EVP_CIPHER_CTX_encrypting(ctx)) {
            if (gctx->ctr) {
                size_t bulk = 0;
#if defined(AES_GCM_ASM)
                if (len >= 32 && AES_GCM_ASM(gctx)) {
                    size_t res = (16 - gctx->gcm.mres) % 16;

                    if (CRYPTO_gcm128_encrypt(&gctx->gcm, in, out, res))
                        return -1;

                    bulk = AES_gcm_encrypt(in + res,
                                           out + res, len - res,
                                           gctx->gcm.key, gctx->gcm.Yi.c,
                                           gctx->gcm.Xi.u);
                    gctx->gcm.len.u[1] += bulk;
                    bulk += res;
                }
#endif
                if (CRYPTO_gcm128_encrypt_ctr32(&gctx->gcm,
                                                in + bulk,
                                                out + bulk,
                                                len - bulk, gctx->ctr))
                    return -1;
            } else {
                size_t bulk = 0;
#if defined(AES_GCM_ASM2)
                if (len >= 32 && AES_GCM_ASM2(gctx)) {
                    size_t res = (16 - gctx->gcm.mres) % 16;

                    if (CRYPTO_gcm128_encrypt(&gctx->gcm, in, out, res))
                        return -1;

                    bulk = AES_gcm_encrypt(in + res,
                                           out + res, len - res,
                                           gctx->gcm.key, gctx->gcm.Yi.c,
                                           gctx->gcm.Xi.u);
                    gctx->gcm.len.u[1] += bulk;
                    bulk += res;
                }
#endif
                if (CRYPTO_gcm128_encrypt(&gctx->gcm,
                                          in + bulk, out + bulk, len - bulk))
                    return -1;
            }
        } else {
            if (gctx->ctr) {
                size_t bulk = 0;
#if defined(AES_GCM_ASM)
                if (len >= 16 && AES_GCM_ASM(gctx)) {
                    size_t res = (16 - gctx->gcm.mres) % 16;

                    if (CRYPTO_gcm128_decrypt(&gctx->gcm, in, out, res))
                        return -1;

                    bulk = AES_gcm_decrypt(in + res,
                                           out + res, len - res,
                                           gctx->gcm.key,
                                           gctx->gcm.Yi.c, gctx->gcm.Xi.u);
                    gctx->gcm.len.u[1] += bulk;
                    bulk += res;
                }
#endif
                if (CRYPTO_gcm128_decrypt_ctr32(&gctx->gcm,
                                                in + bulk,
                                                out + bulk,
                                                len - bulk, gctx->ctr))
                    return -1;
            } else {
                size_t bulk = 0;
#if defined(AES_GCM_ASM2)
                if (len >= 16 && AES_GCM_ASM2(gctx)) {
                    size_t res = (16 - gctx->gcm.mres) % 16;

                    if (CRYPTO_gcm128_decrypt(&gctx->gcm, in, out, res))
                        return -1;

                    bulk = AES_gcm_decrypt(in + res,
                                           out + res, len - res,
                                           gctx->gcm.key,
                                           gctx->gcm.Yi.c, gctx->gcm.Xi.u);
                    gctx->gcm.len.u[1] += bulk;
                    bulk += res;
                }
#endif
                if (CRYPTO_gcm128_decrypt(&gctx->gcm,
                                          in + bulk, out + bulk, len - bulk))
                    return -1;
            }
        }
        return len;
    } else {
        if (!EVP_CIPHER_CTX_encrypting(ctx)) {
            if (gctx->taglen < 0)
                return -1;
            if (CRYPTO_gcm128_finish(&gctx->gcm,
                                     EVP_CIPHER_CTX_buf_noconst(ctx),
                                     gctx->taglen) != 0)
                return -1;
            gctx->iv_set = 0;
            return 0;
        }
        CRYPTO_gcm128_tag(&gctx->gcm, EVP_CIPHER_CTX_buf_noconst(ctx), 16);
        gctx->taglen = 16;
        /* Don't reuse the IV */
        gctx->iv_set = 0;
        return 0;
    }

}


static int aesni_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
                          const unsigned char *iv, int enc)
{
    int ret, mode;
    EVP_AES_KEY *dat = EVP_C_DATA(EVP_AES_KEY,ctx);

    mode = EVP_CIPHER_CTX_mode(ctx);
    if ((mode == EVP_CIPH_ECB_MODE || mode == EVP_CIPH_CBC_MODE)
        && !enc) {
        ret = aesni_set_decrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
                                    &dat->ks.ks);
        dat->block = (block128_f) aesni_decrypt;
        dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ?
            (cbc128_f) aesni_cbc_encrypt : NULL;
    } else {
        ret = aesni_set_encrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
                                    &dat->ks.ks);
        dat->block = (block128_f) aesni_encrypt;
        if (mode == EVP_CIPH_CBC_MODE)
            dat->stream.cbc = (cbc128_f) aesni_cbc_encrypt;
        else if (mode == EVP_CIPH_CTR_MODE)
            dat->stream.ctr = (ctr128_f) aesni_ctr32_encrypt_blocks;
        else
            dat->stream.cbc = NULL;
    }

    if (ret < 0) {
        EVPerr(EVP_F_AESNI_INIT_KEY, EVP_R_AES_KEY_SETUP_FAILED);
        return 0;
    }

    return 1;
}


static int chacha_cipher(EVP_CIPHER_CTX * ctx, unsigned char *out,
                         const unsigned char *inp, size_t len)
{
    EVP_CHACHA_KEY *key = data(ctx);
    unsigned int n, rem, ctr32;

    if ((n = key->partial_len)) {
        while (len && n < CHACHA_BLK_SIZE) {
            *out++ = *inp++ ^ key->buf[n++];
            len--;
        }
        key->partial_len = n;

        if (len == 0)
            return 1;

        if (n == CHACHA_BLK_SIZE) {
            key->partial_len = 0;
            key->counter[0]++;
            if (key->counter[0] == 0)
                key->counter[1]++;
        }
    }

    rem = (unsigned int)(len % CHACHA_BLK_SIZE);
    len -= rem;
    ctr32 = key->counter[0];
    while (len >= CHACHA_BLK_SIZE) {
        size_t blocks = len / CHACHA_BLK_SIZE;
        /*
         * 1<<28 is just a not-so-small yet not-so-large number...
         * Below condition is practically never met, but it has to
         * be checked for code correctness.
         */
        if (sizeof(size_t)>sizeof(unsigned int) && blocks>(1U<<28))
            blocks = (1U<<28);

        /*
         * As ChaCha20_ctr32 operates on 32-bit counter, caller
         * has to handle overflow. 'if' below detects the
         * overflow, which is then handled by limiting the
         * amount of blocks to the exact overflow point...
         */
        ctr32 += (unsigned int)blocks;
        if (ctr32 < blocks) {
            blocks -= ctr32;
            ctr32 = 0;
        }
        blocks *= CHACHA_BLK_SIZE;
        ChaCha20_ctr32(out, inp, blocks, key->key.d, key->counter);
        len -= blocks;
        inp += blocks;
        out += blocks;

        key->counter[0] = ctr32;
        if (ctr32 == 0) key->counter[1]++;
    }

    if (rem) {
        memset(key->buf, 0, sizeof(key->buf));
        ChaCha20_ctr32(key->buf, key->buf, CHACHA_BLK_SIZE,
                       key->key.d, key->counter);
        for (n = 0; n < rem; n++)
            out[n] = inp[n] ^ key->buf[n];
        key->partial_len = rem;
    }

    return 1;
}


static int aes_ocb_cleanup(EVP_CIPHER_CTX *c)
{
    EVP_AES_OCB_CTX *octx = EVP_C_DATA(EVP_AES_OCB_CTX,c);
    CRYPTO_ocb128_cleanup(&octx->ocb);
    return 1;
}


static int aes_gcm_tls_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
                              const unsigned char *in, size_t len)
{
    EVP_AES_GCM_CTX *gctx = EVP_C_DATA(EVP_AES_GCM_CTX,ctx);
    int rv = -1;
    /* Encrypt/decrypt must be performed in place */
    if (out != in
        || len < (EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN))
        return -1;
    /*
     * Set IV from start of buffer or generate IV and write to start of
     * buffer.
     */
    if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CIPHER_CTX_encrypting(ctx) ?
                            EVP_CTRL_GCM_IV_GEN : EVP_CTRL_GCM_SET_IV_INV,
                            EVP_GCM_TLS_EXPLICIT_IV_LEN, out) <= 0)
        goto err;
    /* Use saved AAD */
    if (CRYPTO_gcm128_aad(&gctx->gcm, EVP_CIPHER_CTX_buf_noconst(ctx),
                          gctx->tls_aad_len))
        goto err;
    /* Fix buffer and length to point to payload */
    in += EVP_GCM_TLS_EXPLICIT_IV_LEN;
    out += EVP_GCM_TLS_EXPLICIT_IV_LEN;
    len -= EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN;
    if (EVP_CIPHER_CTX_encrypting(ctx)) {
        /* Encrypt payload */
        if (gctx->ctr) {
            size_t bulk = 0;
#if defined(AES_GCM_ASM)
            if (len >= 32 && AES_GCM_ASM(gctx)) {
                if (CRYPTO_gcm128_encrypt(&gctx->gcm, NULL, NULL, 0))
                    return -1;

                bulk = AES_gcm_encrypt(in, out, len,
                                       gctx->gcm.key,
                                       gctx->gcm.Yi.c, gctx->gcm.Xi.u);
                gctx->gcm.len.u[1] += bulk;
            }
#endif
            if (CRYPTO_gcm128_encrypt_ctr32(&gctx->gcm,
                                            in + bulk,
                                            out + bulk,
                                            len - bulk, gctx->ctr))
                goto err;
        } else {
            size_t bulk = 0;
#if defined(AES_GCM_ASM2)
            if (len >= 32 && AES_GCM_ASM2(gctx)) {
                if (CRYPTO_gcm128_encrypt(&gctx->gcm, NULL, NULL, 0))
                    return -1;

                bulk = AES_gcm_encrypt(in, out, len,
                                       gctx->gcm.key,
                                       gctx->gcm.Yi.c, gctx->gcm.Xi.u);
                gctx->gcm.len.u[1] += bulk;
            }
#endif
            if (CRYPTO_gcm128_encrypt(&gctx->gcm,
                                      in + bulk, out + bulk, len - bulk))
                goto err;
        }
        out += len;
        /* Finally write tag */
        CRYPTO_gcm128_tag(&gctx->gcm, out, EVP_GCM_TLS_TAG_LEN);
        rv = len + EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN;
    } else {
        /* Decrypt */
        if (gctx->ctr) {
            size_t bulk = 0;
#if defined(AES_GCM_ASM)
            if (len >= 16 && AES_GCM_ASM(gctx)) {
                if (CRYPTO_gcm128_decrypt(&gctx->gcm, NULL, NULL, 0))
                    return -1;

                bulk = AES_gcm_decrypt(in, out, len,
                                       gctx->gcm.key,
                                       gctx->gcm.Yi.c, gctx->gcm.Xi.u);
                gctx->gcm.len.u[1] += bulk;
            }
#endif
            if (CRYPTO_gcm128_decrypt_ctr32(&gctx->gcm,
                                            in + bulk,
                                            out + bulk,
                                            len - bulk, gctx->ctr))
                goto err;
        } else {
            size_t bulk = 0;
#if defined(AES_GCM_ASM2)
            if (len >= 16 && AES_GCM_ASM2(gctx)) {
                if (CRYPTO_gcm128_decrypt(&gctx->gcm, NULL, NULL, 0))
                    return -1;

                bulk = AES_gcm_decrypt(in, out, len,
                                       gctx->gcm.key,
                                       gctx->gcm.Yi.c, gctx->gcm.Xi.u);
                gctx->gcm.len.u[1] += bulk;
            }
#endif
            if (CRYPTO_gcm128_decrypt(&gctx->gcm,
                                      in + bulk, out + bulk, len - bulk))
                goto err;
        }
        /* Retrieve tag */
        CRYPTO_gcm128_tag(&gctx->gcm, EVP_CIPHER_CTX_buf_noconst(ctx),
                          EVP_GCM_TLS_TAG_LEN);
        /* If tag mismatch wipe buffer */
        if (CRYPTO_memcmp(EVP_CIPHER_CTX_buf_noconst(ctx), in + len,
                          EVP_GCM_TLS_TAG_LEN)) {
            OPENSSL_cleanse(out, len);
            goto err;
        }
        rv = len;
    }

 err:
    gctx->iv_set = 0;
    gctx->tls_aad_len = -1;
    return rv;
}


static void alarm_win32(unsigned int secs)
{
    lapse = secs * 1000;
}


static char *sstrsep(char **string, const char *delim)
{
    char isdelim[256];
    char *token = *string;

    if (**string == 0)
        return NULL;

    memset(isdelim, 0, sizeof isdelim);
    isdelim[0] = 1;

    while (*delim) {
        isdelim[(unsigned char)(*delim)] = 1;
        delim++;
    }

    while (!isdelim[(unsigned char)(**string)]) {
        (*string)++;
    }

    if (**string) {
        **string = 0;
        (*string)++;
    }

    return token;
}


static int aes_cfb1_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
                           const unsigned char *in, size_t len)
{
    EVP_AES_KEY *dat = (EVP_AES_KEY *) ctx->cipher_data;

    if (ctx->flags & EVP_CIPH_FLAG_LENGTH_BITS) {
        CRYPTO_cfb128_1_encrypt(in, out, len, &dat->ks,
                                ctx->iv, &ctx->num, ctx->encrypt, dat->block);
        return 1;
    }

    while (len >= MAXBITCHUNK) {
        CRYPTO_cfb128_1_encrypt(in, out, MAXBITCHUNK * 8, &dat->ks,
                                ctx->iv, &ctx->num, ctx->encrypt, dat->block);
        len -= MAXBITCHUNK;
    }
    if (len)
        CRYPTO_cfb128_1_encrypt(in, out, len * 8, &dat->ks,
                                ctx->iv, &ctx->num, ctx->encrypt, dat->block);

    return 1;
}


static int aes_t4_xts_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
                               const unsigned char *iv, int enc)
{
    EVP_AES_XTS_CTX *xctx = ctx->cipher_data;
    if (!iv && !key)
        return 1;

    if (key) {
        int bits = ctx->key_len * 4;
        xctx->stream = NULL;
        /* key_len is two AES keys */
        if (enc) {
            aes_t4_set_encrypt_key(key, bits, &xctx->ks1.ks);
            xctx->xts.block1 = (block128_f) aes_t4_encrypt;
            switch (bits) {
            case 128:
                xctx->stream = aes128_t4_xts_encrypt;
                break;
#  if 0                         /* not yet */
            case 192:
                xctx->stream = aes192_t4_xts_encrypt;
                break;
#  endif
            case 256:
                xctx->stream = aes256_t4_xts_encrypt;
                break;
            default:
                return 0;
            }
        } else {
            aes_t4_set_decrypt_key(key, ctx->key_len * 4, &xctx->ks1.ks);
            xctx->xts.block1 = (block128_f) aes_t4_decrypt;
            switch (bits) {
            case 128:
                xctx->stream = aes128_t4_xts_decrypt;
                break;
#  if 0                         /* not yet */
            case 192:
                xctx->stream = aes192_t4_xts_decrypt;
                break;
#  endif
            case 256:
                xctx->stream = aes256_t4_xts_decrypt;
                break;
            default:
                return 0;
            }
        }

        aes_t4_set_encrypt_key(key + ctx->key_len / 2,
                               ctx->key_len * 4, &xctx->ks2.ks);
        xctx->xts.block2 = (block128_f) aes_t4_encrypt;

        xctx->xts.key1 = &xctx->ks1;
    }

    if (iv) {
        xctx->xts.key2 = &xctx->ks2;
        memcpy(ctx->iv, iv, 16);
    }

    return 1;
}


int tls1_cert_verify_mac(SSL *s, int md_nid, unsigned char *out)
{
    unsigned int ret;
    EVP_MD_CTX ctx, *d = NULL;
    int i;

    if (s->s3->handshake_buffer)
        if (!ssl3_digest_cached_records(s))
            return 0;

    for (i = 0; i < SSL_MAX_DIGEST; i++) {
        if (s->s3->handshake_dgst[i]
            && EVP_MD_CTX_type(s->s3->handshake_dgst[i]) == md_nid) {
            d = s->s3->handshake_dgst[i];
            break;
        }
    }
    if (!d) {
        SSLerr(SSL_F_TLS1_CERT_VERIFY_MAC, SSL_R_NO_REQUIRED_DIGEST);
        return 0;
    }

    EVP_MD_CTX_init(&ctx);
    EVP_MD_CTX_copy_ex(&ctx, d);
    EVP_DigestFinal_ex(&ctx, out, &ret);
    EVP_MD_CTX_cleanup(&ctx);
    return ((int)ret);
}


static int tls1_generate_key_block(SSL *s, unsigned char *km,
                                   unsigned char *tmp, int num)
{
    int ret;
    ret = tls1_PRF(ssl_get_algorithm2(s),
                   TLS_MD_KEY_EXPANSION_CONST,
                   TLS_MD_KEY_EXPANSION_CONST_SIZE, s->s3->server_random,
                   SSL3_RANDOM_SIZE, s->s3->client_random, SSL3_RANDOM_SIZE,
                   NULL, 0, NULL, 0, s->session->master_key,
                   s->session->master_key_length, km, tmp, num);
#ifdef KSSL_DEBUG
    fprintf(stderr, "tls1_generate_key_block() ==> %d byte master_key =\n\t",
            s->session->master_key_length);
    {
        int i;
        for (i = 0; i < s->session->master_key_length; i++) {
            fprintf(stderr, "%02X", s->session->master_key[i]);
        }
        fprintf(stderr, "\n");
    }
#endif                          /* KSSL_DEBUG */
    return ret;
}


unsigned long ossl_statem_client_max_message_size(SSL *s)
{
    OSSL_STATEM *st = &s->statem;

    switch (st->hand_state) {
    case TLS_ST_CR_SRVR_HELLO:
        return SERVER_HELLO_MAX_LENGTH;

    case DTLS_ST_CR_HELLO_VERIFY_REQUEST:
        return HELLO_VERIFY_REQUEST_MAX_LENGTH;

    case TLS_ST_CR_CERT:
        return s->max_cert_list;

    case TLS_ST_CR_CERT_STATUS:
        return SSL3_RT_MAX_PLAIN_LENGTH;

    case TLS_ST_CR_KEY_EXCH:
        return SERVER_KEY_EXCH_MAX_LENGTH;

    case TLS_ST_CR_CERT_REQ:
        /*
         * Set to s->max_cert_list for compatibility with previous releases. In
         * practice these messages can get quite long if servers are configured
         * to provide a long list of acceptable CAs
         */
        return s->max_cert_list;

    case TLS_ST_CR_SRVR_DONE:
        return SERVER_HELLO_DONE_MAX_LENGTH;

    case TLS_ST_CR_CHANGE:
        if (s->version == DTLS1_BAD_VER)
            return 3;
        return CCS_MAX_LENGTH;

    case TLS_ST_CR_SESSION_TICKET:
        return SSL3_RT_MAX_PLAIN_LENGTH;

    case TLS_ST_CR_FINISHED:
        return FINISHED_MAX_LENGTH;

    default:
        /* Shouldn't happen */
        break;
    }

    return 0;
}


static int key_exchange_expected(SSL *s)
{
    long alg_k = s->s3->tmp.new_cipher->algorithm_mkey;

    /*
     * Can't skip server key exchange if this is an ephemeral
     * ciphersuite or for SRP
     */
    if (alg_k & (SSL_kDHE | SSL_kECDHE | SSL_kDHEPSK | SSL_kECDHEPSK
                 | SSL_kSRP)) {
        return 1;
    }

    return 0;
}


static int tls_process_ske_srp(SSL *s, PACKET *pkt, EVP_PKEY **pkey, int *al)
{
#ifndef OPENSSL_NO_SRP
    PACKET prime, generator, salt, server_pub;

    if (!PACKET_get_length_prefixed_2(pkt, &prime)
        || !PACKET_get_length_prefixed_2(pkt, &generator)
        || !PACKET_get_length_prefixed_1(pkt, &salt)
        || !PACKET_get_length_prefixed_2(pkt, &server_pub)) {
        *al = SSL_AD_DECODE_ERROR;
        SSLerr(SSL_F_TLS_PROCESS_SKE_SRP, SSL_R_LENGTH_MISMATCH);
        return 0;
    }

    if ((s->srp_ctx.N =
         BN_bin2bn(PACKET_data(&prime),
                   PACKET_remaining(&prime), NULL)) == NULL
        || (s->srp_ctx.g =
            BN_bin2bn(PACKET_data(&generator),
                      PACKET_remaining(&generator), NULL)) == NULL
        || (s->srp_ctx.s =
            BN_bin2bn(PACKET_data(&salt),
                      PACKET_remaining(&salt), NULL)) == NULL
        || (s->srp_ctx.B =
            BN_bin2bn(PACKET_data(&server_pub),
                      PACKET_remaining(&server_pub), NULL)) == NULL) {
        *al = SSL_AD_INTERNAL_ERROR;
        SSLerr(SSL_F_TLS_PROCESS_SKE_SRP, ERR_R_BN_LIB);
        return 0;
    }

    if (!srp_verify_server_param(s, al)) {
        *al = SSL_AD_DECODE_ERROR;
        SSLerr(SSL_F_TLS_PROCESS_SKE_SRP, SSL_R_BAD_SRP_PARAMETERS);
        return 0;
    }

    /* We must check if there is a certificate */
    if (s->s3->tmp.new_cipher->algorithm_auth & (SSL_aRSA | SSL_aDSS))
        *pkey = X509_get0_pubkey(s->session->peer);

    return 1;
#else
    SSLerr(SSL_F_TLS_PROCESS_SKE_SRP, ERR_R_INTERNAL_ERROR);
    *al = SSL_AD_INTERNAL_ERROR;
    return 0;
#endif
}


MSG_PROCESS_RETURN tls_process_cert_status(SSL *s, PACKET *pkt)
{
    int al;
    unsigned long resplen;
    unsigned int type;

    if (!PACKET_get_1(pkt, &type)
        || type != TLSEXT_STATUSTYPE_ocsp) {
        al = SSL_AD_DECODE_ERROR;
        SSLerr(SSL_F_TLS_PROCESS_CERT_STATUS, SSL_R_UNSUPPORTED_STATUS_TYPE);
        goto f_err;
    }
    if (!PACKET_get_net_3(pkt, &resplen)
        || PACKET_remaining(pkt) != resplen) {
        al = SSL_AD_DECODE_ERROR;
        SSLerr(SSL_F_TLS_PROCESS_CERT_STATUS, SSL_R_LENGTH_MISMATCH);
        goto f_err;
    }
    s->tlsext_ocsp_resp = OPENSSL_malloc(resplen);
    if (s->tlsext_ocsp_resp == NULL) {
        al = SSL_AD_INTERNAL_ERROR;
        SSLerr(SSL_F_TLS_PROCESS_CERT_STATUS, ERR_R_MALLOC_FAILURE);
        goto f_err;
    }
    if (!PACKET_copy_bytes(pkt, s->tlsext_ocsp_resp, resplen)) {
        al = SSL_AD_DECODE_ERROR;
        SSLerr(SSL_F_TLS_PROCESS_CERT_STATUS, SSL_R_LENGTH_MISMATCH);
        goto f_err;
    }
    s->tlsext_ocsp_resplen = resplen;
    return MSG_PROCESS_CONTINUE_READING;
 f_err:
    ssl3_send_alert(s, SSL3_AL_FATAL, al);
    ossl_statem_set_error(s);
    return MSG_PROCESS_ERROR;
}


static void test_wl4435()
{
  MYSQL_STMT *stmt;
  int        rc;
  char query[MAX_TEST_QUERY_LENGTH];

  char       str_data[20][WL4435_STRING_SIZE];
  double     dbl_data[20];
  char       dec_data[20][WL4435_STRING_SIZE];
  int        int_data[20];
  ulong      str_length= WL4435_STRING_SIZE;
  my_bool    is_null;
  MYSQL_BIND ps_params[WL4435_NUM_PARAMS];

  int exec_counter;

  myheader("test_wl4435");
  mct_start_logging("test_wl4435");

  rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1");
  myquery(rc);

  rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p2");
  myquery(rc);

  rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
  myquery(rc);

  rc= mysql_query(mysql, "DROP TABLE IF EXISTS t2");
  myquery(rc);

  rc= mysql_query(mysql, "CREATE TABLE t1(a1 INT, a2 CHAR(32), "
                       "  a3 DOUBLE(4, 2), a4 DECIMAL(3, 1))");
  myquery(rc);

  rc= mysql_query(mysql, "CREATE TABLE t2(b0 INT, b1 INT, b2 CHAR(32), "
                       "  b3 DOUBLE(4, 2), b4 DECIMAL(3, 1))");
  myquery(rc);

  rc= mysql_query(mysql, "INSERT INTO t1 VALUES"
    "(1, '11', 12.34, 56.7), "
    "(2, '12', 56.78, 90.1), "
    "(3, '13', 23.45, 67.8)");
  myquery(rc);

  rc= mysql_query(mysql, "INSERT INTO t2 VALUES"
    "(100, 10, '110', 70.70, 10.1), "
    "(200, 20, '120', 80.80, 20.2), "
    "(300, 30, '130', 90.90, 30.3)");
  myquery(rc);

  rc= mysql_query(mysql,
    "CREATE PROCEDURE p1("
    "   IN v0 INT, "
    "   OUT v_str_1 CHAR(32), "
    "   OUT v_dbl_1 DOUBLE(4, 2), "
    "   OUT v_dec_1 DECIMAL(6, 3), "
    "   OUT v_int_1 INT, "
    "   IN v1 INT, "
    "   INOUT v_str_2 CHAR(64), "
    "   INOUT v_dbl_2 DOUBLE(5, 3), "
    "   INOUT v_dec_2 DECIMAL(7, 4), "
    "   INOUT v_int_2 INT)"
    "BEGIN "
    "   SET v0 = -1; "
    "   SET v1 = -1; "
    "   SET v_str_1 = 'test_1'; "
    "   SET v_dbl_1 = 12.34; "
    "   SET v_dec_1 = 567.891; "
    "   SET v_int_1 = 2345; "
    "   SET v_str_2 = 'test_2'; "
    "   SET v_dbl_2 = 67.891; "
    "   SET v_dec_2 = 234.6789; "
    "   SET v_int_2 = 6789; "
    "   SELECT * FROM t1; "
    "   SELECT * FROM t2; "
    "END");
  myquery(rc);

  rc= mysql_query(mysql,
    "CREATE PROCEDURE p2("
    "   IN i1 VARCHAR(255) CHARACTER SET koi8r, "
    "   OUT o1 VARCHAR(255) CHARACTER SET cp1251, "
    "   OUT o2 VARBINARY(255)) "
    "BEGIN "
    "   SET o1 = i1; "
    "   SET o2 = i1; "
    "END");
  myquery(rc);

  strmov(query, "CALL p1(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
  stmt= mysql_simple_prepare(mysql, query);
  check_stmt(stmt);

  /* Init PS-parameters. */

  bzero((char *) ps_params, sizeof (ps_params));

  /* - v0 -- INT */

  ps_params[0].buffer_type= MYSQL_TYPE_LONG;
  ps_params[0].buffer= (char *) &int_data[0];
  ps_params[0].length= 0;
  ps_params[0].is_null= 0;

  /* - v_str_1 -- CHAR(32) */

  ps_params[1].buffer_type= MYSQL_TYPE_STRING;
  ps_params[1].buffer= (char *) str_data[0];
  ps_params[1].buffer_length= WL4435_STRING_SIZE;
  ps_params[1].length= &str_length;
  ps_params[1].is_null= 0;

  /* - v_dbl_1 -- DOUBLE */

  ps_params[2].buffer_type= MYSQL_TYPE_DOUBLE;
  ps_params[2].buffer= (char *) &dbl_data[0];
  ps_params[2].length= 0;
  ps_params[2].is_null= 0;

  /* - v_dec_1 -- DECIMAL */

  ps_params[3].buffer_type= MYSQL_TYPE_NEWDECIMAL;
  ps_params[3].buffer= (char *) dec_data[0];
  ps_params[3].buffer_length= WL4435_STRING_SIZE;
  ps_params[3].length= 0;
  ps_params[3].is_null= 0;

  /* - v_int_1 -- INT */

  ps_params[4].buffer_type= MYSQL_TYPE_LONG;
  ps_params[4].buffer= (char *) &int_data[0];
  ps_params[4].length= 0;
  ps_params[4].is_null= 0;

  /* - v1 -- INT */

  ps_params[5].buffer_type= MYSQL_TYPE_LONG;
  ps_params[5].buffer= (char *) &int_data[0];
  ps_params[5].length= 0;
  ps_params[5].is_null= 0;

  /* - v_str_2 -- CHAR(32) */

  ps_params[6].buffer_type= MYSQL_TYPE_STRING;
  ps_params[6].buffer= (char *) str_data[0];
  ps_params[6].buffer_length= WL4435_STRING_SIZE;
  ps_params[6].length= &str_length;
  ps_params[6].is_null= 0;

  /* - v_dbl_2 -- DOUBLE */

  ps_params[7].buffer_type= MYSQL_TYPE_DOUBLE;
  ps_params[7].buffer= (char *) &dbl_data[0];
  ps_params[7].length= 0;
  ps_params[7].is_null= 0;

  /* - v_dec_2 -- DECIMAL */

  ps_params[8].buffer_type= MYSQL_TYPE_DECIMAL;
  ps_params[8].buffer= (char *) dec_data[0];
  ps_params[8].buffer_length= WL4435_STRING_SIZE;
  ps_params[8].length= 0;
  ps_params[8].is_null= 0;

  /* - v_int_2 -- INT */

  ps_params[9].buffer_type= MYSQL_TYPE_LONG;
  ps_params[9].buffer= (char *) &int_data[0];
  ps_params[9].length= 0;
  ps_params[9].is_null= 0;

  /* Bind parameters. */

  rc= mysql_stmt_bind_param(stmt, ps_params);

  /* Execute! */

  for (exec_counter= 0; exec_counter < 3; ++exec_counter)
  {
    int i;
    int num_fields;
    MYSQL_BIND *rs_bind;

    mct_log("\nexec_counter: %d\n", (int) exec_counter);

    rc= mysql_stmt_execute(stmt);
    check_execute(stmt, rc);

    while (1)
    {
      MYSQL_FIELD *fields;

      MYSQL_RES *rs_metadata= mysql_stmt_result_metadata(stmt);

      num_fields= mysql_stmt_field_count(stmt);
      fields= mysql_fetch_fields(rs_metadata);

      rs_bind= (MYSQL_BIND *) malloc(sizeof (MYSQL_BIND) * num_fields);
      bzero(rs_bind, sizeof (MYSQL_BIND) * num_fields);

      mct_log("num_fields: %d\n", (int) num_fields);

      for (i = 0; i < num_fields; ++i)
      {
        mct_log("  - %d: name: '%s'/'%s'; table: '%s'/'%s'; "
                "db: '%s'; catalog: '%s'; length: %d; max_length: %d; "
                "type: %d; decimals: %d\n",
                (int) i,
                (const char *) fields[i].name,
                (const char *) fields[i].org_name,
                (const char *) fields[i].table,
                (const char *) fields[i].org_table,
                (const char *) fields[i].db,
                (const char *) fields[i].catalog,
                (int) fields[i].length,
                (int) fields[i].max_length,
                (int) fields[i].type,
                (int) fields[i].decimals);

        rs_bind[i].buffer_type= fields[i].type;
        rs_bind[i].is_null= &is_null;

        switch (fields[i].type)
        {
          case MYSQL_TYPE_LONG:
            rs_bind[i].buffer= (char *) &(int_data[i]);
            rs_bind[i].buffer_length= sizeof (int_data);
            break;

          case MYSQL_TYPE_STRING:
            rs_bind[i].buffer= (char *) str_data[i];
            rs_bind[i].buffer_length= WL4435_STRING_SIZE;
            rs_bind[i].length= &str_length;
            break;

          case MYSQL_TYPE_DOUBLE:
            rs_bind[i].buffer= (char *) &dbl_data[i];
            rs_bind[i].buffer_length= sizeof (dbl_data);
            break;

          case MYSQL_TYPE_NEWDECIMAL:
            rs_bind[i].buffer= (char *) dec_data[i];
            rs_bind[i].buffer_length= WL4435_STRING_SIZE;
            rs_bind[i].length= &str_length;
            break;

          default:
            fprintf(stderr, "ERROR: unexpected type: %d.\n", fields[i].type);
            exit(1);
        }
      }

      rc= mysql_stmt_bind_result(stmt, rs_bind);
      check_execute(stmt, rc);

      mct_log("Data:\n");

      while (1)
      {
        int rc= mysql_stmt_fetch(stmt);

        if (rc == 1 || rc == MYSQL_NO_DATA)
          break;

        mct_log(" ");

        for (i = 0; i < num_fields; ++i)
        {
          switch (rs_bind[i].buffer_type)
          {
            case MYSQL_TYPE_LONG:
              mct_log(" int: %ld;",
                      (long) *((int *) rs_bind[i].buffer));
              break;

            case MYSQL_TYPE_STRING:
              mct_log(" str: '%s';",
                      (char *) rs_bind[i].buffer);
              break;

            case MYSQL_TYPE_DOUBLE:
              mct_log(" dbl: %lf;",
                      (double) *((double *) rs_bind[i].buffer));
              break;

            case MYSQL_TYPE_NEWDECIMAL:
              mct_log(" dec: '%s';",
                      (char *) rs_bind[i].buffer);
              break;

            default:
              printf("  unexpected type (%d)\n",
                rs_bind[i].buffer_type);
          }
        }
        mct_log("\n");
      }

      mct_log("EOF\n");

      rc= mysql_stmt_next_result(stmt);
      mct_log("mysql_stmt_next_result(): %d; field_count: %d\n",
              (int) rc, (int) mysql->field_count);

      free(rs_bind);
      mysql_free_result(rs_metadata);

      if (rc > 0)
      {
        printf("Error: %s (errno: %d)\n",
               mysql_stmt_error(stmt), mysql_stmt_errno(stmt));
        DIE(rc > 0);
      }

      if (rc)
        break;

      if (!mysql->field_count)
      {
        /* This is the last OK-packet. No more resultsets. */
        break;
      }
    }

  }

  mysql_stmt_close(stmt);

  mct_close_log();

  rc= mysql_commit(mysql);
  myquery(rc);

  /* i18n part of test case. */

  {
    const char *str_koi8r= "\xee\xd5\x2c\x20\xda\xc1\x20\xd2\xd9\xc2\xc1\xcc\xcb\xd5";
    const char *str_cp1251= "\xcd\xf3\x2c\x20\xe7\xe0\x20\xf0\xfb\xe1\xe0\xeb\xea\xf3";
    char o1_buffer[255];
    ulong o1_length;
    char o2_buffer[255];
    ulong o2_length;

    MYSQL_BIND rs_bind[2];

    strmov(query, "CALL p2(?, ?, ?)");
    stmt= mysql_simple_prepare(mysql, query);
    check_stmt(stmt);

    /* Init PS-parameters. */

    bzero((char *) ps_params, sizeof (ps_params));

    ps_params[0].buffer_type= MYSQL_TYPE_STRING;
    ps_params[0].buffer= (char *) str_koi8r;
    ps_params[0].buffer_length= strlen(str_koi8r);

    ps_params[1].buffer_type= MYSQL_TYPE_STRING;
    ps_params[1].buffer= o1_buffer;
    ps_params[1].buffer_length= 0;

    ps_params[2].buffer_type= MYSQL_TYPE_STRING;
    ps_params[2].buffer= o2_buffer;
    ps_params[2].buffer_length= 0;

    /* Bind parameters. */

    rc= mysql_stmt_bind_param(stmt, ps_params);
    check_execute(stmt, rc);

    /* Prevent converting to character_set_results. */

    rc= mysql_query(mysql, "SET NAMES binary");
    myquery(rc);

    /* Execute statement. */

    rc= mysql_stmt_execute(stmt);
    check_execute(stmt, rc);

    /* Bind result. */

    bzero(rs_bind, sizeof (rs_bind));

    rs_bind[0].buffer_type= MYSQL_TYPE_STRING;
    rs_bind[0].buffer= o1_buffer;
    rs_bind[0].buffer_length= sizeof (o1_buffer);
    rs_bind[0].length= &o1_length;

    rs_bind[1].buffer_type= MYSQL_TYPE_BLOB;
    rs_bind[1].buffer= o2_buffer;
    rs_bind[1].buffer_length= sizeof (o2_buffer);
    rs_bind[1].length= &o2_length;

    rc= mysql_stmt_bind_result(stmt, rs_bind);
    check_execute(stmt, rc);

    /* Fetch result. */

    rc= mysql_stmt_fetch(stmt);
    check_execute(stmt, rc);

    /* Check result. */

    DIE_UNLESS(o1_length == strlen(str_cp1251));
    DIE_UNLESS(o2_length == strlen(str_koi8r));
    DIE_UNLESS(!memcmp(o1_buffer, str_cp1251, o1_length));
    DIE_UNLESS(!memcmp(o2_buffer, str_koi8r, o2_length));

    rc= mysql_stmt_fetch(stmt);
    DIE_UNLESS(rc == MYSQL_NO_DATA);

    rc= mysql_stmt_next_result(stmt);
    DIE_UNLESS(rc == 0 && mysql->field_count == 0);

    mysql_stmt_close(stmt);

    rc= mysql_commit(mysql);
    myquery(rc);
  }
}


static void test_bind_result_ext()
{
  MYSQL_STMT *stmt;
  int        rc, i;
  uchar      t_data;
  short      s_data;
  int        i_data;
  longlong   b_data;
  float      f_data;
  double     d_data;
  char       szData[20], bData[20];
  ulong       szLength, bLength;
  MYSQL_BIND my_bind[8];
  ulong      length[8];
  my_bool    is_null[8];
  char	     llbuf[22];
  myheader("test_bind_result_ext");

  rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_result");
  myquery(rc);

  rc= mysql_query(mysql, "CREATE TABLE test_bind_result(c1 tinyint, "
                                                      " c2 smallint, "
                                                      " c3 int, c4 bigint, "
                                                      " c5 float, c6 double, "
                                                      " c7 varbinary(10), "
                                                      " c8 varchar(50))");
  myquery(rc);

  rc= mysql_query(mysql, "INSERT INTO test_bind_result "
                         "VALUES (19, 2999, 3999, 4999999, "
                         " 2345.6, 5678.89563, 'venu', 'mysql')");
  myquery(rc);

  rc= mysql_commit(mysql);
  myquery(rc);

  bzero((char*) my_bind, sizeof(my_bind));
  for (i= 0; i < (int) array_elements(my_bind); i++)
  {
    my_bind[i].length=  &length[i];
    my_bind[i].is_null= &is_null[i];
  }

  my_bind[0].buffer_type= MYSQL_TYPE_TINY;
  my_bind[0].buffer= (void *)&t_data;

  my_bind[1].buffer_type= MYSQL_TYPE_SHORT;
  my_bind[2].buffer_type= MYSQL_TYPE_LONG;

  my_bind[3].buffer_type= MYSQL_TYPE_LONGLONG;
  my_bind[1].buffer= (void *)&s_data;

  my_bind[2].buffer= (void *)&i_data;
  my_bind[3].buffer= (void *)&b_data;

  my_bind[4].buffer_type= MYSQL_TYPE_FLOAT;
  my_bind[4].buffer= (void *)&f_data;

  my_bind[5].buffer_type= MYSQL_TYPE_DOUBLE;
  my_bind[5].buffer= (void *)&d_data;

  my_bind[6].buffer_type= MYSQL_TYPE_STRING;
  my_bind[6].buffer= (void *)szData;
  my_bind[6].buffer_length= sizeof(szData);
  my_bind[6].length= &szLength;

  my_bind[7].buffer_type= MYSQL_TYPE_TINY_BLOB;
  my_bind[7].buffer= (void *)&bData;
  my_bind[7].length= &bLength;
  my_bind[7].buffer_length= sizeof(bData);

  stmt= mysql_simple_prepare(mysql, "select * from test_bind_result");
  check_stmt(stmt);

  rc= mysql_stmt_bind_result(stmt, my_bind);
  check_execute(stmt, rc);

  rc= mysql_stmt_execute(stmt);
  check_execute(stmt, rc);

  rc= mysql_stmt_fetch(stmt);
  check_execute(stmt, rc);

  if (!opt_silent)
  {
    fprintf(stdout, "\n data (tiny)   : %d", t_data);
    fprintf(stdout, "\n data (short)  : %d", s_data);
    fprintf(stdout, "\n data (int)    : %d", i_data);
    fprintf(stdout, "\n data (big)    : %s", llstr(b_data, llbuf));

    fprintf(stdout, "\n data (float)  : %f", f_data);
    fprintf(stdout, "\n data (double) : %f", d_data);

    fprintf(stdout, "\n data (str)    : %s(%lu)", szData, szLength);

    bData[bLength]= '\0';                         /* bData is binary */
    fprintf(stdout, "\n data (bin)    : %s(%lu)", bData, bLength);
  }

  DIE_UNLESS(t_data == 19);
  DIE_UNLESS(s_data == 2999);
  DIE_UNLESS(i_data == 3999);
  DIE_UNLESS(b_data == 4999999);
  /*DIE_UNLESS(f_data == 2345.60);*/
  /*DIE_UNLESS(d_data == 5678.89563);*/
  DIE_UNLESS(strcmp(szData, "venu") == 0);
  DIE_UNLESS(strncmp(bData, "mysql", 5) == 0);
  DIE_UNLESS(szLength == 4);
  DIE_UNLESS(bLength == 5);

  rc= mysql_stmt_fetch(stmt);
  DIE_UNLESS(rc == MYSQL_NO_DATA);

  mysql_stmt_close(stmt);
}


static void test_view_star()
{
  MYSQL_STMT *stmt;
  int rc, i;
  MYSQL_BIND      my_bind[8];
  char            parms[8][100];
  ulong           length[8];
  const char *query= "SELECT * FROM vt1 WHERE a IN (?,?)";

  myheader("test_view_star");

  rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1, vt1");
  myquery(rc);
  rc= mysql_query(mysql, "DROP VIEW IF EXISTS t1, vt1");
  myquery(rc);
  rc= mysql_query(mysql, "CREATE TABLE t1 (a int)");
  myquery(rc);
  rc= mysql_query(mysql, "CREATE VIEW vt1 AS SELECT a FROM t1");
  myquery(rc);
  bzero((char*) my_bind, sizeof(my_bind));
  for (i= 0; i < 2; i++) {
    sprintf((char *)&parms[i], "%d", i);
    my_bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
    my_bind[i].buffer = (char *)&parms[i];
    my_bind[i].buffer_length = 100;
    my_bind[i].is_null = 0;
    my_bind[i].length = &length[i];
    length[i] = 1;
  }

  stmt= mysql_stmt_init(mysql);
  rc= mysql_stmt_prepare(stmt, query, strlen(query));
  check_execute(stmt, rc);

  rc= mysql_stmt_bind_param(stmt, my_bind);
  check_execute(stmt,rc);

  for (i= 0; i < 3; i++)
  {
    rc= mysql_stmt_execute(stmt);
    check_execute(stmt, rc);
    rc= my_process_stmt_result(stmt);
    DIE_UNLESS(0 == rc);
  }

  mysql_stmt_close(stmt);

  rc= mysql_query(mysql, "DROP TABLE t1");
  myquery(rc);
  rc= mysql_query(mysql, "DROP VIEW vt1");
  myquery(rc);
}


mysql_ssl_set(MYSQL *mysql __attribute__((unused)) ,
	      const char *key __attribute__((unused)),
	      const char *cert __attribute__((unused)),
	      const char *ca __attribute__((unused)),
	      const char *capath __attribute__((unused)),
	      const char *cipher __attribute__((unused)))
{
  DBUG_ENTER("mysql_ssl_set");
#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
  my_free(mysql->options.ssl_key);
  my_free(mysql->options.ssl_cert);
  my_free(mysql->options.ssl_ca);
  my_free(mysql->options.ssl_capath);
  my_free(mysql->options.ssl_cipher);
  mysql->options.ssl_key=    set_ssl_option_unpack_path(key);
  mysql->options.ssl_cert=   set_ssl_option_unpack_path(cert);
  mysql->options.ssl_ca=     set_ssl_option_unpack_path(ca);
  mysql->options.ssl_capath= set_ssl_option_unpack_path(capath);
  mysql->options.ssl_cipher= strdup_if_not_null(cipher);
  mysql->options.use_ssl= TRUE;
#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
  DBUG_RETURN(0);
}


static void test_bug6058()
{
  MYSQL_STMT *stmt;
  MYSQL_BIND my_bind[1];
  MYSQL_RES *res;
  MYSQL_ROW row;
  const char *stmt_text;
  char buffer[30];
  ulong length;
  int rc;

  myheader("test_bug6058");

  rc= mysql_query(mysql, "SET SQL_MODE=''");
  myquery(rc);

  stmt_text= "SELECT CAST('0000-00-00' AS DATE)";

  rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
  myquery(rc);
  res= mysql_store_result(mysql);
  row= mysql_fetch_row(res);

  stmt= mysql_stmt_init(mysql);
  rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
  check_execute(stmt, rc);
  rc= mysql_stmt_execute(stmt);
  check_execute(stmt, rc);

  bzero((char*) my_bind, sizeof(my_bind));
  my_bind[0].buffer_type    = MYSQL_TYPE_STRING;
  my_bind[0].buffer         = &buffer;
  my_bind[0].buffer_length  = sizeof(buffer);
  my_bind[0].length         = &length;

  mysql_stmt_bind_result(stmt, my_bind);
  rc= mysql_stmt_fetch(stmt);
  DIE_UNLESS(rc == 0);

  if (!opt_silent)
  {
    printf("Result from query: %s\n", row[0]);
    printf("Result from prepared statement: %s\n", buffer);
  }

  DIE_UNLESS(strcmp(row[0], buffer) == 0);

  mysql_free_result(res);
  mysql_stmt_close(stmt);
}


static void test_wl4166_1()
{
  MYSQL_STMT *stmt;
  int        int_data;
  char       str_data[50];
  char       tiny_data;
  short      small_data;
  longlong   big_data;
  float      real_data;
  double     double_data;
  ulong      length[7];
  my_bool    is_null[7];
  MYSQL_BIND my_bind[7];
  int rc;
  int i;

  myheader("test_wl4166_1");

  rc= mysql_query(mysql, "DROP TABLE IF EXISTS table_4166");
  myquery(rc);

  rc= mysql_query(mysql, "CREATE TABLE table_4166(col1 tinyint NOT NULL, "
                         "col2 varchar(15), col3 int, "
                         "col4 smallint, col5 bigint, "
                         "col6 float, col7 double, "
                         "colX varchar(10) default NULL)");
  myquery(rc);

  stmt= mysql_simple_prepare(mysql,
    "INSERT INTO table_4166(col1, col2, col3, col4, col5, col6, col7) "
    "VALUES(?, ?, ?, ?, ?, ?, ?)");
  check_stmt(stmt);

  verify_param_count(stmt, 7);

  bzero(my_bind, sizeof(my_bind));
  /* tinyint */
  my_bind[0].buffer_type= MYSQL_TYPE_TINY;
  my_bind[0].buffer= (void *)&tiny_data;
  /* string */
  my_bind[1].buffer_type= MYSQL_TYPE_STRING;
  my_bind[1].buffer= (void *)str_data;
  my_bind[1].buffer_length= 1000;                  /* Max string length */
  /* integer */
  my_bind[2].buffer_type= MYSQL_TYPE_LONG;
  my_bind[2].buffer= (void *)&int_data;
  /* short */
  my_bind[3].buffer_type= MYSQL_TYPE_SHORT;
  my_bind[3].buffer= (void *)&small_data;
  /* bigint */
  my_bind[4].buffer_type= MYSQL_TYPE_LONGLONG;
  my_bind[4].buffer= (void *)&big_data;
  /* float */
  my_bind[5].buffer_type= MYSQL_TYPE_FLOAT;
  my_bind[5].buffer= (void *)&real_data;
  /* double */
  my_bind[6].buffer_type= MYSQL_TYPE_DOUBLE;
  my_bind[6].buffer= (void *)&double_data;

  for (i= 0; i < (int) array_elements(my_bind); i++)
  {
    my_bind[i].length= &length[i];
    my_bind[i].is_null= &is_null[i];
    is_null[i]= 0;
  }

  rc= mysql_stmt_bind_param(stmt, my_bind);
  check_execute(stmt, rc);

  int_data= 320;
  small_data= 1867;
  big_data= 1000;
  real_data= 2;
  double_data= 6578.001;

  /* now, execute the prepared statement to insert 10 records.. */
  for (tiny_data= 0; tiny_data < 10; tiny_data++)
  {
    length[1]= sprintf(str_data, "MySQL%d", int_data);
    rc= mysql_stmt_execute(stmt);
    check_execute(stmt, rc);
    int_data += 25;
    small_data += 10;
    big_data += 100;
    real_data += 1;
    double_data += 10.09;
  }

  /* force a re-prepare with some DDL */

  rc= mysql_query(mysql,
    "ALTER TABLE table_4166 change colX colX varchar(20) default NULL");
  myquery(rc);

  /*
    execute the prepared statement again,
    without changing the types of parameters already bound.
  */

  for (tiny_data= 50; tiny_data < 60; tiny_data++)
  {
    length[1]= sprintf(str_data, "MySQL%d", int_data);
    rc= mysql_stmt_execute(stmt);
    check_execute(stmt, rc);
    int_data += 25;
    small_data += 10;
    big_data += 100;
    real_data += 1;
    double_data += 10.09;
  }

  mysql_stmt_close(stmt);

  rc= mysql_query(mysql, "DROP TABLE table_4166");
  myquery(rc);
}


static void test_bug1500()
{
  MYSQL_STMT *stmt;
  MYSQL_BIND my_bind[3];
  int        rc;
  int32 int_data[3]= {2, 3, 4};
  const char *data;

  myheader("test_bug1500");

  rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bg1500");
  myquery(rc);

  rc= mysql_query(mysql, "CREATE TABLE test_bg1500 (i INT)");
  myquery(rc);

  rc= mysql_query(mysql, "INSERT INTO test_bg1500 VALUES (1), (2)");
  myquery(rc);

  rc= mysql_commit(mysql);
  myquery(rc);

  stmt= mysql_simple_prepare(mysql, "SELECT i FROM test_bg1500 WHERE i IN (?, ?, ?)");
  check_stmt(stmt);
  verify_param_count(stmt, 3);

  /*
    We need to bzero bind structure because mysql_stmt_bind_param checks all
    its members.
  */
  bzero((char*) my_bind, sizeof(my_bind));

  my_bind[0].buffer= (void *)int_data;
  my_bind[0].buffer_type= MYSQL_TYPE_LONG;
  my_bind[2]= my_bind[1]= my_bind[0];
  my_bind[1].buffer= (void *)(int_data + 1);
  my_bind[2].buffer= (void *)(int_data + 2);

  rc= mysql_stmt_bind_param(stmt, my_bind);
  check_execute(stmt, rc);

  rc= mysql_stmt_execute(stmt);
  check_execute(stmt, rc);

  rc= my_process_stmt_result(stmt);
  DIE_UNLESS(rc == 1);

  mysql_stmt_close(stmt);

  rc= mysql_query(mysql, "DROP TABLE test_bg1500");
  myquery(rc);

  rc= mysql_query(mysql, "CREATE TABLE test_bg1500 (s VARCHAR(25), FULLTEXT(s)) engine=MyISAM");
  myquery(rc);

  rc= mysql_query(mysql,
        "INSERT INTO test_bg1500 VALUES ('Gravedigger'), ('Greed'), ('Hollow Dogs')");
  myquery(rc);

  rc= mysql_commit(mysql);
  myquery(rc);

  stmt= mysql_simple_prepare(mysql,
          "SELECT s FROM test_bg1500 WHERE MATCH (s) AGAINST (?)");
  check_stmt(stmt);

  verify_param_count(stmt, 1);

  data= "Dogs";
  my_bind[0].buffer_type= MYSQL_TYPE_STRING;
  my_bind[0].buffer= (void *) data;
  my_bind[0].buffer_length= strlen(data);
  my_bind[0].is_null= 0;
  my_bind[0].length= 0;

  rc= mysql_stmt_bind_param(stmt, my_bind);
  check_execute(stmt, rc);

  rc= mysql_stmt_execute(stmt);
  check_execute(stmt, rc);

  rc= my_process_stmt_result(stmt);
  DIE_UNLESS(rc == 1);

  mysql_stmt_close(stmt);

  /* This should work too */
  stmt= mysql_simple_prepare(mysql,
          "SELECT s FROM test_bg1500 WHERE MATCH (s) AGAINST (CONCAT(?, 'digger'))");
  check_stmt(stmt);

  verify_param_count(stmt, 1);

  data= "Grave";
  my_bind[0].buffer_type= MYSQL_TYPE_STRING;
  my_bind[0].buffer= (void *) data;
  my_bind[0].buffer_length= strlen(data);

  rc= mysql_stmt_bind_param(stmt, my_bind);
  check_execute(stmt, rc);

  rc= mysql_stmt_execute(stmt);
  check_execute(stmt, rc);

  rc= my_process_stmt_result(stmt);
  DIE_UNLESS(rc == 1);

  mysql_stmt_close(stmt);
}


static void test_bug1644()
{
  MYSQL_STMT *stmt;
  MYSQL_RES *result;
  MYSQL_ROW row;
  MYSQL_BIND my_bind[4];
  int num;
  my_bool isnull;
  int rc, i;
  char query[MAX_TEST_QUERY_LENGTH];

  myheader("test_bug1644");

  rc= mysql_query(mysql, "DROP TABLE IF EXISTS foo_dfr");
  myquery(rc);

  rc= mysql_query(mysql,
           "CREATE TABLE foo_dfr(col1 int, col2 int, col3 int, col4 int);");
  myquery(rc);

  strmov(query, "INSERT INTO foo_dfr VALUES (?, ?, ?, ? )");
  stmt= mysql_simple_prepare(mysql, query);
  check_stmt(stmt);

  verify_param_count(stmt, 4);

  /* Always bzero all members of bind parameter */
  bzero((char*) my_bind, sizeof(my_bind));

  num= 22;
  isnull= 0;
  for (i= 0 ; i < 4 ; i++)
  {
    my_bind[i].buffer_type= MYSQL_TYPE_LONG;
    my_bind[i].buffer= (void *)#
    my_bind[i].is_null= &isnull;
  }

  rc= mysql_stmt_bind_param(stmt, my_bind);
  check_execute(stmt, rc);

  rc= mysql_stmt_execute(stmt);
  check_execute(stmt, rc);

  isnull= 1;
  for (i= 0 ; i < 4 ; i++)
    my_bind[i].is_null= &isnull;

  rc= mysql_stmt_bind_param(stmt, my_bind);
  check_execute(stmt, rc);

  rc= mysql_stmt_execute(stmt);
  check_execute(stmt, rc);

  isnull= 0;
  num= 88;
  for (i= 0 ; i < 4 ; i++)
    my_bind[i].is_null= &isnull;

  rc= mysql_stmt_bind_param(stmt, my_bind);
  check_execute(stmt, rc);

  rc= mysql_stmt_execute(stmt);
  check_execute(stmt, rc);

  mysql_stmt_close(stmt);

  rc= mysql_query(mysql, "SELECT * FROM foo_dfr");
  myquery(rc);

  result= mysql_store_result(mysql);
  mytest(result);

  rc= my_process_result_set(result);
  DIE_UNLESS(rc == 3);

  mysql_data_seek(result, 0);

  row= mysql_fetch_row(result);
  mytest(row);
  for (i= 0 ; i < 4 ; i++)
  {
    DIE_UNLESS(strcmp(row[i], "22") == 0);
  }
  row= mysql_fetch_row(result);
  mytest(row);
  for (i= 0 ; i < 4 ; i++)
  {
    DIE_UNLESS(row[i] == 0);
  }
  row= mysql_fetch_row(result);
  mytest(row);
  for (i= 0 ; i < 4 ; i++)
  {
    DIE_UNLESS(strcmp(row[i], "88") == 0);
  }
  row= mysql_fetch_row(result);
  mytest_r(row);

  mysql_free_result(result);
}


static void test_bug3035()
{
  MYSQL_STMT *stmt;
  int rc;
  MYSQL_BIND bind_array[12], *my_bind= bind_array, *bind_end= my_bind + 12;
  int8 int8_val;
  uint8 uint8_val;
  int16 int16_val;
  uint16 uint16_val;
  int32 int32_val;
  uint32 uint32_val;
  longlong int64_val;
  ulonglong uint64_val;
  double double_val, udouble_val, double_tmp;
  char longlong_as_string[22], ulonglong_as_string[22];

  /* mins and maxes */
  const int8 int8_min= -128;
  const int8 int8_max= 127;
  const uint8 uint8_min= 0;
  const uint8 uint8_max= 255;

  const int16 int16_min= -32768;
  const int16 int16_max= 32767;
  const uint16 uint16_min= 0;
  const uint16 uint16_max= 65535;

  const int32 int32_max= 2147483647L;
  const int32 int32_min= -int32_max - 1;
  const uint32 uint32_min= 0;
  const uint32 uint32_max= 4294967295U;

  /* it might not work okay everyplace */
  const longlong int64_max= LL(9223372036854775807);
  const longlong int64_min= -int64_max - 1;

  const ulonglong uint64_min= 0U;
  const ulonglong uint64_max= ULL(18446744073709551615);

  const char *stmt_text;

  myheader("test_bug3035");

  stmt_text= "DROP TABLE IF EXISTS t1";
  rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
  myquery(rc);

  stmt_text= "CREATE TABLE t1 (i8 TINYINT, ui8 TINYINT UNSIGNED, "
                              "i16 SMALLINT, ui16 SMALLINT UNSIGNED, "
                              "i32 INT, ui32 INT UNSIGNED, "
                              "i64 BIGINT, ui64 BIGINT UNSIGNED, "
                              "id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT)";
  rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
  myquery(rc);

  bzero((char*) bind_array, sizeof(bind_array));

  for (my_bind= bind_array; my_bind < bind_end; my_bind++)
    my_bind->error= &my_bind->error_value;

  bind_array[0].buffer_type= MYSQL_TYPE_TINY;
  bind_array[0].buffer= (void *) &int8_val;

  bind_array[1].buffer_type= MYSQL_TYPE_TINY;
  bind_array[1].buffer= (void *) &uint8_val;
  bind_array[1].is_unsigned= 1;

  bind_array[2].buffer_type= MYSQL_TYPE_SHORT;
  bind_array[2].buffer= (void *) &int16_val;

  bind_array[3].buffer_type= MYSQL_TYPE_SHORT;
  bind_array[3].buffer= (void *) &uint16_val;
  bind_array[3].is_unsigned= 1;

  bind_array[4].buffer_type= MYSQL_TYPE_LONG;
  bind_array[4].buffer= (void *) &int32_val;

  bind_array[5].buffer_type= MYSQL_TYPE_LONG;
  bind_array[5].buffer= (void *) &uint32_val;
  bind_array[5].is_unsigned= 1;

  bind_array[6].buffer_type= MYSQL_TYPE_LONGLONG;
  bind_array[6].buffer= (void *) &int64_val;

  bind_array[7].buffer_type= MYSQL_TYPE_LONGLONG;
  bind_array[7].buffer= (void *) &uint64_val;
  bind_array[7].is_unsigned= 1;

  stmt= mysql_stmt_init(mysql);
  check_stmt(stmt);

  stmt_text= "INSERT INTO t1 (i8, ui8, i16, ui16, i32, ui32, i64, ui64) "
                     "VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
  rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
  check_execute(stmt, rc);

  mysql_stmt_bind_param(stmt, bind_array);

  int8_val= int8_min;
  uint8_val= uint8_min;
  int16_val= int16_min;
  uint16_val= uint16_min;
  int32_val= int32_min;
  uint32_val= uint32_min;
  int64_val= int64_min;
  uint64_val= uint64_min;

  rc= mysql_stmt_execute(stmt);
  check_execute(stmt, rc);

  int8_val= int8_max;
  uint8_val= uint8_max;
  int16_val= int16_max;
  uint16_val= uint16_max;
  int32_val= int32_max;
  uint32_val= uint32_max;
  int64_val= int64_max;
  uint64_val= uint64_max;

  rc= mysql_stmt_execute(stmt);
  check_execute(stmt, rc);

  stmt_text= "SELECT i8, ui8, i16, ui16, i32, ui32, i64, ui64, ui64, "
             "cast(ui64 as signed), ui64, cast(ui64 as signed)"
             "FROM t1 ORDER BY id ASC";

  rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
  check_execute(stmt, rc);

  rc= mysql_stmt_execute(stmt);
  check_execute(stmt, rc);

  bind_array[8].buffer_type= MYSQL_TYPE_DOUBLE;
  bind_array[8].buffer= (void *) &udouble_val;

  bind_array[9].buffer_type= MYSQL_TYPE_DOUBLE;
  bind_array[9].buffer= (void *) &double_val;

  bind_array[10].buffer_type= MYSQL_TYPE_STRING;
  bind_array[10].buffer= (void *) &ulonglong_as_string;
  bind_array[10].buffer_length= sizeof(ulonglong_as_string);

  bind_array[11].buffer_type= MYSQL_TYPE_STRING;
  bind_array[11].buffer= (void *) &longlong_as_string;
  bind_array[11].buffer_length= sizeof(longlong_as_string);

  mysql_stmt_bind_result(stmt, bind_array);

  rc= mysql_stmt_fetch(stmt);
  check_execute(stmt, rc);

  DIE_UNLESS(int8_val == int8_min);
  DIE_UNLESS(uint8_val == uint8_min);
  DIE_UNLESS(int16_val == int16_min);
  DIE_UNLESS(uint16_val == uint16_min);
  DIE_UNLESS(int32_val == int32_min);
  DIE_UNLESS(uint32_val == uint32_min);
  DIE_UNLESS(int64_val == int64_min);
  DIE_UNLESS(uint64_val == uint64_min);
  DIE_UNLESS(double_val == (longlong) uint64_min);
  double_tmp= ulonglong2double(uint64_val);
  DIE_UNLESS(cmp_double(&udouble_val, &double_tmp));
  DIE_UNLESS(!strcmp(longlong_as_string, "0"));
  DIE_UNLESS(!strcmp(ulonglong_as_string, "0"));

  rc= mysql_stmt_fetch(stmt);

  if (!opt_silent)
  {
    printf("Truncation mask: ");
    for (my_bind= bind_array; my_bind < bind_end; my_bind++)
      printf("%d", (int) my_bind->error_value);
    printf("\n");
  }
  DIE_UNLESS(rc == MYSQL_DATA_TRUNCATED || rc == 0);

  DIE_UNLESS(int8_val == int8_max);
  DIE_UNLESS(uint8_val == uint8_max);
  DIE_UNLESS(int16_val == int16_max);
  DIE_UNLESS(uint16_val == uint16_max);
  DIE_UNLESS(int32_val == int32_max);
  DIE_UNLESS(uint32_val == uint32_max);
  DIE_UNLESS(int64_val == int64_max);
  DIE_UNLESS(uint64_val == uint64_max);
  DIE_UNLESS(double_val == (longlong) uint64_val);
  double_tmp= ulonglong2double(uint64_val);
  DIE_UNLESS(cmp_double(&udouble_val, &double_tmp));
  DIE_UNLESS(!strcmp(longlong_as_string, "-1"));
  DIE_UNLESS(!strcmp(ulonglong_as_string, "18446744073709551615"));

  rc= mysql_stmt_fetch(stmt);
  DIE_UNLESS(rc == MYSQL_NO_DATA);

  mysql_stmt_close(stmt);

  stmt_text= "DROP TABLE t1";
  mysql_real_query(mysql, stmt_text, strlen(stmt_text));
}


static void test_bug27876()
{
  int rc;
  MYSQL_RES *result;

  uchar utf8_func[] =
  {
    0xd1, 0x84, 0xd1, 0x83, 0xd0, 0xbd, 0xd0, 0xba,
    0xd1, 0x86, 0xd0, 0xb8, 0xd0, 0xb9, 0xd0, 0xba,
    0xd0, 0xb0,
    0x00
  };

  uchar utf8_param[] =
  {
    0xd0, 0xbf, 0xd0, 0xb0, 0xd1, 0x80, 0xd0, 0xb0,
    0xd0, 0xbc, 0xd0, 0xb5, 0xd1, 0x82, 0xd1, 0x8a,
    0xd1, 0x80, 0x5f, 0xd0, 0xb2, 0xd0, 0xb5, 0xd1,
    0x80, 0xd1, 0x81, 0xd0, 0xb8, 0xd1, 0x8f,
    0x00
  };

  char query[500];

  DBUG_ENTER("test_bug27876");
  myheader("test_bug27876");

  rc= mysql_query(mysql, "set names utf8");
  myquery(rc);

  rc= mysql_query(mysql, "select version()");
  myquery(rc);
  result= mysql_store_result(mysql);
  mytest(result);
  mysql_free_result(result);

  sprintf(query, "DROP FUNCTION IF EXISTS %s", (char*) utf8_func);
  rc= mysql_query(mysql, query);
  myquery(rc);

  sprintf(query,
          "CREATE FUNCTION %s( %s VARCHAR(25))"
          " RETURNS VARCHAR(25) DETERMINISTIC RETURN %s",
          (char*) utf8_func, (char*) utf8_param, (char*) utf8_param);
  rc= mysql_query(mysql, query);
  myquery(rc);
  sprintf(query, "SELECT %s(VERSION())", (char*) utf8_func);
  rc= mysql_query(mysql, query);
  myquery(rc);
  result= mysql_store_result(mysql);
  mytest(result);
  mysql_free_result(result);

  sprintf(query, "DROP FUNCTION %s", (char*) utf8_func);
  rc= mysql_query(mysql, query);
  myquery(rc);

  rc= mysql_query(mysql, "set names default");
  myquery(rc);

  DBUG_VOID_RETURN;
}