/*
 *  $Id: gwyzip.h 29403 2026-01-29 17:37:40Z yeti-dn $
 *  Copyright (C) 2015-2024 David Necas (Yeti).
 *  E-mail: yeti@gwyddion.net.
 *
 *  This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public
 *  License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any
 *  later version.
 *
 *  This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
 *  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 *  details.
 *
 *  You should have received a copy of the GNU General Public License along with this program; if not, write to the
 *  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
#ifndef __GWY_FILE_ZIP_H__
#define __GWY_FILE_ZIP_H__

#include "config.h"
#include <errno.h>
#include <stdio.h>
#include <glib.h>
#include <glib/gstdio.h>
#include "err.h"

/* You can include this header even if no ZIP library is available and then check if HAVE_GWYZIP is defined.  When it
 * is defined then you actually have GwyZip. */
#undef HAVE_GWYZIP

struct _GwyZipFile;

typedef struct _GwyZipFile *GwyZipFile;

#if defined(HAVE_MINIZIP1) || defined(HAVE_MINIZIP2) || defined(HAVE_MINIZIPNG) || defined(HAVE_LIBZIP) || defined(HAVE_ZZIP)
#define HAVE_GWYZIP 1
/* This is the interface we implement.
 *
 * There is also an optional gwyzip_open_memory(), which some backens implement.
 *
 * There can be other symbols starting with gwyzip_ defined, they are reserved. */
static GwyZipFile gwyzip_open                (const gchar *path,
                                              GError **error);
static void       gwyzip_close               (GwyZipFile zipfile);
static gboolean   gwyzip_first_file          (GwyZipFile zipfile,
                                              GError **error);
static gboolean   gwyzip_next_file           (GwyZipFile zipfile,
                                              GError **error);
static gboolean   gwyzip_get_current_filename(GwyZipFile zipfile,
                                              gchar **filename,
                                              GError **error);
static gboolean   gwyzip_locate_file         (GwyZipFile zipfile,
                                              const gchar *filename,
                                              gint casesens,
                                              GError **error);
static guchar*    gwyzip_get_file_content    (GwyZipFile zipfile,
                                              gsize *contentsize, /* may be NULL */
                                              GError **error);
#endif

#ifdef HAVE_MINIZIP1
#include "gwyzip-minizip1.h"
#endif

#ifdef HAVE_MINIZIP2
#include "gwyzip-minizip2.h"
#endif

#ifdef HAVE_MINIZIPNG
#include "gwyzip-minizip-ng.h"
#endif

#ifdef HAVE_LIBZIP
#include "gwyzip-libzip.h"
#endif

#ifdef HAVE_ZZIP
#include "gwyzip-zzlib.h"
#endif

/* Backend-independent utilities.
 *
 * NB: The function may fill NULL to actualname if the library supports memory streams (or we create an anonymous
 * file). */
#ifdef HAVE_GWYZIP
G_GNUC_UNUSED
static GwyZipFile
gwyzip_make_temporary_archive(const guchar *buffer, gsize size,
                              G_GNUC_UNUSED const gchar *nametemplate,
                              GError **error)
{
#ifdef GWYZIP_HAS_MEMORY_FILES
    return gwyzip_open_memory(buffer, size, error);
#else
    GwyZipFile zipfile;
    GError *err = NULL;
    gssize bytes_written;
    gint fd;
    guint failcount = 0;
    gchar *actualname = NULL;

    fd = g_file_open_tmp(nametemplate, &actualname, &err);
    if (fd == -1) {
        err_OPEN_WRITE_GERROR(error, &err);
        return NULL;
    }
    gwy_debug("temporary ZIP file <%s>", actualname);

    while (size) {
        bytes_written = write(fd, buffer, size);
        if (bytes_written <= 0) {
            /* We might want to try again when we get zero written bytes or an error such as EAGAIN, EWOULDBLOCK or
             * EINTR.  But in this context, screw it. */
            if (++failcount > 5 || (errno != EAGAIN && errno != EINTR)) {
                err_WRITE(error);
                close(fd);
                goto fail;
            }
        }
        else
            failcount = 0;
        buffer += bytes_written;
        size -= bytes_written;
    }

    close(fd);
    if ((zipfile = gwyzip_open(actualname, error))) {
        /* Backends which do not have memory files must have this field. They must also clean up the file in their
         * gwyzip_close() function! */
        zipfile->tmpfilename = actualname;
        return zipfile;
    }

fail:
    g_unlink(actualname);
    g_free(actualname);
    return NULL;
#endif
}

G_GNUC_UNUSED
static void
gwyzip_debug_print_filenames(GwyZipFile zipfile)
{
    gchar *filename;
    GError *error = NULL;

    if (!gwyzip_first_file(zipfile, &error)) {
        gwy_debug("cannot rewind to the first file: %s", error->message);
        g_clear_error(&error);
        return;
    }
    do {
        if (!gwyzip_get_current_filename(zipfile, &filename, &error)) {
            gwy_debug("cannot get file name: %s", error->message);
            g_clear_error(&error);
            return;
        }
        gwy_debug("found file: <%s>", filename);
        g_free(filename);
    } while (gwyzip_next_file(zipfile, NULL));
}
#endif

#endif

/* vim: set cin columns=120 tw=118 et ts=4 sw=4 cino=>1s,e0,n0,f0,{0,}0,^0,\:1s,=0,g1s,h0,t0,+1s,c3,(0,u0 : */
