sort_by_offset (GCabFile *a, GCabFile *b)
{
    g_return_val_if_fail (a != NULL, 0);
    g_return_val_if_fail (b != NULL, 0);

    return (gint64) gcab_file_get_uoffset (a) - (gint64) gcab_file_get_uoffset (b);
}


xcf_save_invoker (GimpProcedure         *procedure,
                  Gimp                  *gimp,
                  GimpContext           *context,
                  GimpProgress          *progress,
                  const GimpValueArray  *args,
                  GError               **error)
{
  GimpValueArray *return_vals;
  GimpImage      *image;
  const gchar    *uri;
  GFile          *file;
  GOutputStream  *output;
  gboolean        success  = FALSE;
  GError         *my_error = NULL;

  gimp_set_busy (gimp);

  image = gimp_value_get_image (gimp_value_array_index (args, 1), gimp);
  uri   = g_value_get_string (gimp_value_array_index (args, 3));
  file  = g_file_new_for_uri (uri);

  output = G_OUTPUT_STREAM (g_file_replace (file,
                                            NULL, FALSE, G_FILE_CREATE_NONE,
                                            NULL, &my_error));

  if (output)
    {
      success = xcf_save_stream (gimp, image, output, file, progress, error);

      g_object_unref (output);
    }
  else
    {
      g_propagate_prefixed_error (error, my_error,
                                  _("Error creating '%s': "),
                                  gimp_file_get_utf8_name (file));
    }

  g_object_unref (file);

  return_vals = gimp_procedure_get_return_values (procedure, success,
                                                  error ? *error : NULL);

  gimp_unset_busy (gimp);

  return return_vals;
}


save_image (const gchar  *filename,
            gint32        image_ID,
            gint32        drawable_ID,
            GError      **error)
{
  g_message ("Exporting not implemented yet");

  return FALSE;
}


load_image (const gchar  *filename,
            GError      **error)
{
  FILE *f;
  struct stat st;
  char buf[32];
  PSPimage ia;
  guint32 block_init_len, block_total_len;
  long block_start;
  PSPBlockID id = -1;
  gint block_number;

  gint32 image_ID = -1;

  if (g_stat (filename, &st) == -1)
    return -1;

  f = g_fopen (filename, "rb");
  if (f == NULL)
    {
      g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
                   _("Could not open '%s' for reading: %s"),
                   gimp_filename_to_utf8 (filename), g_strerror (errno));
      return -1;
    }

  /* Read the PSP File Header and determine file version */
  if (fread (buf, 32, 1, f) < 1
      || fread (&psp_ver_major, 2, 1, f) < 1
      || fread (&psp_ver_minor, 2, 1, f) < 1)
    {
      g_message ("Error reading file header");
      goto error;
    }

  if (memcmp (buf, "Paint Shop Pro Image File\n\032\0\0\0\0\0", 32) != 0)
    {
      g_message ("Incorrect file signature");
      goto error;
    }

  psp_ver_major = GUINT16_FROM_LE (psp_ver_major);
  psp_ver_minor = GUINT16_FROM_LE (psp_ver_minor);

  /* I only have the documentation for file format version 3.0,
   * but PSP 6 writes version 4.0. Let's hope it's backwards compatible.
   * Earlier versions probably don't have all the fields I expect
   * so don't accept those.
   */
  if (psp_ver_major < 3)
    {
      g_message ("Unsupported PSP file format version "
                 "%d.%d, only knows 3.0 (and later?)",
                 psp_ver_major, psp_ver_minor);
      goto error;
    }
  else if ((psp_ver_major == 3)
        || (psp_ver_major == 4)
        || (psp_ver_major == 5)
        || (psp_ver_major == 6))
    ; /* OK */
  else
    {
      g_message ("Unsupported PSP file format version %d.%d",
                 psp_ver_major, psp_ver_minor);
      goto error;
    }

  /* Read all the blocks */
  block_number = 0;

  IFDBG(3) g_message ("size = %d", (int)st.st_size);
  while (ftell (f) != st.st_size
         && (id = read_block_header (f, &block_init_len,
                                     &block_total_len)) != -1)
    {
      block_start = ftell (f);

      if (block_start + block_total_len > st.st_size)
        {
          g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                       _("Could not open '%s' for reading: %s"),
                       gimp_filename_to_utf8 (filename),
                       _("invalid block size"));
          goto error;
        }

      if (id == PSP_IMAGE_BLOCK)
        {
          if (block_number != 0)
            {
              g_message ("Duplicate General Image Attributes block");
              goto error;
            }
          if (read_general_image_attribute_block (f, block_init_len,
                                                  block_total_len, &ia) == -1)
            {
              goto error;
            }

          IFDBG(2) g_message ("%d dpi %dx%d %s",
                              (int) ia.resolution,
                              ia.width, ia.height,
                              compression_name (ia.compression));

          image_ID = gimp_image_new (ia.width, ia.height,
                                     ia.grayscale ? GIMP_GRAY : GIMP_RGB);
          if (image_ID == -1)
            {
              goto error;
            }

          gimp_image_set_filename (image_ID, filename);

          gimp_image_set_resolution (image_ID, ia.resolution, ia.resolution);
        }
      else
        {
          if (block_number == 0)
            {
              g_message ("Missing General Image Attributes block");
              goto error;
            }

          switch (id)
            {
            case PSP_CREATOR_BLOCK:
              if (read_creator_block (f, image_ID, block_total_len, &ia) == -1)
                goto error;
              break;

            case PSP_COLOR_BLOCK:
              break;            /* Not yet implemented */

            case PSP_LAYER_START_BLOCK:
              if (read_layer_block (f, image_ID, block_total_len, &ia) == -1)
                goto error;
              break;

            case PSP_SELECTION_BLOCK:
              break;            /* Not yet implemented */

            case PSP_ALPHA_BANK_BLOCK:
              break;            /* Not yet implemented */

            case PSP_THUMBNAIL_BLOCK:
              break;            /* No use for it */

            case PSP_EXTENDED_DATA_BLOCK:
              break;            /* Not yet implemented */

            case PSP_TUBE_BLOCK:
              if (read_tube_block (f, image_ID, block_total_len, &ia) == -1)
                goto error;
              break;

            case PSP_COMPOSITE_IMAGE_BANK_BLOCK:
              break;            /* Not yet implemented */

            case PSP_LAYER_BLOCK:
            case PSP_CHANNEL_BLOCK:
            case PSP_ALPHA_CHANNEL_BLOCK:
            case PSP_ADJUSTMENT_EXTENSION_BLOCK:
            case PSP_VECTOR_EXTENSION_BLOCK:
            case PSP_SHAPE_BLOCK:
            case PSP_PAINTSTYLE_BLOCK:
            case PSP_COMPOSITE_ATTRIBUTES_BLOCK:
            case PSP_JPEG_BLOCK:
              g_message ("Sub-block %s should not occur "
                         "at main level of file",
                         block_name (id));
              break;

            default:
              g_message ("Unrecognized block id %d", id);
              break;
            }
        }

      if (block_start + block_total_len >= st.st_size)
        break;

      if (try_fseek (f, block_start + block_total_len, SEEK_SET) < 0)
        goto error;

      block_number++;
    }

  if (id == -1)
    {
    error:
      fclose (f);
      if (image_ID != -1)
        gimp_image_delete (image_ID);
      return -1;
    }

  fclose (f);

  return image_ID;
}


read_layer_block (FILE     *f,
                  gint      image_ID,
                  guint     total_len,
                  PSPimage *ia)
{
  gint i;
  long block_start, sub_block_start, channel_start;
  gint sub_id;
  guint32 sub_init_len, sub_total_len;
  gchar *name = NULL;
  guint16 namelen;
  guchar type, opacity, blend_mode, visibility, transparency_protected;
  guchar link_group_id, mask_linked, mask_disabled;
  guint32 image_rect[4], saved_image_rect[4], mask_rect[4], saved_mask_rect[4];
  gboolean null_layer = FALSE;
  guint16 bitmap_count, channel_count;
  GimpImageType drawable_type;
  guint32 layer_ID = 0;
  GimpLayerMode layer_mode;
  guint32 channel_init_len, channel_total_len;
  guint32 compressed_len, uncompressed_len;
  guint16 bitmap_type, channel_type;
  gint width, height, bytespp, offset;
  guchar **pixels, *pixel;
  GeglBuffer *buffer;

  block_start = ftell (f);

  while (ftell (f) < block_start + total_len)
    {
      /* Read the layer sub-block header */
      sub_id = read_block_header (f, &sub_init_len, &sub_total_len);
      if (sub_id == -1)
        return -1;

      if (sub_id != PSP_LAYER_BLOCK)
        {
          g_message ("Invalid layer sub-block %s, should be LAYER",
                     block_name (sub_id));
          return -1;
        }

      sub_block_start = ftell (f);

      /* Read layer information chunk */
      if (psp_ver_major >= 4)
        {
          if (fseek (f, 4, SEEK_CUR) < 0
              || fread (&namelen, 2, 1, f) < 1
              || ((namelen = GUINT16_FROM_LE (namelen)) && FALSE)
              || (name = g_malloc (namelen + 1)) == NULL
              || fread (name, namelen, 1, f) < 1
              || fread (&type, 1, 1, f) < 1
              || fread (&image_rect, 16, 1, f) < 1
              || fread (&saved_image_rect, 16, 1, f) < 1
              || fread (&opacity, 1, 1, f) < 1
              || fread (&blend_mode, 1, 1, f) < 1
              || fread (&visibility, 1, 1, f) < 1
              || fread (&transparency_protected, 1, 1, f) < 1
              || fread (&link_group_id, 1, 1, f) < 1
              || fread (&mask_rect, 16, 1, f) < 1
              || fread (&saved_mask_rect, 16, 1, f) < 1
              || fread (&mask_linked, 1, 1, f) < 1
              || fread (&mask_disabled, 1, 1, f) < 1
              || fseek (f, 47, SEEK_CUR) < 0
              || fread (&bitmap_count, 2, 1, f) < 1
              || fread (&channel_count, 2, 1, f) < 1)
            {
              g_message ("Error reading layer information chunk");
              g_free (name);
              return -1;
            }

          name[namelen] = 0;
          type = PSP_LAYER_NORMAL; /* ??? */
        }
      else
        {
          name = g_malloc (257);
          name[256] = 0;

          if (fread (name, 256, 1, f) < 1
              || fread (&type, 1, 1, f) < 1
              || fread (&image_rect, 16, 1, f) < 1
              || fread (&saved_image_rect, 16, 1, f) < 1
              || fread (&opacity, 1, 1, f) < 1
              || fread (&blend_mode, 1, 1, f) < 1
              || fread (&visibility, 1, 1, f) < 1
              || fread (&transparency_protected, 1, 1, f) < 1
              || fread (&link_group_id, 1, 1, f) < 1
              || fread (&mask_rect, 16, 1, f) < 1
              || fread (&saved_mask_rect, 16, 1, f) < 1
              || fread (&mask_linked, 1, 1, f) < 1
              || fread (&mask_disabled, 1, 1, f) < 1
              || fseek (f, 43, SEEK_CUR) < 0
              || fread (&bitmap_count, 2, 1, f) < 1
              || fread (&channel_count, 2, 1, f) < 1)
            {
              g_message ("Error reading layer information chunk");
              g_free (name);
              return -1;
            }
        }

      if (type == PSP_LAYER_FLOATING_SELECTION)
        g_message ("Floating selection restored as normal layer");

      swab_rect (image_rect);
      swab_rect (saved_image_rect);
      swab_rect (mask_rect);
      swab_rect (saved_mask_rect);
      bitmap_count = GUINT16_FROM_LE (bitmap_count);
      channel_count = GUINT16_FROM_LE (channel_count);

      layer_mode = gimp_layer_mode_from_psp_blend_mode (blend_mode);
      if ((int) layer_mode == -1)
        {
          g_message ("Unsupported PSP layer blend mode %s "
                     "for layer %s, setting layer invisible",
                     blend_mode_name (blend_mode), name);
          layer_mode = GIMP_LAYER_MODE_NORMAL_LEGACY;
          visibility = FALSE;
        }

      width = saved_image_rect[2] - saved_image_rect[0];
      height = saved_image_rect[3] - saved_image_rect[1];

      if ((width < 0) || (width > GIMP_MAX_IMAGE_SIZE)       /* w <= 2^18 */
          || (height < 0) || (height > GIMP_MAX_IMAGE_SIZE)  /* h <= 2^18 */
          || ((width / 256) * (height / 256) >= 8192))       /* w * h < 2^29 */
        {
          g_message ("Invalid layer dimensions: %dx%d", width, height);
          return -1;
        }

      IFDBG(2) g_message
        ("layer: %s %dx%d (%dx%d) @%d,%d opacity %d blend_mode %s "
         "%d bitmaps %d channels",
         name,
         image_rect[2] - image_rect[0], image_rect[3] - image_rect[1],
         width, height,
         saved_image_rect[0], saved_image_rect[1],
         opacity, blend_mode_name (blend_mode),
         bitmap_count, channel_count);

      IFDBG(2) g_message
        ("mask %dx%d (%dx%d) @%d,%d",
         mask_rect[2] - mask_rect[0],
         mask_rect[3] - mask_rect[1],
         saved_mask_rect[2] - saved_mask_rect[0],
         saved_mask_rect[3] - saved_mask_rect[1],
         saved_mask_rect[0], saved_mask_rect[1]);

      if (width == 0)
        {
          width++;
          null_layer = TRUE;
        }
      if (height == 0)
        {
          height++;
          null_layer = TRUE;
        }

      if (ia->grayscale)
        if (!null_layer && bitmap_count == 1)
          drawable_type = GIMP_GRAY_IMAGE, bytespp = 1;
        else
          drawable_type = GIMP_GRAYA_IMAGE, bytespp = 1;
      else
        if (!null_layer && bitmap_count == 1)
          drawable_type = GIMP_RGB_IMAGE, bytespp = 3;
        else
          drawable_type = GIMP_RGBA_IMAGE, bytespp = 4;

      layer_ID = gimp_layer_new (image_ID, name,
                                 width, height,
                                 drawable_type,
                                 100.0 * opacity / 255.0,
                                 layer_mode);
      if (layer_ID == -1)
        {
          g_message ("Error creating layer");
          return -1;
        }

      g_free (name);

      gimp_image_insert_layer (image_ID, layer_ID, -1, -1);

      if (saved_image_rect[0] != 0 || saved_image_rect[1] != 0)
        gimp_layer_set_offsets (layer_ID,
                                saved_image_rect[0], saved_image_rect[1]);

      if (!visibility)
        gimp_item_set_visible (layer_ID, FALSE);

      gimp_layer_set_lock_alpha (layer_ID, transparency_protected);

      if (psp_ver_major < 4)
        if (try_fseek (f, sub_block_start + sub_init_len, SEEK_SET) < 0)
          {
            return -1;
          }

      pixel = g_malloc0 (height * width * bytespp);
      if (null_layer)
        {
          pixels = NULL;
        }
      else
        {
          pixels = g_new (guchar *, height);
          for (i = 0; i < height; i++)
            pixels[i] = pixel + width * bytespp * i;
        }

      buffer = gimp_drawable_get_buffer (layer_ID);

      /* Read the layer channel sub-blocks */
      while (ftell (f) < sub_block_start + sub_total_len)
        {
          sub_id = read_block_header (f, &channel_init_len,
                                      &channel_total_len);
          if (sub_id == -1)
            {
              gimp_image_delete (image_ID);
              return -1;
            }

          if (sub_id != PSP_CHANNEL_BLOCK)
            {
              g_message ("Invalid layer sub-block %s, should be CHANNEL",
                         block_name (sub_id));
              return -1;
            }

          channel_start = ftell (f);

          if (psp_ver_major == 4)
            fseek (f, 4, SEEK_CUR); /* Unknown field */

          if (fread (&compressed_len, 4, 1, f) < 1
              || fread (&uncompressed_len, 4, 1, f) < 1
              || fread (&bitmap_type, 2, 1, f) < 1
              || fread (&channel_type, 2, 1, f) < 1)
            {
              g_message ("Error reading channel information chunk");
              return -1;
            }

          compressed_len = GUINT32_FROM_LE (compressed_len);
          uncompressed_len = GUINT32_FROM_LE (uncompressed_len);
          bitmap_type = GUINT16_FROM_LE (bitmap_type);
          channel_type = GUINT16_FROM_LE (channel_type);

          if (bitmap_type > PSP_DIB_USER_MASK)
            {
              g_message ("Invalid bitmap type %d in channel information chunk",
                         bitmap_type);
              return -1;
            }

          if (channel_type > PSP_CHANNEL_BLUE)
            {
              g_message ("Invalid channel type %d in channel information chunk",
                         channel_type);
              return -1;
            }

          IFDBG(2) g_message ("channel: %s %s %d (%d) bytes %d bytespp",
                              bitmap_type_name (bitmap_type),
                              channel_type_name (channel_type),
                              uncompressed_len, compressed_len,
                              bytespp);

          if (bitmap_type == PSP_DIB_TRANS_MASK)
            offset = 3;
          else
            offset = channel_type - PSP_CHANNEL_RED;

          if (psp_ver_major < 4)
            if (try_fseek (f, channel_start + channel_init_len, SEEK_SET) < 0)
              {
                return -1;
              }

          if (!null_layer)
            if (read_channel_data (f, ia, pixels, bytespp,
                                   offset, buffer, compressed_len) == -1)
              {
                return -1;
              }

          if (try_fseek (f, channel_start + channel_total_len, SEEK_SET) < 0)
            {
              return -1;
            }
        }

      gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, width, height), 0,
                       NULL, pixel, GEGL_AUTO_ROWSTRIDE);

      g_object_unref (buffer);

      g_free (pixels);
      g_free (pixel);
    }

  if (try_fseek (f, block_start + total_len, SEEK_SET) < 0)
    {
      return -1;
    }

  return layer_ID;
}


read_creator_block (FILE     *f,
                    gint      image_ID,
                    guint     total_len,
                    PSPimage *ia)
{
  long          data_start;
  guchar        buf[4];
  guint16       keyword;
  guint32       length;
  gchar        *string;
  gchar        *title = NULL, *artist = NULL, *copyright = NULL, *description = NULL;
  guint32       dword;
  guint32       __attribute__((unused))cdate = 0;
  guint32       __attribute__((unused))mdate = 0;
  guint32       __attribute__((unused))appid;
  guint32       __attribute__((unused))appver;
  GString      *comment;
  GimpParasite *comment_parasite;

  data_start = ftell (f);
  comment = g_string_new (NULL);

  while (ftell (f) < data_start + total_len)
    {
      if (fread (buf, 4, 1, f) < 1
          || fread (&keyword, 2, 1, f) < 1
          || fread (&length, 4, 1, f) < 1)
        {
          g_message ("Error reading creator keyword chunk");
          return -1;
        }
      if (memcmp (buf, "~FL\0", 4) != 0)
        {
          g_message ("Invalid keyword chunk header");
          return -1;
        }
      keyword = GUINT16_FROM_LE (keyword);
      length = GUINT32_FROM_LE (length);
      switch (keyword)
        {
        case PSP_CRTR_FLD_TITLE:
        case PSP_CRTR_FLD_ARTIST:
        case PSP_CRTR_FLD_CPYRGHT:
        case PSP_CRTR_FLD_DESC:
          string = g_malloc (length + 1);
          if (fread (string, length, 1, f) < 1)
            {
              g_message ("Error reading creator keyword data");
              g_free (string);
              return -1;
            }
          if (string[length - 1] != '\0')
            {
              g_message ("Creator keyword data not nul-terminated");
              g_free (string);
              return -1;
            }
          switch (keyword)
            {
            case PSP_CRTR_FLD_TITLE:
              g_free (title); title = string; break;
            case PSP_CRTR_FLD_ARTIST:
              g_free (artist); artist = string; break;
            case PSP_CRTR_FLD_CPYRGHT:
              g_free (copyright); copyright = string; break;
            case PSP_CRTR_FLD_DESC:
              g_free (description); description = string; break;
            default:
              g_free (string);
            }
          break;
        case PSP_CRTR_FLD_CRT_DATE:
        case PSP_CRTR_FLD_MOD_DATE:
        case PSP_CRTR_FLD_APP_ID:
        case PSP_CRTR_FLD_APP_VER:
          if (fread (&dword, 4, 1, f) < 1)
            {
              g_message ("Error reading creator keyword data");
              return -1;
            }
          switch (keyword)
            {
            case PSP_CRTR_FLD_CRT_DATE:
              cdate = dword; break;
            case PSP_CRTR_FLD_MOD_DATE:
              mdate = dword; break;
            case PSP_CRTR_FLD_APP_ID:
              appid = dword; break;
            case PSP_CRTR_FLD_APP_VER:
              appver = dword; break;
            }
          break;
        default:
          if (try_fseek (f, length, SEEK_CUR) < 0)
            {
              return -1;
            }
          break;
        }
    }

  if (title)
    {
      g_string_append (comment, title);
      g_free (title);
      g_string_append (comment, "\n");
    }
  if (artist)
    {
      g_string_append (comment, artist);
      g_free (artist);
      g_string_append (comment, "\n");
    }
  if (copyright)
    {
      g_string_append (comment, "Copyright ");
      g_string_append (comment, copyright);
      g_free (copyright);
      g_string_append (comment, "\n");
    }
  if (description)
    {
      g_string_append (comment, description);
      g_free (description);
      g_string_append (comment, "\n");
    }
  if (comment->len > 0)
    {
      comment_parasite = gimp_parasite_new ("gimp-comment",
                                            GIMP_PARASITE_PERSISTENT,
                                            strlen (comment->str) + 1,
                                            comment->str);
      gimp_image_attach_parasite (image_ID, comment_parasite);
      gimp_parasite_free (comment_parasite);
    }

  g_string_free (comment, FALSE);

  return 0;
}


run (const gchar      *name,
     gint              nparams,
     const GimpParam  *param,
     gint             *nreturn_vals,
     GimpParam       **return_vals)
{
  static GimpParam   values[2];
  GimpRunMode        run_mode;
  GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
  gint32             image_ID;
  gint32             drawable_ID;
  GimpExportReturn   export = GIMP_EXPORT_CANCEL;
  GError            *error  = NULL;

  INIT_I18N ();

  run_mode = param[0].data.d_int32;

  *nreturn_vals = 1;
  *return_vals  = values;

  values[0].type          = GIMP_PDB_STATUS;
  values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;

  if (strcmp (name, LOAD_PROC) == 0)
    {
      image_ID = load_image (param[1].data.d_string, &error);

      if (image_ID != -1)
        {
          *nreturn_vals = 2;
          values[1].type         = GIMP_PDB_IMAGE;
          values[1].data.d_image = image_ID;
        }
      else
        {
          status = GIMP_PDB_EXECUTION_ERROR;
        }
    }
  else if (strcmp (name, SAVE_PROC) == 0)
    {
      image_ID = param[1].data.d_int32;
      drawable_ID = param[2].data.d_int32;

      /*  eventually export the image */
      switch (run_mode)
        {
        case GIMP_RUN_INTERACTIVE:
        case GIMP_RUN_WITH_LAST_VALS:
          gimp_ui_init (PLUG_IN_BINARY, FALSE);

          export = gimp_export_image (&image_ID, &drawable_ID, "PSP",
                                      GIMP_EXPORT_CAN_HANDLE_RGB     |
                                      GIMP_EXPORT_CAN_HANDLE_GRAY    |
                                      GIMP_EXPORT_CAN_HANDLE_INDEXED |
                                      GIMP_EXPORT_CAN_HANDLE_ALPHA   |
                                      GIMP_EXPORT_CAN_HANDLE_LAYERS);

          if (export == GIMP_EXPORT_CANCEL)
            {
              values[0].data.d_status = GIMP_PDB_CANCEL;
              return;
            }
          break;
        default:
          break;
        }

      switch (run_mode)
        {
        case GIMP_RUN_INTERACTIVE:
          /*  Possibly retrieve data  */
          gimp_get_data (SAVE_PROC, &psvals);

          /*  First acquire information with a dialog  */
          if (! save_dialog ())
            status = GIMP_PDB_CANCEL;
          break;

        case GIMP_RUN_NONINTERACTIVE:
          /*  Make sure all the arguments are there!  */
          if (nparams != 6)
            {
              status = GIMP_PDB_CALLING_ERROR;
            }
          else
            {
              psvals.compression = (param[5].data.d_int32) ? TRUE : FALSE;

              if (param[5].data.d_int32 < 0 ||
                  param[5].data.d_int32 > PSP_COMP_LZ77)
                status = GIMP_PDB_CALLING_ERROR;
            }

        case GIMP_RUN_WITH_LAST_VALS:
          gimp_get_data (SAVE_PROC, &psvals);
          break;

        default:
          break;
        }

      if (status == GIMP_PDB_SUCCESS)
        {
          if (save_image (param[3].data.d_string, image_ID, drawable_ID,
                          &error))
            {
              gimp_set_data (SAVE_PROC, &psvals, sizeof (PSPSaveVals));
            }
          else
            {
              status = GIMP_PDB_EXECUTION_ERROR;
            }
        }

      if (export == GIMP_EXPORT_EXPORT)
        gimp_image_delete (image_ID);
    }
  else
    {
      status = GIMP_PDB_CALLING_ERROR;
    }

  if (status != GIMP_PDB_SUCCESS && error)
    {
      *nreturn_vals = 2;
      values[1].type          = GIMP_PDB_STRING;
      values[1].data.d_string = error->message;
    }

  values[0].data.d_status = status;
}


save_dialog (void)
{
  GtkWidget *dialog;
  GtkWidget *table;
  GtkWidget *entry;
  GtkWidget *spinbutton;
  GtkObject *adj;
  gboolean   run;

  dialog = gimp_export_dialog_new (_("Brush"), PLUG_IN_BINARY, SAVE_PROC);

  /* The main table */
  table = gtk_table_new (2, 2, FALSE);
  gtk_container_set_border_width (GTK_CONTAINER (table), 12);
  gtk_table_set_row_spacings (GTK_TABLE (table), 6);
  gtk_table_set_col_spacings (GTK_TABLE (table), 6);
  gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
                      table, TRUE, TRUE, 0);
  gtk_widget_show (table);

  spinbutton = gimp_spin_button_new (&adj,
                                     info.spacing, 1, 1000, 1, 10, 0, 1, 0);
  gtk_entry_set_activates_default (GTK_ENTRY (spinbutton), TRUE);
  gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
                             _("Spacing:"), 1.0, 0.5,
                             spinbutton, 1, TRUE);

  g_signal_connect (adj, "value-changed",
                    G_CALLBACK (gimp_int_adjustment_update),
                    &info.spacing);

  entry = gtk_entry_new ();
  gtk_widget_set_size_request (entry, 200, -1);
  gtk_entry_set_text (GTK_ENTRY (entry), info.description);
  gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
  gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
                             _("Description:"), 1.0, 0.5,
                             entry, 1, FALSE);

  g_signal_connect (entry, "changed",
                    G_CALLBACK (entry_callback),
                    info.description);

  gtk_widget_show (dialog);

  run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);

  gtk_widget_destroy (dialog);

  return run;
}


run (const gchar      *name,
     gint              nparams,
     const GimpParam  *param,
     gint             *nreturn_vals,
     GimpParam       **return_vals)
{
  static GimpParam   values[2];
  GimpRunMode        run_mode;
  GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
  gint32             image_ID;
  gint32             drawable_ID;
  GimpExportReturn   export = GIMP_EXPORT_CANCEL;
  GError            *error  = NULL;

#ifdef PROFILE
  struct tms tbuf1, tbuf2;
#endif

  INIT_I18N ();
  gegl_init (NULL, NULL);

  run_mode = param[0].data.d_int32;

  *nreturn_vals = 1;
  *return_vals  = values;

  values[0].type          = GIMP_PDB_STATUS;
  values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;

  if (strcmp (name, LOAD_PROC) == 0)
    {
#ifdef PROFILE
      times (&tbuf1);
#endif

      image_ID = load_image (param[1].data.d_string, &error);

      if (image_ID != -1)
        {
          *nreturn_vals = 2;
          values[1].type         = GIMP_PDB_IMAGE;
          values[1].data.d_image = image_ID;
        }
      else
        {
          status = GIMP_PDB_EXECUTION_ERROR;
        }
    }
  else if (strcmp (name, SAVE_PROC) == 0)
    {
      gimp_ui_init (PLUG_IN_BINARY, FALSE);

      image_ID     = param[1].data.d_int32;
      drawable_ID  = param[2].data.d_int32;

      /*  eventually export the image */
      switch (run_mode)
        {
        case GIMP_RUN_INTERACTIVE:
        case GIMP_RUN_WITH_LAST_VALS:
          export = gimp_export_image (&image_ID, &drawable_ID, "TGA",
                                      GIMP_EXPORT_CAN_HANDLE_RGB     |
                                      GIMP_EXPORT_CAN_HANDLE_GRAY    |
                                      GIMP_EXPORT_CAN_HANDLE_INDEXED |
                                      GIMP_EXPORT_CAN_HANDLE_ALPHA);

          if (export == GIMP_EXPORT_CANCEL)
            {
              values[0].data.d_status = GIMP_PDB_CANCEL;
              return;
            }
          break;
        default:
          break;
        }

      switch (run_mode)
        {
        case GIMP_RUN_INTERACTIVE:
          /*  Possibly retrieve data  */
          gimp_get_data (SAVE_PROC, &tsvals);

          /*  First acquire information with a dialog  */
          if (! save_dialog ())
            status = GIMP_PDB_CANCEL;
          break;

        case GIMP_RUN_NONINTERACTIVE:
          /*  Make sure all the arguments are there!  */
          if (nparams != 7)
            {
              status = GIMP_PDB_CALLING_ERROR;
            }
          else
            {
              tsvals.rle = (param[5].data.d_int32) ? TRUE : FALSE;
            }
          break;

        case GIMP_RUN_WITH_LAST_VALS:
          /*  Possibly retrieve data  */
          gimp_get_data (SAVE_PROC, &tsvals);
          break;

        default:
          break;
        }

#ifdef PROFILE
      times (&tbuf1);
#endif

      if (status == GIMP_PDB_SUCCESS)
        {
          if (save_image (param[3].data.d_string, image_ID, drawable_ID,
                          &error))
            {
              /*  Store psvals data  */
              gimp_set_data (SAVE_PROC, &tsvals, sizeof (tsvals));
            }
          else
            {
              status = GIMP_PDB_EXECUTION_ERROR;
            }
        }

      if (export == GIMP_EXPORT_EXPORT)
        gimp_image_delete (image_ID);
    }
  else
    {
      status = GIMP_PDB_CALLING_ERROR;
    }

  if (status != GIMP_PDB_SUCCESS && error)
    {
      *nreturn_vals = 2;
      values[1].type          = GIMP_PDB_STRING;
      values[1].data.d_string = error->message;
    }

  values[0].data.d_status = status;

#ifdef PROFILE
  times (&tbuf2);
  printf ("TGA: %s profile: %ld user %ld system\n", name,
          (long) tbuf2.tms_utime - tbuf1.tms_utime,
          (long) tbuf2.tms_stime - tbuf2.tms_stime);
#endif
}


ReadImage (FILE        *fp,
           tga_info    *info,
           const gchar *filename)
{
  static gint32      image_ID;
  gint32             layer_ID;
  GeglBuffer        *buffer;
  guchar            *data, *buf, *row;
  GimpImageType      dtype = 0;
  GimpImageBaseType  itype = 0;
  gint               bpp;
  gint               i, y;
  gint               max_tileheight, tileheight;
  guint              cmap_bytes   = 0;
  guchar            *tga_cmap     = NULL;
  guchar            *gimp_cmap    = NULL;
  guchar            *convert_cmap = NULL;

  switch (info->imageType)
    {
    case TGA_TYPE_MAPPED:
      cmap_bytes = (info->colorMapSize + 7 ) / 8;
      tga_cmap = g_new (guchar, info->colorMapLength * cmap_bytes);

      if (info->colorMapSize > 24)
        {
          /* indexed + full alpha => promoted to RGBA */
          itype = GIMP_RGB;
          dtype = GIMP_RGBA_IMAGE;
          convert_cmap = g_new (guchar, info->colorMapLength * 4);
        }
      else if (info->colorMapIndex + info->colorMapLength > 256)
        {
          /* more than 256 colormap entries => promoted to RGB */
          itype = GIMP_RGB;
          dtype = GIMP_RGB_IMAGE;
          convert_cmap = g_new (guchar, info->colorMapLength * 3);
        }
      else if (info->alphaBits > 0)
        {
          /* if alpha exists here, promote to RGB */
          itype = GIMP_RGB;
          dtype = GIMP_RGBA_IMAGE;
          convert_cmap = g_new (guchar, info->colorMapLength * 4);
        }
      else
        {
          itype = GIMP_INDEXED;
          dtype = GIMP_INDEXED_IMAGE;
          gimp_cmap = g_new (guchar, info->colorMapLength * 3);
        }
      break;

    case TGA_TYPE_GRAY:
      itype = GIMP_GRAY;

      if (info->alphaBits)
        dtype = GIMP_GRAYA_IMAGE;
      else
        dtype = GIMP_GRAY_IMAGE;
      break;

    case TGA_TYPE_COLOR:
      itype = GIMP_RGB;

      if (info->alphaBits)
        dtype = GIMP_RGBA_IMAGE;
      else
        dtype = GIMP_RGB_IMAGE;
      break;
    }

  /* Handle colormap */

  if (info->imageType == TGA_TYPE_MAPPED)
    {
      if (cmap_bytes <= 4 &&
          fread (tga_cmap, info->colorMapLength * cmap_bytes, 1, fp) == 1)
        {
          if (convert_cmap)
            {
              if (info->colorMapSize == 32)
                bgr2rgb (convert_cmap, tga_cmap,
                         info->colorMapLength, cmap_bytes, 1);
              else if (info->colorMapSize == 24)
                bgr2rgb (convert_cmap, tga_cmap,
                         info->colorMapLength, cmap_bytes, 0);
              else if (info->colorMapSize == 16 || info->colorMapSize == 15)
                upsample (convert_cmap, tga_cmap,
                          info->colorMapLength, cmap_bytes, info->alphaBits);
              else
                {
                  g_message ("Unsupported colormap depth: %u",
                             info->colorMapSize);
                  return -1;
                }
            }
          else
            {
              if (info->colorMapSize == 24)
                bgr2rgb (gimp_cmap, tga_cmap,
                         info->colorMapLength, cmap_bytes, 0);
              else if (info->colorMapSize == 16 || info->colorMapSize == 15)
                upsample (gimp_cmap, tga_cmap,
                          info->colorMapLength, cmap_bytes, info->alphaBits);
              else
                {
                  g_message ("Unsupported colormap depth: %u",
                             info->colorMapSize);
                  return -1;
                }
            }
        }
      else
        {
          g_message ("File '%s' is truncated or corrupted",
                     gimp_filename_to_utf8 (filename));
          return -1;
        }
    }

  image_ID = gimp_image_new (info->width, info->height, itype);
  gimp_image_set_filename (image_ID, filename);

  if (gimp_cmap)
    gimp_image_set_colormap (image_ID, gimp_cmap, info->colorMapLength);

  layer_ID = gimp_layer_new (image_ID,
                             _("Background"),
                             info->width, info->height,
                             dtype,
                             100,
                             gimp_image_get_default_new_layer_mode (image_ID));

  gimp_image_insert_layer (image_ID, layer_ID, -1, 0);

  buffer = gimp_drawable_get_buffer (layer_ID);

  bpp = gimp_drawable_bpp (layer_ID);

  /* Allocate the data. */
  max_tileheight = gimp_tile_height ();
  data = g_new (guchar, info->width * max_tileheight * bpp);
  buf  = g_new (guchar, info->width * info->bytes);

  if (info->flipVert)
    {
      for (i = 0; i < info->height; i += tileheight)
        {
          tileheight = i ? max_tileheight : (info->height % max_tileheight);
          if (tileheight == 0)
            tileheight = max_tileheight;

          for (y = 1; y <= tileheight; ++y)
            {
              row = data + (info->width * bpp * (tileheight - y));
              read_line (fp, row, buf, info, bpp, convert_cmap);
            }

          gegl_buffer_set (buffer,
                           GEGL_RECTANGLE (0, info->height - i - tileheight,
                                           info->width, tileheight), 0,
                           NULL, data, GEGL_AUTO_ROWSTRIDE);

          gimp_progress_update ((gdouble) (i + tileheight) /
                                (gdouble) info->height);
        }
    }
  else
    {
      for (i = 0; i < info->height; i += max_tileheight)
        {
          tileheight = MIN (max_tileheight, info->height - i);

          for (y = 0; y < tileheight; ++y)
            {
              row= data + (info->width * bpp * y);
              read_line (fp, row, buf, info, bpp, convert_cmap);
            }

          gegl_buffer_set (buffer,
                           GEGL_RECTANGLE (0, i, info->width, tileheight), 0,
                           NULL, data, GEGL_AUTO_ROWSTRIDE);

          gimp_progress_update ((gdouble) (i + tileheight) /
                                (gdouble) info->height);
        }
    }

  g_free (data);
  g_free (buf);

  g_free (convert_cmap);
  g_free (gimp_cmap);
  g_free (tga_cmap);

  g_object_unref (buffer);

  gimp_progress_update (1.0);

  return image_ID;
}


run (const gchar      *name,
     gint              nparams,
     const GimpParam  *param,
     gint             *nreturn_vals,
     GimpParam       **return_vals)
{
  static GimpParam   values[2];
  GimpRunMode        run_mode;
  GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
  gint32             image_ID;
  gint32             drawable_ID;
  GimpExportReturn   export = GIMP_EXPORT_CANCEL;
  GError            *error  = NULL;

#ifdef PROFILE
  struct tms tbuf1, tbuf2;
#endif

  run_mode = param[0].data.d_int32;

  INIT_I18N ();

  *nreturn_vals = 1;
  *return_vals  = values;

  values[0].type          = GIMP_PDB_STATUS;
  values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;

  if (strcmp (name, LOAD_PROC) == 0)
    {
#ifdef PROFILE
      times (&tbuf1);
#endif

      image_ID = load_image (param[1].data.d_string, &error);

      if (image_ID != -1)
        {
          *nreturn_vals = 2;
          values[1].type         = GIMP_PDB_IMAGE;
          values[1].data.d_image = image_ID;
        }
      else
        {
          status = GIMP_PDB_EXECUTION_ERROR;
        }
    }
  else if (strcmp (name, SAVE_PROC) == 0)
    {
      gimp_ui_init (PLUG_IN_BINARY, FALSE);

      image_ID     = param[1].data.d_int32;
      drawable_ID  = param[2].data.d_int32;

      /*  eventually export the image */
      switch (run_mode)
        {
        case GIMP_RUN_INTERACTIVE:
        case GIMP_RUN_WITH_LAST_VALS:
          export = gimp_export_image (&image_ID, &drawable_ID, "TGA",
                                      GIMP_EXPORT_CAN_HANDLE_RGB     |
                                      GIMP_EXPORT_CAN_HANDLE_GRAY    |
                                      GIMP_EXPORT_CAN_HANDLE_INDEXED |
                                      GIMP_EXPORT_CAN_HANDLE_ALPHA);

          if (export == GIMP_EXPORT_CANCEL)
            {
              values[0].data.d_status = GIMP_PDB_CANCEL;
              return;
            }
          break;
        default:
          break;
        }

      switch (run_mode)
        {
        case GIMP_RUN_INTERACTIVE:
          /*  Possibly retrieve data  */
          gimp_get_data (SAVE_PROC, &tsvals);

          /*  First acquire information with a dialog  */
          if (! save_dialog ())
            status = GIMP_PDB_CANCEL;
          break;

        case GIMP_RUN_NONINTERACTIVE:
          /*  Make sure all the arguments are there!  */
          if (nparams != 7)
            {
              status = GIMP_PDB_CALLING_ERROR;
            }
          else
            {
              tsvals.rle = (param[5].data.d_int32) ? TRUE : FALSE;
            }
          break;

        case GIMP_RUN_WITH_LAST_VALS:
          /*  Possibly retrieve data  */
          gimp_get_data (SAVE_PROC, &tsvals);
          break;

        default:
          break;
        }

#ifdef PROFILE
      times (&tbuf1);
#endif

      if (status == GIMP_PDB_SUCCESS)
        {
          if (save_image (param[3].data.d_string, image_ID, drawable_ID,
                          &error))
            {
              /*  Store psvals data  */
              gimp_set_data (SAVE_PROC, &tsvals, sizeof (tsvals));
            }
          else
            {
              status = GIMP_PDB_EXECUTION_ERROR;
            }
        }

      if (export == GIMP_EXPORT_EXPORT)
        gimp_image_delete (image_ID);
    }
  else
    {
      status = GIMP_PDB_CALLING_ERROR;
    }

  if (status != GIMP_PDB_SUCCESS && error)
    {
      *nreturn_vals = 2;
      values[1].type          = GIMP_PDB_STRING;
      values[1].data.d_string = error->message;
    }

  values[0].data.d_status = status;

#ifdef PROFILE
  times (&tbuf2);
  printf ("TGA: %s profile: %ld user %ld system\n", name,
          (long) tbuf2.tms_utime - tbuf1.tms_utime,
          (long) tbuf2.tms_stime - tbuf2.tms_stime);
#endif
}


void fli_write_black(FILE *f, s_fli_header *fli_header, unsigned char *framebuf)
{
	s_fli_chunk chunk;

	chunk.size=6;
	chunk.magic=FLI_BLACK;

	fli_write_long(f, chunk.size);
	fli_write_short(f, chunk.magic);
}


void fli_write_lc(FILE *f, s_fli_header *fli_header, unsigned char *old_framebuf, unsigned char *framebuf)
{
	unsigned long chunkpos;
	s_fli_chunk chunk;
	unsigned short yc, firstline, numline, lastline;
	unsigned char *linebuf, *old_linebuf;

	chunkpos=ftell(f);
	fseek(f, chunkpos+6, SEEK_SET);

	/* first check, how many lines are unchanged at the beginning */
	firstline=0;
	while ((memcmp(old_framebuf+(firstline*fli_header->width), framebuf+(firstline*fli_header->width), fli_header->width)==0) && (firstline<fli_header->height)) firstline++;

	/* then check from the end, how many lines are unchanged */
	if (firstline<fli_header->height) {
		lastline=fli_header->height-1;
		while ((memcmp(old_framebuf+(lastline*fli_header->width), framebuf+(lastline*fli_header->width), fli_header->width)==0) && (lastline>firstline)) lastline--;
		numline=(lastline-firstline)+1;
	} else {
		numline=0;
	}
	if (numline==0) firstline=0;

	fli_write_short(f, firstline);
	fli_write_short(f, numline);

	for (yc=0; yc < numline; yc++) {
		unsigned short xc, sc, cc, tc;
		unsigned long linepos, lineend, bc;
		linepos=ftell(f); bc=0;
		fseek(f, 1, SEEK_CUR);

		linebuf=framebuf + ((firstline+yc)*fli_header->width);
		old_linebuf=old_framebuf + ((firstline+yc)*fli_header->width);
		xc=0;
		while (xc < fli_header->width) {
			sc=0;
			while ((linebuf[xc]==old_linebuf[xc]) && (xc<fli_header->width) && (sc<255)) {
				xc++; sc++;
			}
			fli_write_char(f, sc);
			cc=1;
			while ((linebuf[xc]==linebuf[xc+cc]) && ((xc+cc)<fli_header->width) && (cc<120)) {
				cc++;
			}
			if (cc>2) {
				bc++;
				fli_write_char(f, (cc-1)^0xFF);
				fli_write_char(f, linebuf[xc]);
				xc+=cc;
			} else {
				tc=0;
				do {
					sc=0;
					while ((linebuf[tc+xc+sc]==old_linebuf[tc+xc+sc]) && ((tc+xc+sc)<fli_header->width) && (sc<5)) {
						sc++;
					}
					cc=1;
					while ((linebuf[tc+xc]==linebuf[tc+xc+cc]) && ((tc+xc+cc)<fli_header->width) && (cc<10)) {
						cc++;
					}
					tc++;
				} while ((tc<120) && (cc<9) && (sc<4) && ((xc+tc)<fli_header->width));
				bc++;
				fli_write_char(f, tc);
				fwrite(linebuf+xc, tc, 1, f);
				xc+=tc;
			}
		}
		lineend=ftell(f);
		fseek(f, linepos, SEEK_SET);
		fli_write_char(f, bc);
		fseek(f, lineend, SEEK_SET);
	}

	chunk.size=ftell(f)-chunkpos;
	chunk.magic=FLI_LC;

	fseek(f, chunkpos, SEEK_SET);
	fli_write_long(f, chunk.size);
	fli_write_short(f, chunk.magic);

	if (chunk.size & 1) chunk.size++;
	fseek(f,chunkpos+chunk.size,SEEK_SET);
}


void fli_read_black(FILE *f, s_fli_header *fli_header, unsigned char *framebuf)
{
	memset(framebuf, 0, fli_header->width * fli_header->height);
}


static void fli_write_char(FILE *f, unsigned char b)
{
	fwrite(&b,1,1,f);
}


static void compactxref(fz_context *ctx, pdf_document *doc, pdf_write_state *opts)
{
	int num, newnum;
	int xref_len = pdf_xref_len(ctx, doc);

	/*
	 * Update renumber_map in-place, clustering all used
	 * objects together at low object ids. Objects that
	 * already should be renumbered will have their new
	 * object ids be updated to reflect the compaction.
	 */

	if (xref_len > opts->list_len)
		expand_lists(ctx, opts, xref_len-1);

	newnum = 1;
	for (num = 1; num < xref_len; num++)
	{
		/* If it's not used, map it to zero */
		if (!opts->use_list[opts->renumber_map[num]])
		{
			opts->renumber_map[num] = 0;
		}
		/* If it's not moved, compact it. */
		else if (opts->renumber_map[num] == num)
		{
			opts->rev_renumber_map[newnum] = opts->rev_renumber_map[num];
			opts->renumber_map[num] = newnum++;
		}
		/* Otherwise it's used, and moved. We know that it must have
		 * moved down, so the place it's moved to will be in the right
		 * place already. */
		else
		{
			opts->renumber_map[num] = opts->renumber_map[opts->renumber_map[num]];
		}
	}
}


page_objects_list_sort_and_dedupe(fz_context *ctx, page_objects_list *pol)
{
	int i;
	int n = pol->len;

	for (i = 0; i < n; i++)
	{
		page_objects_sort(ctx, pol->page[i]);
		page_objects_dedupe(ctx, pol->page[i]);
	}
}


static int markobj(fz_context *ctx, pdf_document *doc, pdf_write_state *opts, pdf_obj *obj)
{
	int i;

	DEBUGGING_MARKING(depth++);

	while (pdf_is_indirect(ctx, obj))
	{
		int duff;
		DEBUGGING_MARKING(indent(); printf("Marking object %d\n", pdf_to_num(ctx, obj)));
		obj = markref(ctx, doc, opts, obj, &duff);
		if (duff)
		{
			DEBUGGING_MARKING(depth--);
			return 1;
		}
	}

	if (pdf_is_dict(ctx, obj))
	{
		int n = pdf_dict_len(ctx, obj);
		for (i = 0; i < n; i++)
		{
			DEBUGGING_MARKING(indent(); printf("DICT[%d/%d] = %s\n", i, n, pdf_to_name(ctx, pdf_dict_get_key(ctx, obj, i))));
			if (markobj(ctx, doc, opts, pdf_dict_get_val(ctx, obj, i)))
				pdf_dict_put_val_null(ctx, obj, i);
		}
	}

	else if (pdf_is_array(ctx, obj))
	{
		int n = pdf_array_len(ctx, obj);
		for (i = 0; i < n; i++)
		{
			DEBUGGING_MARKING(indent(); printf("ARRAY[%d/%d]\n", i, n));
			if (markobj(ctx, doc, opts, pdf_array_get(ctx, obj, i)))
				pdf_array_put_drop(ctx, obj, i, pdf_new_null(ctx, doc));
		}
	}

	DEBUGGING_MARKING(depth--);

	return 0;
}


page_objects_create(fz_context *ctx)
{
	int initial_cap = 8;
	page_objects *po = fz_calloc(ctx, 1, sizeof(*po) + (initial_cap-1) * sizeof(int));

	po->cap = initial_cap;
	po->len = 0;
	return po;
}


static void renumberobj(fz_context *ctx, pdf_document *doc, pdf_write_state *opts, pdf_obj *obj)
{
	int i;
	int xref_len = pdf_xref_len(ctx, doc);

	if (pdf_is_dict(ctx, obj))
	{
		int n = pdf_dict_len(ctx, obj);
		for (i = 0; i < n; i++)
		{
			pdf_obj *key = pdf_dict_get_key(ctx, obj, i);
			pdf_obj *val = pdf_dict_get_val(ctx, obj, i);
			if (pdf_is_indirect(ctx, val))
			{
				int o = pdf_to_num(ctx, val);
				if (o >= xref_len || o <= 0 || opts->renumber_map[o] == 0)
					val = pdf_new_null(ctx, doc);
				else
					val = pdf_new_indirect(ctx, doc, opts->renumber_map[o], 0);
				pdf_dict_put_drop(ctx, obj, key, val);
			}
			else
			{
				renumberobj(ctx, doc, opts, val);
			}
		}
	}

	else if (pdf_is_array(ctx, obj))
	{
		int n = pdf_array_len(ctx, obj);
		for (i = 0; i < n; i++)
		{
			pdf_obj *val = pdf_array_get(ctx, obj, i);
			if (pdf_is_indirect(ctx, val))
			{
				int o = pdf_to_num(ctx, val);
				if (o >= xref_len || o <= 0 || opts->renumber_map[o] == 0)
					val = pdf_new_null(ctx, doc);
				else
					val = pdf_new_indirect(ctx, doc, opts->renumber_map[o], 0);
				pdf_array_put_drop(ctx, obj, i, val);
			}
			else
			{
				renumberobj(ctx, doc, opts, val);
			}
		}
	}
}


static MagickBooleanType SetCacheAlphaChannel(Image *image,const Quantum alpha,
  ExceptionInfo *exception)
{
  CacheInfo
    *magick_restrict cache_info;

  CacheView
    *magick_restrict image_view;

  MagickBooleanType
    status;

  ssize_t
    y;

  assert(image != (Image *) NULL);
  assert(image->signature == MagickCoreSignature);
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  assert(image->cache != (Cache) NULL);
  cache_info=(CacheInfo *) image->cache;
  assert(cache_info->signature == MagickCoreSignature);
  image->alpha_trait=BlendPixelTrait;
  status=MagickTrue;
  image_view=AcquireVirtualCacheView(image,exception);  /* must be virtual */
#if defined(MAGICKCORE_OPENMP_SUPPORT)
  #pragma omp parallel for schedule(static,4) shared(status) \
    magick_number_threads(image,image,image->rows,1)
#endif
  for (y=0; y < (ssize_t) image->rows; y++)
  {
    register Quantum
      *magick_restrict q;

    register ssize_t
      x;

    if (status == MagickFalse)
      continue;
    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
    if (q == (Quantum *) NULL)
      {
        status=MagickFalse;
        continue;
      }
    for (x=0; x < (ssize_t) image->columns; x++)
    {
      SetPixelAlpha(image,alpha,q);
      q+=GetPixelChannels(image);
    }
    status=SyncCacheViewAuthenticPixels(image_view,exception);
  }
  image_view=DestroyCacheView(image_view);
  return(status);
}


new_context (void)
{
	GifContext *context;

	context = g_try_malloc (sizeof (GifContext));
        if (context == NULL)
                return NULL;

        memset (context, 0, sizeof (GifContext));

        context->animation = g_object_new (GDK_TYPE_PIXBUF_GIF_ANIM, NULL);
	context->frame = NULL;
	context->file = NULL;
	context->state = GIF_START;
	context->size_func = NULL;
	context->prepare_func = NULL;
	context->update_func = NULL;
	context->user_data = NULL;
	context->buf = NULL;
	context->amount_needed = 13;
	context->buf = g_new (guchar, context->amount_needed);
	context->gif89.transparent = -1;
	context->gif89.delay_time = -1;
	context->gif89.input_flag = -1;
	context->gif89.disposal = -1;
        context->animation->loop = 1;
        context->in_loop_extension = FALSE;
        context->stop_after_first_frame = FALSE;

	return context;
}


Decompress::Decompress(std::istream& in, const Poco::Path& outputDir, bool flattenDirs, bool keepIncompleteFiles):
	_in(in),
	_outDir(outputDir),
	_flattenDirs(flattenDirs),
	_keepIncompleteFiles(keepIncompleteFiles),
	_mapping()
{
	_outDir.makeAbsolute();
	_outDir.makeDirectory();
	if (!_in.good()) throw Poco::IOException("Bad input stream");
	Poco::File tmp(_outDir);
	if (!tmp.exists())
	{
		tmp.createDirectories();
	}
	if (!tmp.isDirectory())
		throw Poco::IOException("Failed to create/open directory: " + _outDir.toString());
	EOk += Poco::Delegate<Decompress, std::pair<const ZipLocalFileHeader, const Poco::Path> >(this, &Decompress::onOk);

}


void ZipTest::testDecompressFlatVuln()
{
	std::string testFile = getTestFile("data", "vuln.zip");
	std::ifstream inp(testFile.c_str(), std::ios::binary);
	assert(inp.good());
	Decompress dec(inp, Poco::Path::temp(), true);
	dec.EError += Poco::Delegate<ZipTest, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string> >(this, &ZipTest::onDecompressError);
	dec.decompressAllFiles();
	dec.EError -= Poco::Delegate<ZipTest, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string> >(this, &ZipTest::onDecompressError);
	assert (_errCnt == 0);
	assert (!dec.mapping().empty());
}


void CompressTest::testSetZipComment()
{
	std::string comment("Testing...123...");
	std::ofstream out(Poco::Path::temp() + "comment.zip", std::ios::binary);
	Poco::Path theFile(ZipTest::getTestFile("data", "test.zip"));
	Compress c(out, true);
	c.addFile(theFile, theFile.getFileName());
	c.setZipComment(comment);
	ZipArchive a(c.close());
	assert(a.getZipComment() == comment);
}


void CompressTest::testZip64()
{
	typedef std::map<std::string, Poco::UInt64> FileMap;
	std::cout << std::endl;
	FileMap files;
	files["data1.bin"] = static_cast<Poco::UInt64>(KB)*4096+1;
	files["data2.bin"] = static_cast<Poco::UInt64>(KB)*16;
	files["data3.bin"] = static_cast<Poco::UInt64>(KB)*4096-1;

	for(FileMap::const_iterator it = files.begin(); it != files.end(); it++)
	{
		std::cout << '\t' << "createDataFile(" << it->first << ", " << it->second << ");" << std::endl;
		createDataFile(it->first, it->second);
	}
	std::ofstream out(Poco::Path::temp() + "zip64.zip", std::ios::binary | std::ios::trunc);
	Compress c(out, true, true);
	for(FileMap::const_iterator it = files.begin(); it != files.end(); it++)
	{
		const std::string& path = it->first;
		std::cout << '\t' << "addFile(" << path <<  ");" << std::endl;
		c.addFile(path, path, ZipCommon::CM_STORE);
	}
	ZipArchive a(c.close());
	for(FileMap::const_iterator it = files.begin(); it != files.end(); it++)
	{
		const std::string& path = it->first;
		Poco::UInt64 size = it->second;
		ZipArchive::FileHeaders::const_iterator it2 = a.findHeader(path);
		assert (it2 != a.headerEnd());
		const Poco::Zip::ZipLocalFileHeader& file = it2->second;
		assert(file.getUncompressedSize() == size);
		assert(file.getCompressedSize() == size);
	}
	for (FileMap::const_iterator it = files.begin(); it != files.end(); it++)
	{
		Poco::File(it->first).remove();
	}
}


int ZipStreamBuf::readFromDevice(char* buffer, std::streamsize length)
{
	if (!_ptrBuf) return 0; // directory entry
	_ptrBuf->read(buffer, length);
	int cnt = static_cast<int>(_ptrBuf->gcount());
	if (cnt > 0)
	{
		_crc32.update(buffer, cnt);
	}
	else
	{
		if (_crc32.checksum() != _expectedCrc32)
		{
			if (_checkCRC)
				throw ZipException("CRC failure");
			else
			{
				// the CRC value is written directly after the data block
				// parse it directly from the input stream
				ZipDataInfo nfo(*_pIstr, false);
				// now push back the header to the stream, so that the ZipLocalFileHeader can read it
				Poco::Int32 size = static_cast<Poco::Int32>(nfo.getFullHeaderSize());
				_expectedCrc32 = nfo.getCRC32();
				_pIstr->seekg(-size, std::ios::cur);
				if (!_pIstr->good()) throw Poco::IOException("Failed to seek on input stream");
				if (!crcValid())
					throw ZipException("CRC failure");
			}
		}
	}
	return cnt;
}


static int automount_stop(Unit *u) {
        Automount *a = AUTOMOUNT(u);

        assert(a);
        assert(a->state == AUTOMOUNT_WAITING || a->state == AUTOMOUNT_RUNNING);

        automount_enter_dead(a, AUTOMOUNT_SUCCESS);
        return 1;
}


static int automount_add_mount_links(Automount *a) {
        _cleanup_free_ char *parent = NULL;

        assert(a);

        parent = dirname_malloc(a->where);
        if (!parent)
                return -ENOMEM;

        return unit_require_mounts_for(UNIT(a), parent);
}


int hidp_get_conninfo(struct hidp_conninfo *ci)
{
	struct hidp_session *session;

	session = hidp_session_find(&ci->bdaddr);
	if (session) {
		hidp_copy_session(session, ci);
		hidp_session_put(session);
	}

	return session ? 0 : -ENOENT;
}


static int hidp_input_event(struct input_dev *dev, unsigned int type,
			    unsigned int code, int value)
{
	struct hidp_session *session = input_get_drvdata(dev);
	unsigned char newleds;
	unsigned char hdr, data[2];

	BT_DBG("session %p type %d code %d value %d",
	       session, type, code, value);

	if (type != EV_LED)
		return -1;

	newleds = (!!test_bit(LED_KANA,    dev->led) << 3) |
		  (!!test_bit(LED_COMPOSE, dev->led) << 3) |
		  (!!test_bit(LED_SCROLLL, dev->led) << 2) |
		  (!!test_bit(LED_CAPSL,   dev->led) << 1) |
		  (!!test_bit(LED_NUML,    dev->led) << 0);

	if (session->leds == newleds)
		return 0;

	session->leds = newleds;

	hdr = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT;
	data[0] = 0x01;
	data[1] = newleds;

	return hidp_send_intr_message(session, hdr, data, 2);
}


static int hidp_session_new(struct hidp_session **out, const bdaddr_t *bdaddr,
			    struct socket *ctrl_sock,
			    struct socket *intr_sock,
			    struct hidp_connadd_req *req,
			    struct l2cap_conn *conn)
{
	struct hidp_session *session;
	int ret;
	struct bt_sock *ctrl, *intr;

	ctrl = bt_sk(ctrl_sock->sk);
	intr = bt_sk(intr_sock->sk);

	session = kzalloc(sizeof(*session), GFP_KERNEL);
	if (!session)
		return -ENOMEM;

	/* object and runtime management */
	kref_init(&session->ref);
	atomic_set(&session->state, HIDP_SESSION_IDLING);
	init_waitqueue_head(&session->state_queue);
	session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID);

	/* connection management */
	bacpy(&session->bdaddr, bdaddr);
	session->conn = l2cap_conn_get(conn);
	session->user.probe = hidp_session_probe;
	session->user.remove = hidp_session_remove;
	session->ctrl_sock = ctrl_sock;
	session->intr_sock = intr_sock;
	skb_queue_head_init(&session->ctrl_transmit);
	skb_queue_head_init(&session->intr_transmit);
	session->ctrl_mtu = min_t(uint, l2cap_pi(ctrl)->chan->omtu,
					l2cap_pi(ctrl)->chan->imtu);
	session->intr_mtu = min_t(uint, l2cap_pi(intr)->chan->omtu,
					l2cap_pi(intr)->chan->imtu);
	session->idle_to = req->idle_to;

	/* device management */
	INIT_WORK(&session->dev_init, hidp_session_dev_work);
	setup_timer(&session->timer, hidp_idle_timeout,
		    (unsigned long)session);

	/* session data */
	mutex_init(&session->report_mutex);
	init_waitqueue_head(&session->report_queue);

	ret = hidp_session_dev_init(session, req);
	if (ret)
		goto err_free;

	get_file(session->intr_sock->file);
	get_file(session->ctrl_sock->file);
	*out = session;
	return 0;

err_free:
	l2cap_conn_put(session->conn);
	kfree(session);
	return ret;
}


void server_options(char **args, int *argc_p)
{
	static char argstr[64];
	int ac = *argc_p;
	uchar where;
	char *arg;
	int i, x;

	/* This should always remain first on the server's command-line. */
	args[ac++] = "--server";

	if (daemon_over_rsh > 0) {
		args[ac++] = "--daemon";
		*argc_p = ac;
		/* if we're passing --daemon, we're done */
		return;
	}

	if (!am_sender)
		args[ac++] = "--sender";

	x = 1;
	argstr[0] = '-';

	if (protect_args)
		argstr[x++] = 's';

	for (i = 0; i < verbose; i++)
		argstr[x++] = 'v';

	/* the -q option is intentionally left out */
	if (make_backups)
		argstr[x++] = 'b';
	if (update_only)
		argstr[x++] = 'u';
	if (!do_xfers) /* Note: NOT "dry_run"! */
		argstr[x++] = 'n';
	if (preserve_links)
		argstr[x++] = 'l';
	if ((xfer_dirs >= 2 && xfer_dirs < 4)
	 || (xfer_dirs && !recurse && (list_only || (delete_mode && am_sender))))
		argstr[x++] = 'd';
	if (am_sender) {
		if (keep_dirlinks)
			argstr[x++] = 'K';
		if (prune_empty_dirs)
			argstr[x++] = 'm';
		if (omit_dir_times)
			argstr[x++] = 'O';
		if (omit_link_times)
			argstr[x++] = 'J';
		if (fuzzy_basis) {
			argstr[x++] = 'y';
			if (fuzzy_basis > 1)
				argstr[x++] = 'y';
		}
	} else {
		if (copy_links)
			argstr[x++] = 'L';
		if (copy_dirlinks)
			argstr[x++] = 'k';
	}

	if (whole_file > 0)
		argstr[x++] = 'W';
	/* We don't need to send --no-whole-file, because it's the
	 * default for remote transfers, and in any case old versions
	 * of rsync will not understand it. */

	if (preserve_hard_links) {
		argstr[x++] = 'H';
		if (preserve_hard_links > 1)
			argstr[x++] = 'H';
	}
	if (preserve_uid)
		argstr[x++] = 'o';
	if (preserve_gid)
		argstr[x++] = 'g';
	if (preserve_devices) /* ignore preserve_specials here */
		argstr[x++] = 'D';
	if (preserve_times)
		argstr[x++] = 't';
	if (preserve_perms)
		argstr[x++] = 'p';
	else if (preserve_executability && am_sender)
		argstr[x++] = 'E';
#ifdef SUPPORT_ACLS
	if (preserve_acls)
		argstr[x++] = 'A';
#endif
#ifdef SUPPORT_XATTRS
	if (preserve_xattrs) {
		argstr[x++] = 'X';
		if (preserve_xattrs > 1)
			argstr[x++] = 'X';
	}
#endif
	if (recurse)
		argstr[x++] = 'r';
	if (always_checksum)
		argstr[x++] = 'c';
	if (cvs_exclude)
		argstr[x++] = 'C';
	if (ignore_times)
		argstr[x++] = 'I';
	if (relative_paths)
		argstr[x++] = 'R';
	if (one_file_system) {
		argstr[x++] = 'x';
		if (one_file_system > 1)
			argstr[x++] = 'x';
	}
	if (sparse_files)
		argstr[x++] = 'S';
	if (do_compression == 1)
		argstr[x++] = 'z';

	set_allow_inc_recurse();

	/* We don't really know the actual protocol_version at this point,
	 * but checking the pre-negotiated value allows the user to use a
	 * --protocol=29 override to avoid the use of this -eFLAGS opt. */
	if (protocol_version >= 30) {
		/* Use "eFlags" alias so that cull_options doesn't think that these are no-arg option letters. */
#define eFlags argstr
		/* We make use of the -e option to let the server know about
		 * any pre-release protocol version && some behavior flags. */
		eFlags[x++] = 'e';
#if SUBPROTOCOL_VERSION != 0
		if (protocol_version == PROTOCOL_VERSION) {
			x += snprintf(argstr+x, sizeof argstr - x,
				      "%d.%d",
				      PROTOCOL_VERSION, SUBPROTOCOL_VERSION);
		} else
#endif
			eFlags[x++] = '.';
		if (allow_inc_recurse)
			eFlags[x++] = 'i';
#ifdef CAN_SET_SYMLINK_TIMES
		eFlags[x++] = 'L'; /* symlink time-setting support */
#endif
#ifdef ICONV_OPTION
		eFlags[x++] = 's'; /* symlink iconv translation support */
#endif
		eFlags[x++] = 'f'; /* flist I/O-error safety support */
		eFlags[x++] = 'x'; /* xattr hardlink optimization not desired */
		eFlags[x++] = 'C'; /* support checksum seed order fix */
#undef eFlags
	}

	if (x >= (int)sizeof argstr) { /* Not possible... */
		rprintf(FERROR, "argstr overflow in server_options().\n");
		exit_cleanup(RERR_MALLOC);
	}

	argstr[x] = '\0';

	if (x > 1)
		args[ac++] = argstr;

#ifdef ICONV_OPTION
	if (iconv_opt) {
		char *set = strchr(iconv_opt, ',');
		if (set)
			set++;
		else
			set = iconv_opt;
		if (asprintf(&arg, "--iconv=%s", set) < 0)
			goto oom;
		args[ac++] = arg;
	}
#endif

	if (protect_args && !local_server) /* unprotected args stop here */
		args[ac++] = NULL;

	if (list_only > 1)
		args[ac++] = "--list-only";

	/* This makes sure that the remote rsync can handle deleting with -d
	 * sans -r because the --no-r option was added at the same time. */
	if (xfer_dirs && !recurse && delete_mode && am_sender)
		args[ac++] = "--no-r";

	if (do_compression && def_compress_level != Z_DEFAULT_COMPRESSION) {
		if (asprintf(&arg, "--compress-level=%d", def_compress_level) < 0)
			goto oom;
		args[ac++] = arg;
	}

	if (preserve_devices) {
		/* Note: sending "--devices" would not be backward-compatible. */
		if (!preserve_specials)
			args[ac++] = "--no-specials"; /* -D is already set. */
	} else if (preserve_specials)
		args[ac++] = "--specials";

	/* The server side doesn't use our log-format, but in certain
	 * circumstances they need to know a little about the option. */
	if (stdout_format && am_sender) {
		/* Use --log-format, not --out-format, for compatibility. */
		if (stdout_format_has_i > 1)
			args[ac++] = "--log-format=%i%I";
		else if (stdout_format_has_i)
			args[ac++] = "--log-format=%i";
		else if (stdout_format_has_o_or_i)
			args[ac++] = "--log-format=%o";
		else if (!verbose)
			args[ac++] = "--log-format=X";
	}

	if (block_size) {
		if (asprintf(&arg, "-B%lu", block_size) < 0)
			goto oom;
		args[ac++] = arg;
	}

	if (io_timeout) {
		if (asprintf(&arg, "--timeout=%d", io_timeout) < 0)
			goto oom;
		args[ac++] = arg;
	}

	if (bwlimit) {
		if (asprintf(&arg, "--bwlimit=%d", bwlimit) < 0)
			goto oom;
		args[ac++] = arg;
	}

	if (backup_dir) {
		args[ac++] = "--backup-dir";
		args[ac++] = backup_dir;
	}

	/* Only send --suffix if it specifies a non-default value. */
	if (strcmp(backup_suffix, backup_dir ? "" : BACKUP_SUFFIX) != 0) {
		/* We use the following syntax to avoid weirdness with '~'. */
		if (asprintf(&arg, "--suffix=%s", backup_suffix) < 0)
			goto oom;
		args[ac++] = arg;
	}

	if (checksum_choice) {
		if (asprintf(&arg, "--checksum-choice=%s", checksum_choice) < 0)
			goto oom;
		args[ac++] = arg;
	}

	if (am_sender) {
		if (max_delete > 0) {
			if (asprintf(&arg, "--max-delete=%d", max_delete) < 0)
				goto oom;
			args[ac++] = arg;
		} else if (max_delete == 0)
			args[ac++] = "--max-delete=-1";
		if (min_size >= 0) {
			args[ac++] = "--min-size";
			args[ac++] = min_size_arg;
		}
		if (max_size >= 0) {
			args[ac++] = "--max-size";
			args[ac++] = max_size_arg;
		}
		if (delete_before)
			args[ac++] = "--delete-before";
		else if (delete_during == 2)
			args[ac++] = "--delete-delay";
		else if (delete_during)
			args[ac++] = "--delete-during";
		else if (delete_after)
			args[ac++] = "--delete-after";
		else if (delete_mode && !delete_excluded)
			args[ac++] = "--delete";
		if (delete_excluded)
			args[ac++] = "--delete-excluded";
		if (force_delete)
			args[ac++] = "--force";
		if (write_batch < 0)
			args[ac++] = "--only-write-batch=X";
		if (am_root > 1)
			args[ac++] = "--super";
		if (size_only)
			args[ac++] = "--size-only";
		if (do_stats)
			args[ac++] = "--stats";
	} else {
		if (skip_compress) {
			if (asprintf(&arg, "--skip-compress=%s", skip_compress) < 0)
				goto oom;
			args[ac++] = arg;
		}
	}

	/* --delete-missing-args needs the cooperation of both sides, but
	 * the sender can handle --ignore-missing-args by itself. */
	if (missing_args == 2)
		args[ac++] = "--delete-missing-args";
	else if (missing_args == 1 && !am_sender)
		args[ac++] = "--ignore-missing-args";

	if (modify_window_set && am_sender) {
		char *fmt = modify_window < 0 ? "-@%d" : "--modify-window=%d";
		if (asprintf(&arg, fmt, modify_window) < 0)
			goto oom;
		args[ac++] = arg;
	}

	if (checksum_seed) {
		if (asprintf(&arg, "--checksum-seed=%d", checksum_seed) < 0)
			goto oom;
		args[ac++] = arg;
	}

	if (partial_dir && am_sender) {
		if (partial_dir != tmp_partialdir) {
			args[ac++] = "--partial-dir";
			args[ac++] = partial_dir;
		}
		if (delay_updates)
			args[ac++] = "--delay-updates";
	} else if (keep_partial && am_sender)
		args[ac++] = "--partial";

	if (ignore_errors)
		args[ac++] = "--ignore-errors";

	if (copy_unsafe_links)
		args[ac++] = "--copy-unsafe-links";

	if (safe_symlinks)
		args[ac++] = "--safe-links";

	if (numeric_ids)
		args[ac++] = "--numeric-ids";

	if (use_qsort)
		args[ac++] = "--use-qsort";

	if (am_sender) {
		if (usermap) {
			if (asprintf(&arg, "--usermap=%s", usermap) < 0)
				goto oom;
			args[ac++] = arg;
		}

		if (groupmap) {
			if (asprintf(&arg, "--groupmap=%s", groupmap) < 0)
				goto oom;
			args[ac++] = arg;
		}

		if (ignore_existing)
			args[ac++] = "--ignore-existing";

		/* Backward compatibility: send --existing, not --ignore-non-existing. */
		if (ignore_non_existing)
			args[ac++] = "--existing";

		if (tmpdir) {
			args[ac++] = "--temp-dir";
			args[ac++] = tmpdir;
		}

		if (basis_dir[0]) {
			/* the server only needs this option if it is not the sender,
			 *   and it may be an older version that doesn't know this
			 *   option, so don't send it if client is the sender.
			 */
			for (i = 0; i < basis_dir_cnt; i++) {
				args[ac++] = dest_option;
				args[ac++] = basis_dir[i];
			}
		}
	}

	/* What flags do we need to send to the other side? */
	where = (am_server ? W_CLI : W_SRV) | (am_sender ? W_REC : W_SND);
	arg = make_output_option(info_words, info_levels, where);
	if (arg)
		args[ac++] = arg;

	arg = make_output_option(debug_words, debug_levels, where);
	if (arg)
		args[ac++] = arg;

	if (append_mode) {
		if (append_mode > 1)
			args[ac++] = "--append";
		args[ac++] = "--append";
	} else if (inplace)
		args[ac++] = "--inplace";

	if (files_from && (!am_sender || filesfrom_host)) {
		if (filesfrom_host) {
			args[ac++] = "--files-from";
			args[ac++] = files_from;
			if (eol_nulls)
				args[ac++] = "--from0";
		} else {
			args[ac++] = "--files-from=-";
			args[ac++] = "--from0";
		}
		if (!relative_paths)
			args[ac++] = "--no-relative";
	}
	/* It's OK that this checks the upper-bound of the protocol_version. */
	if (relative_paths && !implied_dirs && (!am_sender || protocol_version >= 30))
		args[ac++] = "--no-implied-dirs";

	if (remove_source_files == 1)
		args[ac++] = "--remove-source-files";
	else if (remove_source_files)
		args[ac++] = "--remove-sent-files";

	if (preallocate_files && am_sender)
		args[ac++] = "--preallocate";

	if (ac > MAX_SERVER_ARGS) { /* Not possible... */
		rprintf(FERROR, "argc overflow in server_options().\n");
		exit_cleanup(RERR_MALLOC);
	}

	if (do_compression > 1)
		args[ac++] = "--new-compress";

	if (remote_option_cnt) {
		int j;
		if (ac + remote_option_cnt > MAX_SERVER_ARGS) {
			rprintf(FERROR, "too many remote options specified.\n");
			exit_cleanup(RERR_SYNTAX);
		}
		for (j = 1; j <= remote_option_cnt; j++)
			args[ac++] = (char*)remote_options[j];
	}

	*argc_p = ac;
	return;

    oom:
	out_of_memory("server_options");
}


static js_Ast *bitor(js_State *J, int notin)
{
	js_Ast *a = bitxor(J, notin);
	SAVEREC();
	while (jsP_accept(J, '|')) {
		INCREC();
		a = EXP2(BITOR, a, bitxor(J, notin));
	}
	POPREC();
	return a;
}


int acpi_smbus_register_callback(struct acpi_smb_hc *hc,
			         smbus_alarm_callback callback, void *context)
{
	mutex_lock(&hc->lock);
	hc->callback = callback;
	hc->context = context;
	mutex_unlock(&hc->lock);
	return 0;
}


static int parse_acls_from_arg(Item *item) {
#if HAVE_ACL
        int r;

        assert(item);

        /* If force (= modify) is set, we will not modify the acl
         * afterwards, so the mask can be added now if necessary. */

        r = parse_acl(item->argument, &item->acl_access, &item->acl_default, !item->force);
        if (r < 0)
                log_warning_errno(r, "Failed to parse ACL \"%s\": %m. Ignoring", item->argument);
#else
        log_warning_errno(ENOSYS, "ACLs are not supported. Ignoring");
#endif

        return 0;
}


static int dir_is_mount_point(DIR *d, const char *subdir) {

        int mount_id_parent, mount_id;
        int r_p, r;

        r_p = name_to_handle_at_loop(dirfd(d), ".", NULL, &mount_id_parent, 0);
        if (r_p < 0)
                r_p = -errno;

        r = name_to_handle_at_loop(dirfd(d), subdir, NULL, &mount_id, 0);
        if (r < 0)
                r = -errno;

        /* got no handle; make no assumptions, return error */
        if (r_p < 0 && r < 0)
                return r_p;

        /* got both handles; if they differ, it is a mount point */
        if (r_p >= 0 && r >= 0)
                return mount_id_parent != mount_id;

        /* got only one handle; assume different mount points if one
         * of both queries was not supported by the filesystem */
        if (IN_SET(r_p, -ENOSYS, -EOPNOTSUPP) || IN_SET(r, -ENOSYS, -EOPNOTSUPP))
                return true;

        /* return error */
        if (r_p < 0)
                return r_p;
        return r;
}


static bool takes_ownership(ItemType t) {
        return IN_SET(t,
                      CREATE_FILE,
                      TRUNCATE_FILE,
                      CREATE_DIRECTORY,
                      EMPTY_DIRECTORY,
                      TRUNCATE_DIRECTORY,
                      CREATE_SUBVOLUME,
                      CREATE_SUBVOLUME_INHERIT_QUOTA,
                      CREATE_SUBVOLUME_NEW_QUOTA,
                      CREATE_FIFO,
                      CREATE_SYMLINK,
                      CREATE_CHAR_DEVICE,
                      CREATE_BLOCK_DEVICE,
                      COPY_FILES,
                      WRITE_FILE,
                      IGNORE_PATH,
                      IGNORE_DIRECTORY_PATH,
                      REMOVE_PATH,
                      RECURSIVE_REMOVE_PATH);
}


int stub_tx_loop(void *data)
{
	struct usbip_device *ud = data;
	struct stub_device *sdev = container_of(ud, struct stub_device, ud);

	while (!kthread_should_stop()) {
		if (usbip_event_happened(ud))
			break;

		/*
		 * send_ret_submit comes earlier than send_ret_unlink.  stub_rx
		 * looks at only priv_init queue. If the completion of a URB is
		 * earlier than the receive of CMD_UNLINK, priv is moved to
		 * priv_tx queue and stub_rx does not find the target priv. In
		 * this case, vhci_rx receives the result of the submit request
		 * and then receives the result of the unlink request. The
		 * result of the submit is given back to the usbcore as the
		 * completion of the unlink request. The request of the
		 * unlink is ignored. This is ok because a driver who calls
		 * usb_unlink_urb() understands the unlink was too late by
		 * getting the status of the given-backed URB which has the
		 * status of usb_submit_urb().
		 */
		if (stub_send_ret_submit(sdev) < 0)
			break;

		if (stub_send_ret_unlink(sdev) < 0)
			break;

		wait_event_interruptible(sdev->tx_waitq,
					 (!list_empty(&sdev->priv_tx) ||
					  !list_empty(&sdev->unlink_tx) ||
					  kthread_should_stop()));
	}

	return 0;
}


static inline u8 bnx2x_fp_igu_sb_id(struct bnx2x_fastpath *fp)
{
	return fp->bp->igu_base_sb + fp->index + CNIC_SUPPORT(fp->bp);
}


static void storm_memset_spq_addr(struct bnx2x *bp,
				  dma_addr_t mapping, u16 abs_fid)
{
	u32 addr = XSEM_REG_FAST_MEMORY +
			XSTORM_SPQ_PAGE_BASE_OFFSET(abs_fid);

	__storm_memset_dma_mapping(bp, addr, mapping);
}


static int bnx2x_get_igu_cam_info(struct bnx2x *bp)
{
	int pfid = BP_FUNC(bp);
	int igu_sb_id;
	u32 val;
	u8 fid, igu_sb_cnt = 0;

	bp->igu_base_sb = 0xff;
	if (CHIP_INT_MODE_IS_BC(bp)) {
		int vn = BP_VN(bp);
		igu_sb_cnt = bp->igu_sb_cnt;
		bp->igu_base_sb = (CHIP_MODE_IS_4_PORT(bp) ? pfid : vn) *
			FP_SB_MAX_E1x;

		bp->igu_dsb_id =  E1HVN_MAX * FP_SB_MAX_E1x +
			(CHIP_MODE_IS_4_PORT(bp) ? pfid : vn);

		return 0;
	}

	/* IGU in normal mode - read CAM */
	for (igu_sb_id = 0; igu_sb_id < IGU_REG_MAPPING_MEMORY_SIZE;
	     igu_sb_id++) {
		val = REG_RD(bp, IGU_REG_MAPPING_MEMORY + igu_sb_id * 4);
		if (!(val & IGU_REG_MAPPING_MEMORY_VALID))
			continue;
		fid = IGU_FID(val);
		if ((fid & IGU_FID_ENCODE_IS_PF)) {
			if ((fid & IGU_FID_PF_NUM_MASK) != pfid)
				continue;
			if (IGU_VEC(val) == 0)
				/* default status block */
				bp->igu_dsb_id = igu_sb_id;
			else {
				if (bp->igu_base_sb == 0xff)
					bp->igu_base_sb = igu_sb_id;
				igu_sb_cnt++;
			}
		}
	}

#ifdef CONFIG_PCI_MSI
	/* Due to new PF resource allocation by MFW T7.4 and above, it's
	 * optional that number of CAM entries will not be equal to the value
	 * advertised in PCI.
	 * Driver should use the minimal value of both as the actual status
	 * block count
	 */
	bp->igu_sb_cnt = min_t(int, bp->igu_sb_cnt, igu_sb_cnt);
#endif

	if (igu_sb_cnt == 0) {
		BNX2X_ERR("CAM configuration error\n");
		return -EINVAL;
	}

	return 0;
}


static void bnx2x_init_internal(struct bnx2x *bp, u32 load_code)
{
	switch (load_code) {
	case FW_MSG_CODE_DRV_LOAD_COMMON:
	case FW_MSG_CODE_DRV_LOAD_COMMON_CHIP:
		bnx2x_init_internal_common(bp);
		/* no break */

	case FW_MSG_CODE_DRV_LOAD_PORT:
		/* nothing to do */
		/* no break */

	case FW_MSG_CODE_DRV_LOAD_FUNCTION:
		/* internal memory per function is
		   initialized inside bnx2x_pf_init */
		break;

	default:
		BNX2X_ERR("Unknown load_code (0x%x) from MCP\n", load_code);
		break;
	}
}


static inline int bnx2x_func_switch_update(struct bnx2x *bp, int suspend)
{
	int rc;
	struct bnx2x_func_state_params func_params = {NULL};
	struct bnx2x_func_switch_update_params *switch_update_params =
		&func_params.params.switch_update;

	/* Prepare parameters for function state transitions */
	__set_bit(RAMROD_COMP_WAIT, &func_params.ramrod_flags);
	__set_bit(RAMROD_RETRY, &func_params.ramrod_flags);

	func_params.f_obj = &bp->func_obj;
	func_params.cmd = BNX2X_F_CMD_SWITCH_UPDATE;

	/* Function parameters */
	__set_bit(BNX2X_F_UPDATE_TX_SWITCH_SUSPEND_CHNG,
		  &switch_update_params->changes);
	if (suspend)
		__set_bit(BNX2X_F_UPDATE_TX_SWITCH_SUSPEND,
			  &switch_update_params->changes);

	rc = bnx2x_func_state_change(bp, &func_params);

	return rc;
}


static pci_ers_result_t bnx2x_io_error_detected(struct pci_dev *pdev,
						pci_channel_state_t state)
{
	struct net_device *dev = pci_get_drvdata(pdev);
	struct bnx2x *bp = netdev_priv(dev);

	rtnl_lock();

	BNX2X_ERR("IO error detected\n");

	netif_device_detach(dev);

	if (state == pci_channel_io_perm_failure) {
		rtnl_unlock();
		return PCI_ERS_RESULT_DISCONNECT;
	}

	if (netif_running(dev))
		bnx2x_eeh_nic_unload(bp);

	bnx2x_prev_path_mark_eeh(bp);

	pci_disable_device(pdev);

	rtnl_unlock();

	/* Request a slot reset */
	return PCI_ERS_RESULT_NEED_RESET;
}


static void bnx2x_wr_sp_sb_data(struct bnx2x *bp,
		struct hc_sp_status_block_data *sp_sb_data)
{
	int func = BP_FUNC(bp);
	int i;
	for (i = 0; i < sizeof(struct hc_sp_status_block_data)/sizeof(u32); i++)
		REG_WR(bp, BAR_CSTRORM_INTMEM +
			CSTORM_SP_STATUS_BLOCK_DATA_OFFSET(func) +
			i*sizeof(u32),
			*((u32 *)sp_sb_data + i));
}


static void bnx2x_attn_int_deasserted3(struct bnx2x *bp, u32 attn)
{
	u32 val;

	if (attn & EVEREST_GEN_ATTN_IN_USE_MASK) {

		if (attn & BNX2X_PMF_LINK_ASSERT) {
			int func = BP_FUNC(bp);

			REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_12 + func*4, 0);
			bnx2x_read_mf_cfg(bp);
			bp->mf_config[BP_VN(bp)] = MF_CFG_RD(bp,
					func_mf_config[BP_ABS_FUNC(bp)].config);
			val = SHMEM_RD(bp,
				       func_mb[BP_FW_MB_IDX(bp)].drv_status);

			if (val & (DRV_STATUS_DCC_EVENT_MASK |
				   DRV_STATUS_OEM_EVENT_MASK))
				bnx2x_oem_event(bp,
					(val & (DRV_STATUS_DCC_EVENT_MASK |
						DRV_STATUS_OEM_EVENT_MASK)));

			if (val & DRV_STATUS_SET_MF_BW)
				bnx2x_set_mf_bw(bp);

			if (val & DRV_STATUS_DRV_INFO_REQ)
				bnx2x_handle_drv_info_req(bp);

			if (val & DRV_STATUS_VF_DISABLED)
				bnx2x_schedule_iov_task(bp,
							BNX2X_IOV_HANDLE_FLR);

			if ((bp->port.pmf == 0) && (val & DRV_STATUS_PMF))
				bnx2x_pmf_update(bp);

			if (bp->port.pmf &&
			    (val & DRV_STATUS_DCBX_NEGOTIATION_RESULTS) &&
				bp->dcbx_enabled > 0)
				/* start dcbx state machine */
				bnx2x_dcbx_set_params(bp,
					BNX2X_DCBX_STATE_NEG_RECEIVED);
			if (val & DRV_STATUS_AFEX_EVENT_MASK)
				bnx2x_handle_afex_cmd(bp,
					val & DRV_STATUS_AFEX_EVENT_MASK);
			if (val & DRV_STATUS_EEE_NEGOTIATION_RESULTS)
				bnx2x_handle_eee_event(bp);

			if (val & DRV_STATUS_OEM_UPDATE_SVID)
				bnx2x_handle_update_svid_cmd(bp);

			if (bp->link_vars.periodic_flags &
			    PERIODIC_FLAGS_LINK_EVENT) {
				/*  sync with link */
				bnx2x_acquire_phy_lock(bp);
				bp->link_vars.periodic_flags &=
					~PERIODIC_FLAGS_LINK_EVENT;
				bnx2x_release_phy_lock(bp);
				if (IS_MF(bp))
					bnx2x_link_sync_notify(bp);
				bnx2x_link_report(bp);
			}
			/* Always call it here: bnx2x_link_report() will
			 * prevent the link indication duplication.
			 */
			bnx2x__link_status_update(bp);
		} else if (attn & BNX2X_MC_ASSERT_BITS) {

			BNX2X_ERR("MC assert!\n");
			bnx2x_mc_assert(bp);
			REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_10, 0);
			REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_9, 0);
			REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_8, 0);
			REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_7, 0);
			bnx2x_panic();

		} else if (attn & BNX2X_MCP_ASSERT) {

			BNX2X_ERR("MCP assert!\n");
			REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_11, 0);
			bnx2x_fw_dump(bp);

		} else
			BNX2X_ERR("Unknown HW assert! (attn 0x%x)\n", attn);
	}

	if (attn & EVEREST_LATCHED_ATTN_IN_USE_MASK) {
		BNX2X_ERR("LATCHED attention 0x%08x (masked)\n", attn);
		if (attn & BNX2X_GRC_TIMEOUT) {
			val = CHIP_IS_E1(bp) ? 0 :
					REG_RD(bp, MISC_REG_GRC_TIMEOUT_ATTN);
			BNX2X_ERR("GRC time-out 0x%08x\n", val);
		}
		if (attn & BNX2X_GRC_RSV) {
			val = CHIP_IS_E1(bp) ? 0 :
					REG_RD(bp, MISC_REG_GRC_RSV_ATTN);
			BNX2X_ERR("GRC reserved 0x%08x\n", val);
		}
		REG_WR(bp, MISC_REG_AEU_CLR_LATCH_SIGNAL, 0x7ff);
	}
}


int bnx2x_pretend_func(struct bnx2x *bp, u16 pretend_func_val)
{
	u32 pretend_reg;

	if (CHIP_IS_E1H(bp) && pretend_func_val >= E1H_FUNC_MAX)
		return -1;

	/* get my own pretend register */
	pretend_reg = bnx2x_get_pretend_reg(bp);
	REG_WR(bp, pretend_reg, pretend_func_val);
	REG_RD(bp, pretend_reg);
	return 0;
}


static void bnx2x_disable_ptp(struct bnx2x *bp)
{
	int port = BP_PORT(bp);

	/* Disable sending PTP packets to host */
	REG_WR(bp, port ? NIG_REG_P1_LLH_PTP_TO_HOST :
	       NIG_REG_P0_LLH_PTP_TO_HOST, 0x0);

	/* Reset PTP event detection rules */
	REG_WR(bp, port ? NIG_REG_P1_LLH_PTP_PARAM_MASK :
	       NIG_REG_P0_LLH_PTP_PARAM_MASK, 0x7FF);
	REG_WR(bp, port ? NIG_REG_P1_LLH_PTP_RULE_MASK :
	       NIG_REG_P0_LLH_PTP_RULE_MASK, 0x3FFF);
	REG_WR(bp, port ? NIG_REG_P1_TLLH_PTP_PARAM_MASK :
	       NIG_REG_P0_TLLH_PTP_PARAM_MASK, 0x7FF);
	REG_WR(bp, port ? NIG_REG_P1_TLLH_PTP_RULE_MASK :
	       NIG_REG_P0_TLLH_PTP_RULE_MASK, 0x3FFF);

	/* Disable the PTP feature */
	REG_WR(bp, port ? NIG_REG_P1_PTP_EN :
	       NIG_REG_P0_PTP_EN, 0x0);
}


static int bnx2x_cnic_ctl_send_bh(struct bnx2x *bp, struct cnic_ctl_info *ctl)
{
	struct cnic_ops *c_ops;
	int rc = 0;

	rcu_read_lock();
	c_ops = rcu_dereference(bp->cnic_ops);
	if (c_ops)
		rc = c_ops->cnic_ctl(bp->cnic_data, ctl);
	rcu_read_unlock();

	return rc;
}


static int bnx2x_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
{
	struct bnx2x *bp = container_of(ptp, struct bnx2x, ptp_clock_info);

	if (!netif_running(bp->dev)) {
		DP(BNX2X_MSG_PTP,
		   "PTP adjtime called while the interface is down\n");
		return -ENETDOWN;
	}

	DP(BNX2X_MSG_PTP, "PTP adjtime called, delta = %llx\n", delta);

	timecounter_adjtime(&bp->timecounter, delta);

	return 0;
}


static void bnx2x_set_endianity(struct bnx2x *bp)
{
#ifdef __BIG_ENDIAN
	bnx2x_config_endianity(bp, 1);
#else
	bnx2x_config_endianity(bp, 0);
#endif
}


irqreturn_t bnx2x_interrupt(int irq, void *dev_instance)
{
	struct bnx2x *bp = netdev_priv(dev_instance);
	u16 status = bnx2x_ack_int(bp);
	u16 mask;
	int i;
	u8 cos;

	/* Return here if interrupt is shared and it's not for us */
	if (unlikely(status == 0)) {
		DP(NETIF_MSG_INTR, "not our interrupt!\n");
		return IRQ_NONE;
	}
	DP(NETIF_MSG_INTR, "got an interrupt  status 0x%x\n", status);

#ifdef BNX2X_STOP_ON_ERROR
	if (unlikely(bp->panic))
		return IRQ_HANDLED;
#endif

	for_each_eth_queue(bp, i) {
		struct bnx2x_fastpath *fp = &bp->fp[i];

		mask = 0x2 << (fp->index + CNIC_SUPPORT(bp));
		if (status & mask) {
			/* Handle Rx or Tx according to SB id */
			for_each_cos_in_tx_queue(fp, cos)
				prefetch(fp->txdata_ptr[cos]->tx_cons_sb);
			prefetch(&fp->sb_running_index[SM_RX_ID]);
			napi_schedule_irqoff(&bnx2x_fp(bp, fp->index, napi));
			status &= ~mask;
		}
	}

	if (CNIC_SUPPORT(bp)) {
		mask = 0x2;
		if (status & (mask | 0x1)) {
			struct cnic_ops *c_ops = NULL;

			rcu_read_lock();
			c_ops = rcu_dereference(bp->cnic_ops);
			if (c_ops && (bp->cnic_eth_dev.drv_state &
				      CNIC_DRV_STATE_HANDLES_IRQ))
				c_ops->cnic_handler(bp->cnic_data, NULL);
			rcu_read_unlock();

			status &= ~mask;
		}
	}

	if (unlikely(status & 0x1)) {

		/* schedule sp task to perform default status block work, ack
		 * attentions and enable interrupts.
		 */
		bnx2x_schedule_sp_task(bp);

		status &= ~0x1;
		if (!status)
			return IRQ_HANDLED;
	}

	if (unlikely(status))
		DP(NETIF_MSG_INTR, "got an unknown interrupt! (status 0x%x)\n",
		   status);

	return IRQ_HANDLED;
}


static int bnx2x_open(struct net_device *dev)
{
	struct bnx2x *bp = netdev_priv(dev);
	int rc;

	bp->stats_init = true;

	netif_carrier_off(dev);

	bnx2x_set_power_state(bp, PCI_D0);

	/* If parity had happen during the unload, then attentions
	 * and/or RECOVERY_IN_PROGRES may still be set. In this case we
	 * want the first function loaded on the current engine to
	 * complete the recovery.
	 * Parity recovery is only relevant for PF driver.
	 */
	if (IS_PF(bp)) {
		int other_engine = BP_PATH(bp) ? 0 : 1;
		bool other_load_status, load_status;
		bool global = false;

		other_load_status = bnx2x_get_load_status(bp, other_engine);
		load_status = bnx2x_get_load_status(bp, BP_PATH(bp));
		if (!bnx2x_reset_is_done(bp, BP_PATH(bp)) ||
		    bnx2x_chk_parity_attn(bp, &global, true)) {
			do {
				/* If there are attentions and they are in a
				 * global blocks, set the GLOBAL_RESET bit
				 * regardless whether it will be this function
				 * that will complete the recovery or not.
				 */
				if (global)
					bnx2x_set_reset_global(bp);

				/* Only the first function on the current
				 * engine should try to recover in open. In case
				 * of attentions in global blocks only the first
				 * in the chip should try to recover.
				 */
				if ((!load_status &&
				     (!global || !other_load_status)) &&
				      bnx2x_trylock_leader_lock(bp) &&
				      !bnx2x_leader_reset(bp)) {
					netdev_info(bp->dev,
						    "Recovered in open\n");
					break;
				}

				/* recovery has failed... */
				bnx2x_set_power_state(bp, PCI_D3hot);
				bp->recovery_state = BNX2X_RECOVERY_FAILED;

				BNX2X_ERR("Recovery flow hasn't been properly completed yet. Try again later.\n"
					  "If you still see this message after a few retries then power cycle is required.\n");

				return -EAGAIN;
			} while (0);
		}
	}

	bp->recovery_state = BNX2X_RECOVERY_DONE;
	rc = bnx2x_nic_load(bp, LOAD_OPEN);
	if (rc)
		return rc;

	if (IS_PF(bp))
		udp_tunnel_get_rx_info(dev);

	return 0;
}


int bnx2x_sp_post(struct bnx2x *bp, int command, int cid,
		  u32 data_hi, u32 data_lo, int cmd_type)
{
	struct eth_spe *spe;
	u16 type;
	bool common = bnx2x_is_contextless_ramrod(command, cmd_type);

#ifdef BNX2X_STOP_ON_ERROR
	if (unlikely(bp->panic)) {
		BNX2X_ERR("Can't post SP when there is panic\n");
		return -EIO;
	}
#endif

	spin_lock_bh(&bp->spq_lock);

	if (common) {
		if (!atomic_read(&bp->eq_spq_left)) {
			BNX2X_ERR("BUG! EQ ring full!\n");
			spin_unlock_bh(&bp->spq_lock);
			bnx2x_panic();
			return -EBUSY;
		}
	} else if (!atomic_read(&bp->cq_spq_left)) {
			BNX2X_ERR("BUG! SPQ ring full!\n");
			spin_unlock_bh(&bp->spq_lock);
			bnx2x_panic();
			return -EBUSY;
	}

	spe = bnx2x_sp_get_next(bp);

	/* CID needs port number to be encoded int it */
	spe->hdr.conn_and_cmd_data =
			cpu_to_le32((command << SPE_HDR_CMD_ID_SHIFT) |
				    HW_CID(bp, cid));

	/* In some cases, type may already contain the func-id
	 * mainly in SRIOV related use cases, so we add it here only
	 * if it's not already set.
	 */
	if (!(cmd_type & SPE_HDR_FUNCTION_ID)) {
		type = (cmd_type << SPE_HDR_CONN_TYPE_SHIFT) &
			SPE_HDR_CONN_TYPE;
		type |= ((BP_FUNC(bp) << SPE_HDR_FUNCTION_ID_SHIFT) &
			 SPE_HDR_FUNCTION_ID);
	} else {
		type = cmd_type;
	}

	spe->hdr.type = cpu_to_le16(type);

	spe->data.update_data_addr.hi = cpu_to_le32(data_hi);
	spe->data.update_data_addr.lo = cpu_to_le32(data_lo);

	/*
	 * It's ok if the actual decrement is issued towards the memory
	 * somewhere between the spin_lock and spin_unlock. Thus no
	 * more explicit memory barrier is needed.
	 */
	if (common)
		atomic_dec(&bp->eq_spq_left);
	else
		atomic_dec(&bp->cq_spq_left);

	DP(BNX2X_MSG_SP,
	   "SPQE[%x] (%x:%x)  (cmd, common?) (%d,%d)  hw_cid %x  data (%x:%x) type(0x%x) left (CQ, EQ) (%x,%x)\n",
	   bp->spq_prod_idx, (u32)U64_HI(bp->spq_mapping),
	   (u32)(U64_LO(bp->spq_mapping) +
	   (void *)bp->spq_prod_bd - (void *)bp->spq), command, common,
	   HW_CID(bp, cid), data_hi, data_lo, type,
	   atomic_read(&bp->cq_spq_left), atomic_read(&bp->eq_spq_left));

	bnx2x_sp_prod_update(bp);
	spin_unlock_bh(&bp->spq_lock);
	return 0;
}


static int bnx2x_prev_path_mark_eeh(struct bnx2x *bp)
{
	struct bnx2x_prev_path_list *tmp_list;
	int rc;

	rc = down_interruptible(&bnx2x_prev_sem);
	if (rc) {
		BNX2X_ERR("Received %d when tried to take lock\n", rc);
		return rc;
	}

	tmp_list = bnx2x_prev_path_get_entry(bp);
	if (tmp_list) {
		tmp_list->aer = 1;
		rc = 0;
	} else {
		BNX2X_ERR("path %d: Entry does not exist for eeh; Flow occurs before initial insmod is over ?\n",
			  BP_PATH(bp));
	}

	up(&bnx2x_prev_sem);

	return rc;
}


static bool bnx2x_check_blocks_with_parity2(struct bnx2x *bp, u32 sig,
					    int *par_num, bool print)
{
	u32 cur_bit;
	bool res;
	int i;

	res = false;

	for (i = 0; sig; i++) {
		cur_bit = (0x1UL << i);
		if (sig & cur_bit) {
			res = true; /* Each bit is real error! */
			if (print) {
				switch (cur_bit) {
				case AEU_INPUTS_ATTN_BITS_CSEMI_PARITY_ERROR:
					_print_next_block((*par_num)++,
							  "CSEMI");
					_print_parity(bp,
						      CSEM_REG_CSEM_PRTY_STS_0);
					_print_parity(bp,
						      CSEM_REG_CSEM_PRTY_STS_1);
					break;
				case AEU_INPUTS_ATTN_BITS_PXP_PARITY_ERROR:
					_print_next_block((*par_num)++, "PXP");
					_print_parity(bp, PXP_REG_PXP_PRTY_STS);
					_print_parity(bp,
						      PXP2_REG_PXP2_PRTY_STS_0);
					_print_parity(bp,
						      PXP2_REG_PXP2_PRTY_STS_1);
					break;
				case AEU_IN_ATTN_BITS_PXPPCICLOCKCLIENT_PARITY_ERROR:
					_print_next_block((*par_num)++,
							  "PXPPCICLOCKCLIENT");
					break;
				case AEU_INPUTS_ATTN_BITS_CFC_PARITY_ERROR:
					_print_next_block((*par_num)++, "CFC");
					_print_parity(bp,
						      CFC_REG_CFC_PRTY_STS);
					break;
				case AEU_INPUTS_ATTN_BITS_CDU_PARITY_ERROR:
					_print_next_block((*par_num)++, "CDU");
					_print_parity(bp, CDU_REG_CDU_PRTY_STS);
					break;
				case AEU_INPUTS_ATTN_BITS_DMAE_PARITY_ERROR:
					_print_next_block((*par_num)++, "DMAE");
					_print_parity(bp,
						      DMAE_REG_DMAE_PRTY_STS);
					break;
				case AEU_INPUTS_ATTN_BITS_IGU_PARITY_ERROR:
					_print_next_block((*par_num)++, "IGU");
					if (CHIP_IS_E1x(bp))
						_print_parity(bp,
							HC_REG_HC_PRTY_STS);
					else
						_print_parity(bp,
							IGU_REG_IGU_PRTY_STS);
					break;
				case AEU_INPUTS_ATTN_BITS_MISC_PARITY_ERROR:
					_print_next_block((*par_num)++, "MISC");
					_print_parity(bp,
						      MISC_REG_MISC_PRTY_STS);
					break;
				}
			}

			/* Clear the bit */
			sig &= ~cur_bit;
		}
	}

	return res;
}


static void bnx2x_config_endianity(struct bnx2x *bp, u32 val)
{
	REG_WR(bp, PXP2_REG_RQ_QM_ENDIAN_M, val);
	REG_WR(bp, PXP2_REG_RQ_TM_ENDIAN_M, val);
	REG_WR(bp, PXP2_REG_RQ_SRC_ENDIAN_M, val);
	REG_WR(bp, PXP2_REG_RQ_CDU_ENDIAN_M, val);
	REG_WR(bp, PXP2_REG_RQ_DBG_ENDIAN_M, val);

	/* make sure this value is 0 */
	REG_WR(bp, PXP2_REG_RQ_HC_ENDIAN_M, 0);

	REG_WR(bp, PXP2_REG_RD_QM_SWAP_MODE, val);
	REG_WR(bp, PXP2_REG_RD_TM_SWAP_MODE, val);
	REG_WR(bp, PXP2_REG_RD_SRC_SWAP_MODE, val);
	REG_WR(bp, PXP2_REG_RD_CDURD_SWAP_MODE, val);
}


static void bnx2x_set_requested_fc(struct bnx2x *bp)
{
	/* Initialize link parameters structure variables
	 * It is recommended to turn off RX FC for jumbo frames
	 *  for better performance
	 */
	if (CHIP_IS_E1x(bp) && (bp->dev->mtu > 5000))
		bp->link_params.req_fc_auto_adv = BNX2X_FLOW_CTRL_TX;
	else
		bp->link_params.req_fc_auto_adv = BNX2X_FLOW_CTRL_BOTH;
}


static int bnx2x_int_mem_test(struct bnx2x *bp)
{
	int factor;
	int count, i;
	u32 val = 0;

	if (CHIP_REV_IS_FPGA(bp))
		factor = 120;
	else if (CHIP_REV_IS_EMUL(bp))
		factor = 200;
	else
		factor = 1;

	/* Disable inputs of parser neighbor blocks */
	REG_WR(bp, TSDM_REG_ENABLE_IN1, 0x0);
	REG_WR(bp, TCM_REG_PRS_IFEN, 0x0);
	REG_WR(bp, CFC_REG_DEBUG0, 0x1);
	REG_WR(bp, NIG_REG_PRS_REQ_IN_EN, 0x0);

	/*  Write 0 to parser credits for CFC search request */
	REG_WR(bp, PRS_REG_CFC_SEARCH_INITIAL_CREDIT, 0x0);

	/* send Ethernet packet */
	bnx2x_lb_pckt(bp);

	/* TODO do i reset NIG statistic? */
	/* Wait until NIG register shows 1 packet of size 0x10 */
	count = 1000 * factor;
	while (count) {

		bnx2x_read_dmae(bp, NIG_REG_STAT2_BRB_OCTET, 2);
		val = *bnx2x_sp(bp, wb_data[0]);
		if (val == 0x10)
			break;

		usleep_range(10000, 20000);
		count--;
	}
	if (val != 0x10) {
		BNX2X_ERR("NIG timeout  val = 0x%x\n", val);
		return -1;
	}

	/* Wait until PRS register shows 1 packet */
	count = 1000 * factor;
	while (count) {
		val = REG_RD(bp, PRS_REG_NUM_OF_PACKETS);
		if (val == 1)
			break;

		usleep_range(10000, 20000);
		count--;
	}
	if (val != 0x1) {
		BNX2X_ERR("PRS timeout val = 0x%x\n", val);
		return -2;
	}

	/* Reset and init BRB, PRS */
	REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR, 0x03);
	msleep(50);
	REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, 0x03);
	msleep(50);
	bnx2x_init_block(bp, BLOCK_BRB1, PHASE_COMMON);
	bnx2x_init_block(bp, BLOCK_PRS, PHASE_COMMON);

	DP(NETIF_MSG_HW, "part2\n");

	/* Disable inputs of parser neighbor blocks */
	REG_WR(bp, TSDM_REG_ENABLE_IN1, 0x0);
	REG_WR(bp, TCM_REG_PRS_IFEN, 0x0);
	REG_WR(bp, CFC_REG_DEBUG0, 0x1);
	REG_WR(bp, NIG_REG_PRS_REQ_IN_EN, 0x0);

	/* Write 0 to parser credits for CFC search request */
	REG_WR(bp, PRS_REG_CFC_SEARCH_INITIAL_CREDIT, 0x0);

	/* send 10 Ethernet packets */
	for (i = 0; i < 10; i++)
		bnx2x_lb_pckt(bp);

	/* Wait until NIG register shows 10 + 1
	   packets of size 11*0x10 = 0xb0 */
	count = 1000 * factor;
	while (count) {

		bnx2x_read_dmae(bp, NIG_REG_STAT2_BRB_OCTET, 2);
		val = *bnx2x_sp(bp, wb_data[0]);
		if (val == 0xb0)
			break;

		usleep_range(10000, 20000);
		count--;
	}
	if (val != 0xb0) {
		BNX2X_ERR("NIG timeout  val = 0x%x\n", val);
		return -3;
	}

	/* Wait until PRS register shows 2 packets */
	val = REG_RD(bp, PRS_REG_NUM_OF_PACKETS);
	if (val != 2)
		BNX2X_ERR("PRS timeout  val = 0x%x\n", val);

	/* Write 1 to parser credits for CFC search request */
	REG_WR(bp, PRS_REG_CFC_SEARCH_INITIAL_CREDIT, 0x1);

	/* Wait until PRS register shows 3 packets */
	msleep(10 * factor);
	/* Wait until NIG register shows 1 packet of size 0x10 */
	val = REG_RD(bp, PRS_REG_NUM_OF_PACKETS);
	if (val != 3)
		BNX2X_ERR("PRS timeout  val = 0x%x\n", val);

	/* clear NIG EOP FIFO */
	for (i = 0; i < 11; i++)
		REG_RD(bp, NIG_REG_INGRESS_EOP_LB_FIFO);
	val = REG_RD(bp, NIG_REG_INGRESS_EOP_LB_EMPTY);
	if (val != 1) {
		BNX2X_ERR("clear of NIG failed\n");
		return -4;
	}

	/* Reset and init BRB, PRS, NIG */
	REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR, 0x03);
	msleep(50);
	REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, 0x03);
	msleep(50);
	bnx2x_init_block(bp, BLOCK_BRB1, PHASE_COMMON);
	bnx2x_init_block(bp, BLOCK_PRS, PHASE_COMMON);
	if (!CNIC_SUPPORT(bp))
		/* set NIC mode */
		REG_WR(bp, PRS_REG_NIC_MODE, 1);

	/* Enable inputs of parser neighbor blocks */
	REG_WR(bp, TSDM_REG_ENABLE_IN1, 0x7fffffff);
	REG_WR(bp, TCM_REG_PRS_IFEN, 0x1);
	REG_WR(bp, CFC_REG_DEBUG0, 0x0);
	REG_WR(bp, NIG_REG_PRS_REQ_IN_EN, 0x1);

	DP(NETIF_MSG_HW, "done\n");

	return 0; /* OK */
}


int bnx2x_flr_clnup_poll_hw_counter(struct bnx2x *bp, u32 reg,
				    char *msg, u32 poll_cnt)
{
	u32 val = bnx2x_flr_clnup_reg_poll(bp, reg, 0, poll_cnt);
	if (val != 0) {
		BNX2X_ERR("%s usage count=%d\n", msg, val);
		return 1;
	}
	return 0;
}


static u32 bnx2x_flr_clnup_reg_poll(struct bnx2x *bp, u32 reg,
				    u32 expected, u32 poll_count)
{
	u32 cur_cnt = poll_count;
	u32 val;

	while ((val = REG_RD(bp, reg)) != expected && cur_cnt--)
		udelay(FLR_WAIT_INTERVAL);

	return val;
}


static void bnx2x_timer(struct timer_list *t)
{
	struct bnx2x *bp = from_timer(bp, t, timer);

	if (!netif_running(bp->dev))
		return;

	if (IS_PF(bp) &&
	    !BP_NOMCP(bp)) {
		int mb_idx = BP_FW_MB_IDX(bp);
		u16 drv_pulse;
		u16 mcp_pulse;

		++bp->fw_drv_pulse_wr_seq;
		bp->fw_drv_pulse_wr_seq &= DRV_PULSE_SEQ_MASK;
		drv_pulse = bp->fw_drv_pulse_wr_seq;
		bnx2x_drv_pulse(bp);

		mcp_pulse = (SHMEM_RD(bp, func_mb[mb_idx].mcp_pulse_mb) &
			     MCP_PULSE_SEQ_MASK);
		/* The delta between driver pulse and mcp response
		 * should not get too big. If the MFW is more than 5 pulses
		 * behind, we should worry about it enough to generate an error
		 * log.
		 */
		if (((drv_pulse - mcp_pulse) & MCP_PULSE_SEQ_MASK) > 5)
			BNX2X_ERR("MFW seems hanged: drv_pulse (0x%x) != mcp_pulse (0x%x)\n",
				  drv_pulse, mcp_pulse);
	}

	if (bp->state == BNX2X_STATE_OPEN)
		bnx2x_stats_handle(bp, STATS_EVENT_UPDATE);

	/* sample pf vf bulletin board for new posts from pf */
	if (IS_VF(bp))
		bnx2x_timer_sriov(bp);

	mod_timer(&bp->timer, jiffies + bp->current_interval);
}


static void validate_set_si_mode(struct bnx2x *bp)
{
	u8 func = BP_ABS_FUNC(bp);
	u32 val;

	val = MF_CFG_RD(bp, func_mf_config[func].mac_upper);

	/* check for legal mac (upper bytes) */
	if (val != 0xffff) {
		bp->mf_mode = MULTI_FUNCTION_SI;
		bp->mf_config[BP_VN(bp)] =
			MF_CFG_RD(bp, func_mf_config[func].config);
	} else
		BNX2X_DEV_INFO("illegal MAC address for SI\n");
}


void bnx2x_disable_close_the_gate(struct bnx2x *bp)
{
	u32 val;

	DP(NETIF_MSG_IFDOWN, "Disabling \"close the gates\"\n");

	if (CHIP_IS_E1(bp)) {
		int port = BP_PORT(bp);
		u32 addr = port ? MISC_REG_AEU_MASK_ATTN_FUNC_1 :
			MISC_REG_AEU_MASK_ATTN_FUNC_0;

		val = REG_RD(bp, addr);
		val &= ~(0x300);
		REG_WR(bp, addr, val);
	} else {
		val = REG_RD(bp, MISC_REG_AEU_GENERAL_MASK);
		val &= ~(MISC_AEU_GENERAL_MASK_REG_AEU_PXP_CLOSE_MASK |
			 MISC_AEU_GENERAL_MASK_REG_AEU_NIG_CLOSE_MASK);
		REG_WR(bp, MISC_REG_AEU_GENERAL_MASK, val);
	}
}


static int bnx2x_poll_hw_usage_counters(struct bnx2x *bp, u32 poll_cnt)
{
	/* wait for CFC PF usage-counter to zero (includes all the VFs) */
	if (bnx2x_flr_clnup_poll_hw_counter(bp,
			CFC_REG_NUM_LCIDS_INSIDE_PF,
			"CFC PF usage counter timed out",
			poll_cnt))
		return 1;

	/* Wait for DQ PF usage-counter to zero (until DQ cleanup) */
	if (bnx2x_flr_clnup_poll_hw_counter(bp,
			DORQ_REG_PF_USAGE_CNT,
			"DQ PF usage counter timed out",
			poll_cnt))
		return 1;

	/* Wait for QM PF usage-counter to zero (until DQ cleanup) */
	if (bnx2x_flr_clnup_poll_hw_counter(bp,
			QM_REG_PF_USG_CNT_0 + 4*BP_FUNC(bp),
			"QM PF usage counter timed out",
			poll_cnt))
		return 1;

	/* Wait for Timer PF usage-counters to zero (until DQ cleanup) */
	if (bnx2x_flr_clnup_poll_hw_counter(bp,
			TM_REG_LIN0_VNIC_UC + 4*BP_PORT(bp),
			"Timers VNIC usage counter timed out",
			poll_cnt))
		return 1;
	if (bnx2x_flr_clnup_poll_hw_counter(bp,
			TM_REG_LIN0_NUM_SCANS + 4*BP_PORT(bp),
			"Timers NUM_SCANS usage counter timed out",
			poll_cnt))
		return 1;

	/* Wait DMAE PF usage counter to zero */
	if (bnx2x_flr_clnup_poll_hw_counter(bp,
			dmae_reg_go_c[INIT_DMAE_C(bp)],
			"DMAE command register timed out",
			poll_cnt))
		return 1;

	return 0;
}


bool bnx2x_chk_parity_attn(struct bnx2x *bp, bool *global, bool print)
{
	struct attn_route attn = { {0} };
	int port = BP_PORT(bp);

	attn.sig[0] = REG_RD(bp,
		MISC_REG_AEU_AFTER_INVERT_1_FUNC_0 +
			     port*4);
	attn.sig[1] = REG_RD(bp,
		MISC_REG_AEU_AFTER_INVERT_2_FUNC_0 +
			     port*4);
	attn.sig[2] = REG_RD(bp,
		MISC_REG_AEU_AFTER_INVERT_3_FUNC_0 +
			     port*4);
	attn.sig[3] = REG_RD(bp,
		MISC_REG_AEU_AFTER_INVERT_4_FUNC_0 +
			     port*4);
	/* Since MCP attentions can't be disabled inside the block, we need to
	 * read AEU registers to see whether they're currently disabled
	 */
	attn.sig[3] &= ((REG_RD(bp,
				!port ? MISC_REG_AEU_ENABLE4_FUNC_0_OUT_0
				      : MISC_REG_AEU_ENABLE4_FUNC_1_OUT_0) &
			 MISC_AEU_ENABLE_MCP_PRTY_BITS) |
			~MISC_AEU_ENABLE_MCP_PRTY_BITS);

	if (!CHIP_IS_E1x(bp))
		attn.sig[4] = REG_RD(bp,
			MISC_REG_AEU_AFTER_INVERT_5_FUNC_0 +
				     port*4);

	return bnx2x_parity_attn(bp, global, print, attn.sig);
}