<commit_before>/* gphoto2-filesys.c
 *
 * Copyright  2000 <NAME>
 *
 * Contributions:
 *      <NAME> <<EMAIL>> (2001)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include "config.h"
#include "gphoto2-filesys.h"

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>

#include "gphoto2-result.h"
#include "gphoto2-file.h"
#include "gphoto2-port-log.h"
#include "gphoto2-setting.h"

#include <limits.h>

#ifdef HAVE_LIBEXIF
#  include <libexif/exif-data.h>
#endif

#ifdef ENABLE_NLS
#  include <libintl.h>
#  undef _
#  define _(String) dgettext (GETTEXT_PACKAGE, String)
#  ifdef gettext_noop
#    define N_(String) gettext_noop (String)
#  else
#    define N_(String) (String)
#  endif
#else
#  define textdomain(String) (String)
#  define gettext(String) (String)
#  define dgettext(Domain,Message) (Message)
#  define dcgettext(Domain,Message,Type) (Message)
#  define bindtextdomain(Domain,Directory) (Domain)
#  define _(String) (String)
#  define N_(String) (String)
#endif

#define GP_MODULE "libgphoto2"

typedef struct _CameraFilesystemFile {
	char name [128];
	int info_dirty;
	CameraFileInfo info;
	struct _CameraFilesystemFile *lru_prev;
	struct _CameraFilesystemFile *lru_next;
	CameraFile *preview;
	CameraFile *normal;
	CameraFile *raw;
	CameraFile *audio;
	CameraFile *exif;
} CameraFilesystemFile;

typedef struct {
	int count;
	char name [128];
	int files_dirty;
	int folders_dirty;
	CameraFilesystemFile *file;
} CameraFilesystemFolder;

#define PICTURES_TO_KEEP	2
static int pictures_to_keep = -1;

#ifdef HAVE_LIBEXIF

static int gp_filesystem_get_file_impl (CameraFilesystem *, const char *,
		const char *, CameraFileType, CameraFile *, GPContext *);

static time_t
get_time_from_exif_tag(ExifEntry *e) {
	struct tm ts;

        e->data[4] = e->data[ 7] = e->data[10] = e->data[13] = e->data[16] = 0;
        ts.tm_year = atoi (e->data) - 1900;
        ts.tm_mon  = atoi (e->data + 5) - 1;
        ts.tm_mday = atoi (e->data + 8);
        ts.tm_hour = atoi (e->data + 11);
        ts.tm_min  = atoi (e->data + 14);
        ts.tm_sec  = atoi (e->data + 17);

        return mktime (&ts);
}

static time_t
get_exif_mtime (const unsigned char *data, unsigned long size)
{
	ExifData *ed;
	ExifEntry *e;
	time_t t, t1 = 0, t2 = 0, t3 = 0;

        ed = exif_data_new_from_data (data, size);
        if (!ed) {
                GP_DEBUG ("Could not parse data for EXIF information.");
                return 0;
        }

        /*
         * HP PhotoSmart C30 has the date and time in ifd_exif.
         */
#ifdef HAVE_LIBEXIF_IFD
	e = exif_content_get_entry (ed->ifd[EXIF_IFD_0], EXIF_TAG_DATE_TIME);
	if (e)
		t1 = get_time_from_exif_tag(e);
	e = exif_content_get_entry (ed->ifd[EXIF_IFD_EXIF],
				    EXIF_TAG_DATE_TIME_ORIGINAL);
	if (e)
		t2 = get_time_from_exif_tag(e);
	e = exif_content_get_entry (ed->ifd[EXIF_IFD_EXIF],
				    EXIF_TAG_DATE_TIME_DIGITIZED);
	if (e)
		t3 = get_time_from_exif_tag(e);
#else
	e = exif_content_get_entry (ed->ifd0, EXIF_TAG_DATE_TIME);
	if (e) {
		t1 = get_time_from_exif_tag(e);
		exif_data_unref (e);
	}
	e = exif_content_get_entry (ed->ifd_exif,
				    EXIF_TAG_DATE_TIME_ORIGINAL);
	if (e) {
		t2 = get_time_from_exif_tag(e);
		exif_data_unref (e);
	}
	e = exif_content_get_entry (ed->ifd_exif, 
				    EXIF_TAG_DATE_TIME_DIGITIZED);
	if (e) {
		t3 = get_time_from_exif_tag(e);
		exif_data_unref (e);
	}
#endif
        exif_data_unref (ed);
        if (!t1 && !t2 && !t3) {
                GP_DEBUG ("EXIF data has not date/time tags.");
                return 0;
        }

	/* Perform some sanity checking on those tags */
	t = t1; /* "last modified" */

	if (t2 > t)	/* "image taken" > "last modified" ? can not be */
		t = t2;
	if (t3 > t)	/* "image digitized" > max(last two) ? can not be */
		t = t3;

        GP_DEBUG ("Found time in EXIF data: '%s'.", asctime (localtime (&t)));
        return (t);
}

static time_t
gp_filesystem_get_exif_mtime (CameraFilesystem *fs, const char *folder,
                              const char *filename)
{
        CameraFile *file;
        const char *data = NULL;
        unsigned long int size = 0;
        time_t t;

        if (!fs)
                return 0;

        gp_file_new (&file);
        if (gp_filesystem_get_file (fs, folder, filename,
				GP_FILE_TYPE_EXIF, file, NULL) != GP_OK) {
                GP_DEBUG ("Could not get EXIF data of '%s' in folder '%s'.",
                          filename, folder);
                gp_file_unref (file);
                return 0;
        }

        gp_file_get_data_and_size (file, &data, &size);
        t = get_exif_mtime (data, size);
        gp_file_unref (file);

        return (t);
}
#endif

/**
 * CameraFilesystem:
 *
 * The internals of the #CameraFilesystem are only visible to gphoto2. You
 * can only access them using the functions provided by gphoto2.
 **/
struct _CameraFilesystem {
	int count;
	CameraFilesystemFolder *folder;

	CameraFilesystemFile *lru_first;
	CameraFilesystemFile *lru_last;
	unsigned long int lru_size;

	CameraFilesystemGetInfoFunc get_info_func;
	CameraFilesystemSetInfoFunc set_info_func;
	void *info_data;

	CameraFilesystemListFunc file_list_func;
	CameraFilesystemListFunc folder_list_func;
	void *list_data;

	CameraFilesystemGetFileFunc get_file_func;
	CameraFilesystemDeleteFileFunc delete_file_func;
	void *file_data;

	CameraFilesystemPutFileFunc put_file_func;
	CameraFilesystemDeleteAllFunc delete_all_func;
	CameraFilesystemDirFunc make_dir_func;
	CameraFilesystemDirFunc remove_dir_func;
	void *folder_data;
};

#undef  MIN
#define MIN(a, b)  (((a) < (b)) ? (a) : (b))

#define CHECK_NULL(r)        {if (!(r)) return (GP_ERROR_BAD_PARAMETERS);}
#define CR(result)           {int r = (result); if (r < 0) return (r);}
#define CHECK_MEM(m)         {if (!(m)) return (GP_ERROR_NO_MEMORY);}

#define CL(result,list)			\
{					\
	int r = (result);		\
					\
	 if (r < 0) {			\
		gp_list_free (list);	\
		return (r);		\
	}				\
}

#define CU(result,file)			\
{					\
	int r = (result);		\
					\
	if (r < 0) {			\
		gp_file_unref (file);	\
		return (r);		\
	}				\
}

#define CC(context)							\
{									\
	if (gp_context_cancel (context) == GP_CONTEXT_FEEDBACK_CANCEL)	\
		return GP_ERROR_CANCEL;					\
}

#define CA(f,c)							\
{									\
	if ((f)[0] != '/') {						\
		gp_context_error ((c),					\
			_("The path '%s' is not absolute."), (f));	\
		return (GP_ERROR_PATH_NOT_ABSOLUTE);			\
	}								\
}

/** check for buffer overflow */
#define CBO(bufsize, string_len, msg) \
	if (bufsize <= string_len) { \
		GP_DEBUG ("%s: strlen(...) = %d " \
			">= sizeof(buffer) = %d", \
			msg, (int)string_len, (int)bufsize \
		); \
		gp_context_error (context, "preventing buffer overflow"); \
		return GP_ERROR; \
	}

static int
delete_all_files (CameraFilesystem *fs, int x)
{
        int y;

        CHECK_NULL (fs);

        if (fs->folder[x].count) {

		/* Get rid of cached files */
                for (y = 0; y < fs->folder[x].count; y++) {
			if (fs->folder[x].file[y].preview) {
	                        gp_file_unref (fs->folder[x].file[y].preview);
				fs->folder[x].file[y].preview = NULL;
			}
			if (fs->folder[x].file[y].normal) {
				gp_file_unref (fs->folder[x].file[y].normal);
				fs->folder[x].file[y].normal = NULL;
			}
			if (fs->folder[x].file[y].raw) {
				gp_file_unref (fs->folder[x].file[y].raw);
				fs->folder[x].file[y].raw = NULL;
			}
			if (fs->folder[x].file[y].audio) {
				gp_file_unref (fs->folder[x].file[y].audio);
				fs->folder[x].file[y].audio = NULL;
			}
			if (fs->folder[x].file[y].exif) {
				gp_file_unref (fs->folder[x].file[y].exif);
				fs->folder[x].file[y].exif = NULL;
			}
		}

                free (fs->folder[x].file);
                fs->folder[x].file = NULL;
                fs->folder[x].count = 0;
        }

        return (GP_OK);
}

static int
delete_folder (CameraFilesystem *fs, int x)
{
        CameraFilesystemFolder *new_fop;

        CHECK_NULL (fs);

        delete_all_files (fs, x);

        /* Move all folders behind one position up */
        if (x < fs->count - 1)
                memmove (&fs->folder[x], &fs->folder[x + 1],
                         sizeof (CameraFilesystemFolder) * (fs->count - x - 1));
        fs->count--;

        /* Get rid of the last one */
        new_fop = realloc (fs->folder,
                           sizeof (CameraFilesystemFolder) * (fs->count));
        if (!fs->count || (fs->count && new_fop))
                fs->folder = new_fop;

        return (GP_OK);
}

static int
delete_all_folders (CameraFilesystem *fs, const char *folder,
		    GPContext *context)
{
        int x;

	gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", "Internally deleting "
		"all folders from '%s'...", folder);

        CHECK_NULL (fs && folder);
	CC (context);
        CA (folder, context);

        for (x = 0; x < fs->count; x++)
                if (!strncmp (fs->folder[x].name, folder, strlen (folder))) {

                        /*
                         * Is this really a subfolder (and not the folder
                         * itself)?
                         */
                        if (strlen (fs->folder[x].name) <= strlen (folder))
                                continue;

                        CR (delete_all_files (fs, x));
                        CR (delete_folder (fs, x));
                        x--;
                }

        return (GP_OK);
}

static int
append_folder (CameraFilesystem *fs, const char *folder, GPContext *context)
{
        CameraFilesystemFolder *new;
        int x;
        char *buf = NULL;

	gp_log (GP_LOG_DEBUG, "gphoto2-filesystem",
		"Internally appending folder %s...", folder);

	CHECK_NULL (fs);
	CHECK_NULL (folder);	
	CC (context);
	CA (folder, context);

        /* Make sure the directory doesn't exist */
	for (x = 0; x < fs->count; x++)
		if (!strncmp (fs->folder[x].name, folder, strlen (folder)) &&
		    (strlen (fs->folder[x].name) == strlen (folder)))
			break;
	if (x < fs->count) {
		gp_context_error (context, _("Could not append folder '%s' "
			"as this folder already exists."), folder);
                return (GP_ERROR_DIRECTORY_EXISTS);
        }

        /* Make sure the parent exist. If not, create it. */
	buf = strdup (folder);
	CHECK_NULL (buf);
	/* Do not forget to free buf before returning in the normal case.
	 * In case of error abortions, it probably will not matter much, though.
	 */
        for (x = strlen (buf) - 1; x >= 0; x--)
                if (buf[x] == '/')
                        break;
        if (x > 0) {
                buf[x] = '\0';
		for (x = 0; x < fs->count; x++)
			if (!strncmp (fs->folder[x].name, buf, strlen (buf)))
				break;
		if (x == fs->count)
			CR (append_folder (fs, buf, context))
        }

        /* Allocate the folder pointer and the actual folder */
        if (fs->count)
                CHECK_MEM (new = realloc (fs->folder,<commit_msg>replaced one strncmp() / strlen() combo by equivalent strcmp(). replaced one bad strncmp() by strcmp() ( think folders like "AA" "AAA" "AAAA" )<commit_after>/* gphoto2-filesys.c
 *
 * Copyright  2000 <NAME>
 *
 * Contributions:
 *      <NAME> <<EMAIL>> (2001)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include "config.h"
#include "gphoto2-filesys.h"

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>

#include "gphoto2-result.h"
#include "gphoto2-file.h"
#include "gphoto2-port-log.h"
#include "gphoto2-setting.h"

#include <limits.h>

#ifdef HAVE_LIBEXIF
#  include <libexif/exif-data.h>
#endif

#ifdef ENABLE_NLS
#  include <libintl.h>
#  undef _
#  define _(String) dgettext (GETTEXT_PACKAGE, String)
#  ifdef gettext_noop
#    define N_(String) gettext_noop (String)
#  else
#    define N_(String) (String)
#  endif
#else
#  define textdomain(String) (String)
#  define gettext(String) (String)
#  define dgettext(Domain,Message) (Message)
#  define dcgettext(Domain,Message,Type) (Message)
#  define bindtextdomain(Domain,Directory) (Domain)
#  define _(String) (String)
#  define N_(String) (String)
#endif

#define GP_MODULE "libgphoto2"

typedef struct _CameraFilesystemFile {
	char name [128];
	int info_dirty;
	CameraFileInfo info;
	struct _CameraFilesystemFile *lru_prev;
	struct _CameraFilesystemFile *lru_next;
	CameraFile *preview;
	CameraFile *normal;
	CameraFile *raw;
	CameraFile *audio;
	CameraFile *exif;
} CameraFilesystemFile;

typedef struct {
	int count;
	char name [128];
	int files_dirty;
	int folders_dirty;
	CameraFilesystemFile *file;
} CameraFilesystemFolder;

#define PICTURES_TO_KEEP	2
static int pictures_to_keep = -1;

#ifdef HAVE_LIBEXIF

static int gp_filesystem_get_file_impl (CameraFilesystem *, const char *,
		const char *, CameraFileType, CameraFile *, GPContext *);

static time_t
get_time_from_exif_tag(ExifEntry *e) {
	struct tm ts;

        e->data[4] = e->data[ 7] = e->data[10] = e->data[13] = e->data[16] = 0;
        ts.tm_year = atoi (e->data) - 1900;
        ts.tm_mon  = atoi (e->data + 5) - 1;
        ts.tm_mday = atoi (e->data + 8);
        ts.tm_hour = atoi (e->data + 11);
        ts.tm_min  = atoi (e->data + 14);
        ts.tm_sec  = atoi (e->data + 17);

        return mktime (&ts);
}

static time_t
get_exif_mtime (const unsigned char *data, unsigned long size)
{
	ExifData *ed;
	ExifEntry *e;
	time_t t, t1 = 0, t2 = 0, t3 = 0;

        ed = exif_data_new_from_data (data, size);
        if (!ed) {
                GP_DEBUG ("Could not parse data for EXIF information.");
                return 0;
        }

        /*
         * HP PhotoSmart C30 has the date and time in ifd_exif.
         */
#ifdef HAVE_LIBEXIF_IFD
	e = exif_content_get_entry (ed->ifd[EXIF_IFD_0], EXIF_TAG_DATE_TIME);
	if (e)
		t1 = get_time_from_exif_tag(e);
	e = exif_content_get_entry (ed->ifd[EXIF_IFD_EXIF],
				    EXIF_TAG_DATE_TIME_ORIGINAL);
	if (e)
		t2 = get_time_from_exif_tag(e);
	e = exif_content_get_entry (ed->ifd[EXIF_IFD_EXIF],
				    EXIF_TAG_DATE_TIME_DIGITIZED);
	if (e)
		t3 = get_time_from_exif_tag(e);
#else
	e = exif_content_get_entry (ed->ifd0, EXIF_TAG_DATE_TIME);
	if (e) {
		t1 = get_time_from_exif_tag(e);
		exif_data_unref (e);
	}
	e = exif_content_get_entry (ed->ifd_exif,
				    EXIF_TAG_DATE_TIME_ORIGINAL);
	if (e) {
		t2 = get_time_from_exif_tag(e);
		exif_data_unref (e);
	}
	e = exif_content_get_entry (ed->ifd_exif, 
				    EXIF_TAG_DATE_TIME_DIGITIZED);
	if (e) {
		t3 = get_time_from_exif_tag(e);
		exif_data_unref (e);
	}
#endif
        exif_data_unref (ed);
        if (!t1 && !t2 && !t3) {
                GP_DEBUG ("EXIF data has not date/time tags.");
                return 0;
        }

	/* Perform some sanity checking on those tags */
	t = t1; /* "last modified" */

	if (t2 > t)	/* "image taken" > "last modified" ? can not be */
		t = t2;
	if (t3 > t)	/* "image digitized" > max(last two) ? can not be */
		t = t3;

        GP_DEBUG ("Found time in EXIF data: '%s'.", asctime (localtime (&t)));
        return (t);
}

static time_t
gp_filesystem_get_exif_mtime (CameraFilesystem *fs, const char *folder,
                              const char *filename)
{
        CameraFile *file;
        const char *data = NULL;
        unsigned long int size = 0;
        time_t t;

        if (!fs)
                return 0;

        gp_file_new (&file);
        if (gp_filesystem_get_file (fs, folder, filename,
				GP_FILE_TYPE_EXIF, file, NULL) != GP_OK) {
                GP_DEBUG ("Could not get EXIF data of '%s' in folder '%s'.",
                          filename, folder);
                gp_file_unref (file);
                return 0;
        }

        gp_file_get_data_and_size (file, &data, &size);
        t = get_exif_mtime (data, size);
        gp_file_unref (file);

        return (t);
}
#endif

/**
 * CameraFilesystem:
 *
 * The internals of the #CameraFilesystem are only visible to gphoto2. You
 * can only access them using the functions provided by gphoto2.
 **/
struct _CameraFilesystem {
	int count;
	CameraFilesystemFolder *folder;

	CameraFilesystemFile *lru_first;
	CameraFilesystemFile *lru_last;
	unsigned long int lru_size;

	CameraFilesystemGetInfoFunc get_info_func;
	CameraFilesystemSetInfoFunc set_info_func;
	void *info_data;

	CameraFilesystemListFunc file_list_func;
	CameraFilesystemListFunc folder_list_func;
	void *list_data;

	CameraFilesystemGetFileFunc get_file_func;
	CameraFilesystemDeleteFileFunc delete_file_func;
	void *file_data;

	CameraFilesystemPutFileFunc put_file_func;
	CameraFilesystemDeleteAllFunc delete_all_func;
	CameraFilesystemDirFunc make_dir_func;
	CameraFilesystemDirFunc remove_dir_func;
	void *folder_data;
};

#undef  MIN
#define MIN(a, b)  (((a) < (b)) ? (a) : (b))

#define CHECK_NULL(r)        {if (!(r)) return (GP_ERROR_BAD_PARAMETERS);}
#define CR(result)           {int r = (result); if (r < 0) return (r);}
#define CHECK_MEM(m)         {if (!(m)) return (GP_ERROR_NO_MEMORY);}

#define CL(result,list)			\
{					\
	int r = (result);		\
					\
	 if (r < 0) {			\
		gp_list_free (list);	\
		return (r);		\
	}				\
}

#define CU(result,file)			\
{					\
	int r = (result);		\
					\
	if (r < 0) {			\
		gp_file_unref (file);	\
		return (r);		\
	}				\
}

#define CC(context)							\
{									\
	if (gp_context_cancel (context) == GP_CONTEXT_FEEDBACK_CANCEL)	\
		return GP_ERROR_CANCEL;					\
}

#define CA(f,c)							\
{									\
	if ((f)[0] != '/') {						\
		gp_context_error ((c),					\
			_("The path '%s' is not absolute."), (f));	\
		return (GP_ERROR_PATH_NOT_ABSOLUTE);			\
	}								\
}

/** check for buffer overflow */
#define CBO(bufsize, string_len, msg) \
	if (bufsize <= string_len) { \
		GP_DEBUG ("%s: strlen(...) = %d " \
			">= sizeof(buffer) = %d", \
			msg, (int)string_len, (int)bufsize \
		); \
		gp_context_error (context, "preventing buffer overflow"); \
		return GP_ERROR; \
	}

static int
delete_all_files (CameraFilesystem *fs, int x)
{
        int y;

        CHECK_NULL (fs);

        if (fs->folder[x].count) {

		/* Get rid of cached files */
                for (y = 0; y < fs->folder[x].count; y++) {
			if (fs->folder[x].file[y].preview) {
	                        gp_file_unref (fs->folder[x].file[y].preview);
				fs->folder[x].file[y].preview = NULL;
			}
			if (fs->folder[x].file[y].normal) {
				gp_file_unref (fs->folder[x].file[y].normal);
				fs->folder[x].file[y].normal = NULL;
			}
			if (fs->folder[x].file[y].raw) {
				gp_file_unref (fs->folder[x].file[y].raw);
				fs->folder[x].file[y].raw = NULL;
			}
			if (fs->folder[x].file[y].audio) {
				gp_file_unref (fs->folder[x].file[y].audio);
				fs->folder[x].file[y].audio = NULL;
			}
			if (fs->folder[x].file[y].exif) {
				gp_file_unref (fs->folder[x].file[y].exif);
				fs->folder[x].file[y].exif = NULL;
			}
		}

                free (fs->folder[x].file);
                fs->folder[x].file = NULL;
                fs->folder[x].count = 0;
        }

        return (GP_OK);
}

static int
delete_folder (CameraFilesystem *fs, int x)
{
        CameraFilesystemFolder *new_fop;

        CHECK_NULL (fs);

        delete_all_files (fs, x);

        /* Move all folders behind one position up */
        if (x < fs->count - 1)
                memmove (&fs->folder[x], &fs->folder[x + 1],
                         sizeof (CameraFilesystemFolder) * (fs->count - x - 1));
        fs->count--;

        /* Get rid of the last one */
        new_fop = realloc (fs->folder,
                           sizeof (CameraFilesystemFolder) * (fs->count));
        if (!fs->count || (fs->count && new_fop))
                fs->folder = new_fop;

        return (GP_OK);
}

static int
delete_all_folders (CameraFilesystem *fs, const char *folder,
		    GPContext *context)
{
        int x;

	gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", "Internally deleting "
		"all folders from '%s'...", folder);

        CHECK_NULL (fs && folder);
	CC (context);
        CA (folder, context);

        for (x = 0; x < fs->count; x++)
                if (!strncmp (fs->folder[x].name, folder, strlen (folder))) {

                        /*
                         * Is this really a subfolder (and not the folder
                         * itself)?
                         */
                        if (strlen (fs->folder[x].name) <= strlen (folder))
                                continue;

                        CR (delete_all_files (fs, x));
                        CR (delete_folder (fs, x));
                        x--;
                }

        return (GP_OK);
}

static int
append_folder (CameraFilesystem *fs, const char *folder, GPContext *context)
{
        CameraFilesystemFolder *new;
        int x;
        char *buf = NULL;

	gp_log (GP_LOG_DEBUG, "gphoto2-filesystem",
		"Internally appending folder %s...", folder);

	CHECK_NULL (fs);
	CHECK_NULL (folder);	
	CC (context);
	CA (folder, context);

        /* Make sure the directory doesn't exist */
	for (x = 0; x < fs->count; x++)
		if (!strcmp (fs->folder[x].name, folder))
			break;
	if (x < fs->count) {
		gp_context_error (context, _("Could not append folder '%s' "
			"as this folder already exists."), folder);
                return (GP_ERROR_DIRECTORY_EXISTS);
        }

        /* Make sure the parent exist. If not, create it. */
	buf = strdup (folder);
	CHECK_NULL (buf);
	/* Do not forget to free buf before returning in the normal case.
	 * In case of error abortions, it probably will not matter much, though.
	 */
        for (x = strlen (buf) - 1; x >= 0; x--)
                if (buf[x] == '/')
                        break;
        if (x > 0) {
                buf[x] = '\0';
		for (x = 0; x < fs->count; x++)
			if (!strcmp (fs->folder[x].name, buf))
				break;
		if (x == fs->count)
			CR (append_folder (fs, buf, context))
        }

        /* Allocate the folder pointer and the actual folder */
        if (fs->count)
                CHECK_MEM (new = realloc (fs->folder,
{'fraction_non_alphanumeric': 0.13403037735037218, 'fraction_numerical': 0.009379975044103093, 'mean_word_length': 2.496615014292162, 'pattern_counts': {'":': 0, '<': 49, '<?xml version=': 0, '>': 156, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5602239', 'n_tokens_mistral': 8878, 'n_tokens_neox': 8190, 'n_words': 2127}