diff options
author | Marcus Meissner <marcus@jet.franken.de> | 2009-01-10 20:34:39 +0000 |
---|---|---|
committer | Marcus Meissner <marcus@jet.franken.de> | 2009-01-10 20:34:39 +0000 |
commit | e36d7e2a836785bd84a9d56bbeed53eed59c0954 (patch) | |
tree | 206883e93d7d2831d0cf46b98754cd3ae893e3ab /libgphoto2/gphoto2-filesys.c | |
parent | 8b05c0065d071772b85971c97560aee2f766bdf0 (diff) | |
download | libgphoto2-e36d7e2a836785bd84a9d56bbeed53eed59c0954.tar.gz |
Rewritten gphoto-filesys:
- tree like structure for directories instead of keeping all
directories full
- fixes LRU problems on capture
- no more limits and fixed name sizes
git-svn-id: https://svn.code.sf.net/p/gphoto/code/trunk/libgphoto2@11674 67ed7778-7388-44ab-90cf-0a291f65f57c
Diffstat (limited to 'libgphoto2/gphoto2-filesys.c')
-rw-r--r-- | libgphoto2/gphoto2-filesys.c | 1409 |
1 files changed, 706 insertions, 703 deletions
diff --git a/libgphoto2/gphoto2-filesys.c b/libgphoto2/gphoto2-filesys.c index f9b5db3d1..385c8d7ec 100644 --- a/libgphoto2/gphoto2-filesys.c +++ b/libgphoto2/gphoto2-filesys.c @@ -2,6 +2,7 @@ * * \author Copyright 2000 Scott Fritzinger * \author Contributions Lutz Müller <lutz@users.sf.net> (2001) + * \author Copyright 2009 Marcus Meissner * * \par License * This library is free software; you can redistribute it and/or @@ -68,9 +69,12 @@ #endif typedef struct _CameraFilesystemFile { - char name [256]; + char *name; + int info_dirty; + CameraFileInfo info; + struct _CameraFilesystemFile *lru_prev; struct _CameraFilesystemFile *lru_next; CameraFile *preview; @@ -79,14 +83,19 @@ typedef struct _CameraFilesystemFile { CameraFile *audio; CameraFile *exif; CameraFile *metadata; + + struct _CameraFilesystemFile *next; /* in folder */ } CameraFilesystemFile; -typedef struct { - int count; - char name [PATH_MAX]; +typedef struct _CameraFilesystemFolder { + char *name; + int files_dirty; int folders_dirty; - CameraFilesystemFile *file; + + struct _CameraFilesystemFolder *next; /* chain in same folder */ + struct _CameraFilesystemFolder *folders; /* childchain of this folder */ + struct _CameraFilesystemFile *files; /* of this folder */ } CameraFilesystemFolder; /** @@ -112,15 +121,15 @@ 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 ((char*)e->data) - 1900; - ts.tm_mon = atoi ((char*)(e->data + 5)) - 1; - ts.tm_mday = atoi ((char*)(e->data + 8)); - ts.tm_hour = atoi ((char*)(e->data + 11)); - ts.tm_min = atoi ((char*)(e->data + 14)); - ts.tm_sec = atoi ((char*)(e->data + 17)); + e->data[4] = e->data[ 7] = e->data[10] = e->data[13] = e->data[16] = 0; + ts.tm_year = atoi ((char*)e->data) - 1900; + ts.tm_mon = atoi ((char*)(e->data + 5)) - 1; + ts.tm_mday = atoi ((char*)(e->data + 8)); + ts.tm_hour = atoi ((char*)(e->data + 11)); + ts.tm_min = atoi ((char*)(e->data + 14)); + ts.tm_sec = atoi ((char*)(e->data + 17)); - return mktime (&ts); + return mktime (&ts); } static time_t @@ -130,15 +139,15 @@ get_exif_mtime (const unsigned char *data, unsigned long size) 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; - } + 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. - */ + /* + * 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) @@ -163,18 +172,18 @@ get_exif_mtime (const unsigned char *data, unsigned long size) t2 = get_time_from_exif_tag(e); exif_data_unref (e); } - e = exif_content_get_entry (ed->ifd_exif, + 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; - } + 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" */ @@ -184,36 +193,36 @@ get_exif_mtime (const unsigned char *data, unsigned long size) 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); + 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) + const char *filename) { - CameraFile *file; - const char *data = NULL; - unsigned long int size = 0; - time_t t; + CameraFile *file; + const char *data = NULL; + unsigned long int size = 0; + time_t t; - if (!fs) - return 0; + if (!fs) + return 0; - gp_file_new (&file); - if (gp_filesystem_get_file (fs, folder, filename, + 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_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 ((unsigned char*)data, size); - gp_file_unref (file); + gp_file_get_data_and_size (file, &data, &size); + t = get_exif_mtime ((unsigned char*)data, size); + gp_file_unref (file); - return (t); + return (t); } #endif @@ -224,8 +233,7 @@ gp_filesystem_get_exif_mtime (CameraFilesystem *fs, const char *folder, * can only access them using the functions provided by gphoto2. **/ struct _CameraFilesystem { - int count; - CameraFilesystemFolder *folder; + CameraFilesystemFolder *rootfolder; CameraFilesystemFile *lru_first; CameraFilesystemFile *lru_last; @@ -294,196 +302,274 @@ struct _CameraFilesystem { } \ } -/** 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) +delete_all_files (CameraFilesystem *fs, CameraFilesystemFolder *folder) { - int y; + CameraFilesystemFile *file; - CHECK_NULL (fs); + CHECK_NULL (folder); + gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", "Delete all files in folder %p", folder); - if (fs->folder[x].count) { + file = folder->files; + while (file) { + CameraFilesystemFile *next; /* Get rid of cached files */ - for (y = 0; y < fs->folder[x].count; y++) { - gp_filesystem_lru_remove_one (fs, &fs->folder[x].file[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; - } - if (fs->folder[x].file[y].metadata) { - gp_file_unref (fs->folder[x].file[y].metadata); - fs->folder[x].file[y].metadata = NULL; - } + gp_filesystem_lru_remove_one (fs, file); + if (file->preview) { + gp_file_unref (file->preview); + file->preview = NULL; } - - free (fs->folder[x].file); - fs->folder[x].file = NULL; - fs->folder[x].count = 0; - } - - return (GP_OK); + if (file->normal) { + gp_file_unref (file->normal); + file->normal = NULL; + } + if (file->raw) { + gp_file_unref (file->raw); + file->raw = NULL; + } + if (file->audio) { + gp_file_unref (file->audio); + file->audio = NULL; + } + if (file->exif) { + gp_file_unref (file->exif); + file->exif = NULL; + } + if (file->metadata) { + gp_file_unref (file->metadata); + file->metadata = NULL; + } + next = file->next; + free (file->name); + free (file); + file = next; + } + folder->files = NULL; + return (GP_OK); } static int -delete_folder (CameraFilesystem *fs, int x) +delete_folder (CameraFilesystem *fs, CameraFilesystemFolder **folder) { - CameraFilesystemFolder *new_fop; - - CHECK_NULL (fs); + CameraFilesystemFolder *next; + CHECK_NULL (folder); + + gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", "Delete one folder %p", *folder); + next = (*folder)->next; + delete_all_files (fs, *folder); + free ((*folder)->name); + free (*folder); + *folder = next; + return (GP_OK); +} - delete_all_files (fs, x); +static CameraFilesystemFolder* +lookup_folder ( + CameraFilesystem *fs, + CameraFilesystemFolder *folder, const char *foldername, + GPContext *context +) { + CameraFilesystemFolder *f; + const char *curpt = foldername; + const char *s; + + gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", "Lookup folder '%s'...", foldername); + while (folder) { + /* handle multiple slashes, and slashes at the end */ + while (curpt[0]=='/') + curpt++; + if (!curpt[0]) { + gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", "Found! %s is %p", foldername, folder); + return folder; + } - /* 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--; + s = strchr(curpt,'/'); + /* Check if we need to load the folder ... */ + if (folder->folders_dirty) { + CameraList *list; + char *copy = strdup (foldername); + int ret; + /* + * The parent folder is dirty. List the folders in the parent + * folder to make it clean. + */ + /* the character _before_ curpt is a /, overwrite it temporary with \0 */ + copy[curpt-foldername] = '\0'; + gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", "Folder %s is dirty. " + "Listing folders in there to make folder clean...", copy); + ret = gp_list_new (&list); + if (ret == GP_OK) { + ret = gp_filesystem_list_folders (fs, copy, list, context); + gp_list_free (list); + } + /* ignore return code. it will just be dirty. */ + gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", "Done making folder %s clean...", copy); + free (copy); + } + f = folder->folders; + while (f) { + if (s) { + if (!strncmp(f->name,curpt, (s-curpt)) && + (strlen(f->name) == (s-curpt)) + ) { + folder = f; + curpt = s; + break; + } + } else { + if (!strcmp(f->name,curpt)) + return f; + } + f = f->next; + } + folder = f; + } + return NULL; +} - /* 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; +static int +lookup_folder_file ( + CameraFilesystem *fs, + const char *folder, const char *filename, + CameraFilesystemFolder **xfolder, CameraFilesystemFile **xfile, + GPContext *context +) { + CameraFilesystemFolder* xf; + CameraFilesystemFile* f; + + gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", "Lookup folder %s file %s", folder, filename); + xf = lookup_folder (fs, fs->rootfolder, folder, context); + if (!xf) return GP_ERROR_DIRECTORY_NOT_FOUND; + + f = xf->files; + while (f) { + if (!strcmp (f->name, filename)) { + *xfile = f; + *xfolder = xf; + return GP_OK; + } + f = f->next; + } + return GP_ERROR_FILE_NOT_FOUND; +} - return (GP_OK); +/* delete all folder content */ +static int +recurse_delete_folder (CameraFilesystem *fs, CameraFilesystemFolder *folder) { + CameraFilesystemFolder **f; + + gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", "Recurse delete folder %p", folder); + f = &folder->folders; + while (*f) { + recurse_delete_folder (fs, *f); + delete_folder (fs, f); /* will also advance to next */ + } + return (GP_OK); } static int -delete_all_folders (CameraFilesystem *fs, const char *folder, +delete_all_folders (CameraFilesystem *fs, const char *foldername, GPContext *context) { - int x; + CameraFilesystemFolder *folder; gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", "Internally deleting " - "all folders from '%s'...", folder); + "all folders from '%s'...", foldername); - CHECK_NULL (fs && folder); + CHECK_NULL (fs && foldername); 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; + CA (foldername, context); - /* Handle "/foo/bar" "/foo/bar bar" style directories, - * where on is contained in the other, but not a subdirectory. - * But make sure "/" still supercedes "/foo". - */ - if ( (folder[strlen(folder)-1] != '/') && - (fs->folder[x].name[strlen(folder)] != '/') && - (fs->folder[x].name[strlen(folder)] != '\0') - ) - continue; - - CR (delete_all_files (fs, x)); - CR (delete_folder (fs, x)); - x--; - } + folder = lookup_folder (fs, fs->rootfolder, foldername, context); + return recurse_delete_folder (fs, folder); +} - return (GP_OK); +/* create and append 1 new folder entry to the current folder */ +static int +append_folder_one ( + CameraFilesystemFolder *folder, + const char *name, + CameraFilesystemFolder **newfolder +) { + CameraFilesystemFolder *f; + + gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", "Append one folder %s", name); + CHECK_MEM (f = calloc(sizeof(CameraFilesystemFolder),1)); + CHECK_MEM (f->name = strdup (name)); + f->files_dirty = 1; + f->folders_dirty = 1; + + /* Link into the current chain... perhaps later alphabetically? */ + f->next = folder->folders; + folder->folders = f; + if (newfolder) *newfolder = f; + return (GP_OK); } +/* This is a mix between lookup and folder creator */ static int -append_folder (CameraFilesystem *fs, const char *folder, GPContext *context) -{ - CameraFilesystemFolder *new; - int x; - char *buf = NULL; +append_to_folder (CameraFilesystemFolder *folder, + const char *foldername, + CameraFilesystemFolder **newfolder +) { + CameraFilesystemFolder *f; + char *s; + + gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", "Append to folder %p - %s", folder, foldername); + /* Handle multiple slashes, and slashes at the end */ + while (foldername[0]=='/') + foldername++; + if (!foldername[0]) { + if (newfolder) *newfolder = folder; + return (GP_OK); + } - gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", - "Internally appending folder %s...", folder); + s = strchr(foldername,'/'); + f = folder->folders; + while (f) { + if (s) { + if (!strncmp(f->name,foldername, (s-foldername)) && + (strlen(f->name) == (s-foldername)) + ) + return append_to_folder (f, s+1, newfolder); + } else { + if (!strcmp(f->name,foldername)) { + if (newfolder) *newfolder = f; + return (GP_OK); + } + } + f = f->next; + } + /* Not found ... create new folder */ + if (s) { + char *x; + CHECK_MEM (x = calloc ((s-foldername)+1,1)); + memcpy (x, foldername, (s-foldername)); + x[s-foldername] = 0; + CR (append_folder_one (folder, x, newfolder)); + free (x); + } else { + CR (append_folder_one (folder, foldername, newfolder)); + } + return (GP_OK); +} +static int +append_folder (CameraFilesystem *fs, + const char *folder, + CameraFilesystemFolder **newfolder, + GPContext *context +) { + gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", "Appending folder %s...", folder); CHECK_NULL (fs); - CHECK_NULL (folder); + 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_log (GP_LOG_DEBUG, "filesys", "Folder %s " - "is duplicated, return OK.", folder); - return (GP_OK); - } - - /* 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)) - } - free(buf); - - /* Allocate the folder pointer and the actual folder */ - if (fs->count) - CHECK_MEM (new = realloc (fs->folder, - sizeof (CameraFilesystemFolder) * (fs->count + 1))) - else - CHECK_MEM (new = malloc (sizeof (CameraFilesystemFolder))); - fs->folder = new; - fs->count++; - - /* Initialize the folder (and remove trailing slashes if necessary). */ - CBO(sizeof(fs->folder[fs->count - 1].name), strlen(folder), - "append_folder(): folder >= sizeof(CameraFilesystemFolder.name)"); - strcpy (fs->folder[fs->count - 1].name, folder); - if ((strlen (folder) > 1) && - (fs->folder[fs->count - 1].name[strlen (folder) - 1] == '/')) - fs->folder[fs->count - 1].name[strlen (folder) - 1] = '\0'; - fs->folder[fs->count - 1].count = 0; - fs->folder[fs->count - 1].files_dirty = 1; - fs->folder[fs->count - 1].folders_dirty = 1; - - return (GP_OK); + return append_to_folder (fs->rootfolder, folder, newfolder); } static int -append_file (CameraFilesystem *fs, int x, CameraFile *file, GPContext *context) +append_file (CameraFilesystem *fs, CameraFilesystemFolder *folder, CameraFile *file, GPContext *context) { CameraFilesystemFile *new; const char *name; @@ -491,27 +577,26 @@ append_file (CameraFilesystem *fs, int x, CameraFile *file, GPContext *context) CHECK_NULL (fs && file); CR (gp_file_get_name (file, &name)); + gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", "Appending file %s...", name); - if (!fs->folder[x].count) - CHECK_MEM (new = malloc (sizeof (CameraFilesystemFile))) - else - CHECK_MEM (new = realloc (fs->folder[x].file, - sizeof (CameraFilesystemFile) * - (fs->folder[x].count + 1))); - fs->folder[x].file = new; - fs->folder[x].count++; - memset (&(fs->folder[x].file[fs->folder[x].count - 1]), 0, - sizeof (CameraFilesystemFile)); - CBO(sizeof(fs->folder[x].file[fs->folder[x].count - 1].name), - strlen(name), - "append_file()"); - strcpy (fs->folder[x].file[fs->folder[x].count - 1].name, name); - fs->folder[x].file[fs->folder[x].count - 1].info_dirty = 1; - fs->folder[x].file[fs->folder[x].count - 1].normal = file; - fs->folder[x].file[fs->folder[x].count - 1].lru_prev = NULL; - fs->folder[x].file[fs->folder[x].count - 1].lru_next = NULL; - gp_file_ref (file); + new = folder->files; + while (new) { + if (!strcmp(new->name, name)) { + gp_log (GP_LOG_ERROR, "gphoto2-filesystem", "File %s already exists!", name); + return (GP_ERROR); + } + new = new->next; + } + + CHECK_MEM (new = calloc (sizeof (CameraFilesystemFile), 1)); + /* hook into folder chain */ + new->next = folder->files; + folder->files = new; + new->name = strdup (name); + new->info_dirty = 1; + new->normal = file; + gp_file_ref (file); return (GP_OK); } @@ -527,21 +612,21 @@ append_file (CameraFilesystem *fs, int x, CameraFile *file, GPContext *context) int gp_filesystem_reset (CameraFilesystem *fs) { - + gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", "resetting filesystem"); CR (gp_filesystem_lru_clear (fs)); - CR (delete_all_folders (fs, "/", NULL)); - CR (delete_all_files (fs, 0)); - - fs->folder[0].folders_dirty = 1; - fs->folder[0].files_dirty = 1; - + if (fs->rootfolder) { + fs->rootfolder->files_dirty = 1; + fs->rootfolder->folders_dirty = 1; + } else { + gp_log (GP_LOG_ERROR,"gphoto2-filesys", "root folder is gone?"); + } return (GP_OK); } /** * \brief Create a new filesystem struct - * + * * \param fs a pointer to a #CameraFilesystem * * Creates a new empty #CameraFilesystem @@ -551,21 +636,26 @@ gp_filesystem_reset (CameraFilesystem *fs) int gp_filesystem_new (CameraFilesystem **fs) { - int result; - CHECK_NULL (fs); CHECK_MEM (*fs = malloc (sizeof (CameraFilesystem))); memset(*fs,0,sizeof(CameraFilesystem)); - result = append_folder (*fs, "/", NULL); - if (result != GP_OK) { + (*fs)->rootfolder = calloc (sizeof (CameraFilesystemFolder), 1); + if (!(*fs)->rootfolder) { free (*fs); - return (result); + return (GP_ERROR_NO_MEMORY); } - - return (GP_OK); + (*fs)->rootfolder->name = strdup("/"); + if (!(*fs)->rootfolder->name) { + free ((*fs)->rootfolder); + free (*fs); + return (GP_ERROR_NO_MEMORY); + } + (*fs)->rootfolder->files_dirty = 1; + (*fs)->rootfolder->folders_dirty = 1; + return (GP_OK); } /** @@ -584,72 +674,10 @@ gp_filesystem_free (CameraFilesystem *fs) /* Now, we've only got left over the root folder. Free that and * the filesystem. */ - free (fs->folder); + free (fs->rootfolder->name); + free (fs->rootfolder); free (fs); - - return (GP_OK); -} - -static int -gp_filesystem_folder_number (CameraFilesystem *fs, const char *folder, - GPContext *context) -{ - int x, y, len; - char buf[PATH_MAX]; - CameraList *list; - - CHECK_NULL (fs && folder); - CC (context); - CA (folder, context); - - /* - * We are nice to front-end/camera-driver writers - we'll ignore - * trailing slashes (if any). - */ - len = strlen (folder); - if ((len > 1) && (folder[len - 1] == '/')) - len--; - - for (x = 0; x < fs->count; x++) - if (!strncmp (fs->folder[x].name, folder, len) && - (len == strlen (fs->folder[x].name))) - return (x); - - /* Ok, we didn't find the folder. Do we have a parent? */ - if (!strcmp (folder, "/")) { - gp_context_error (context, - _("Could not find folder '%s'."), folder); - return (GP_ERROR_DIRECTORY_NOT_FOUND); - } - - /* If the parent folder is not dirty, return. */ - strncpy (buf, folder, len); - buf[len] = '\0'; - for (y = strlen (buf) - 1; y >= 0; y--) - if (buf[y] == '/') - break; - if (y) - buf[y] = '\0'; - else - buf[y + 1] = '\0'; /* Parent is root */ - CR (x = gp_filesystem_folder_number (fs, buf, context)); - if (!fs->folder[x].folders_dirty) { - gp_context_error (context, - _("Folder '%s' does not contain a folder '%s'."), buf, - folder + strlen (buf) + ((strlen (buf) == 1) ? 0 : 1)); - return (GP_ERROR_DIRECTORY_NOT_FOUND); - } - - /* - * The parent folder is dirty. List the folders in the parent - * folder to make it clean. - */ - gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", "Folder %s is dirty. " - "Listing file in there to make folder clean...", buf); - CR (gp_list_new (&list)); - CL (gp_filesystem_list_folders (fs, buf, list, context), list); - gp_list_free (list); - return (gp_filesystem_folder_number (fs, folder, context)); + return (GP_OK); } /** @@ -659,7 +687,7 @@ gp_filesystem_folder_number (CameraFilesystem *fs, const char *folder, * \param filename filename of the file * \param context a #GPContext * - * Tells the fs that there is a file called filename in folder + * Tells the fs that there is a file called filename in folder * called folder. Usually camera drivers will call this function after * capturing an image in order to tell the fs about the new file. * A front-end should not use this function. @@ -667,67 +695,60 @@ gp_filesystem_folder_number (CameraFilesystem *fs, const char *folder, * \return a gphoto2 error code. **/ int -gp_filesystem_append (CameraFilesystem *fs, const char *folder, - const char *filename, GPContext *context) +gp_filesystem_append (CameraFilesystem *fs, const char *folder, + const char *filename, GPContext *context) { CameraFilesystemFile *new; - int x, y; + CameraFilesystemFolder *f; CHECK_NULL (fs && folder); CC (context); CA (folder, context); - /* Check for existence */ - x = gp_filesystem_folder_number (fs, folder, context); - switch (x) { - case GP_ERROR_DIRECTORY_NOT_FOUND: - CR (append_folder (fs, folder, context)); - break; - default: - CR (x); - } - CR (x = gp_filesystem_folder_number (fs, folder, context)); + gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", "Append %s/%s to filesystem", folder, filename); + /* Check folder for existence, if not, create it. */ + f = lookup_folder (fs, fs->rootfolder, folder, context); + if (!f) + CR (append_folder (fs, folder, &f, context)); - if (!filename) - return (GP_OK); - - /* If file exists, return error */ - for (y = 0; y < fs->folder[x].count; y++) - if (!strncmp (fs->folder[x].file[y].name, filename, - strlen (filename)) && ( - (strlen (filename) == strlen (fs->folder[x].file[y].name)))) - break; - if (y < fs->folder[x].count) { - gp_context_error (context, + new = f->files; + while (new) { + if (!strcmp(new->name, filename)) break; + new = new->next; + } + if (new) { + gp_context_error (context, _("Could not append '%s' to folder '%s' because " "this file already exists."), filename, folder); return (GP_ERROR_FILE_EXISTS); } - /* Allocate a new file in that folder and append the file */ - if (!fs->folder[x].count) - CHECK_MEM (new = malloc (sizeof (CameraFilesystemFile))) - else - CHECK_MEM (new = realloc (fs->folder[x].file, - sizeof (CameraFilesystemFile) * - (fs->folder[x].count + 1))); - fs->folder[x].file = new; - fs->folder[x].count++; - memset (&(fs->folder[x].file[fs->folder[x].count - 1]), 0, - sizeof (CameraFilesystemFile)); - strcpy (fs->folder[x].file[fs->folder[x].count - 1].name, filename); - fs->folder[x].file[fs->folder[x].count - 1].info_dirty = 1; - - /* - * If people manually add files, they probably know the contents of - * this folder. - */ - fs->folder[x].files_dirty = 0; + CHECK_MEM (new = calloc (sizeof (CameraFilesystemFile), 1)) + new->name = strdup (filename); + if (!new->name) { + free (new); + return (GP_ERROR_NO_MEMORY); + } + new->info_dirty = 1; - return (GP_OK); + new->next = f->files; + f->files = new; + f->files_dirty = 0; + return (GP_OK); } -/** +static void +recursive_fs_dump (CameraFilesystemFolder *folder, int depth) { + CameraFilesystemFolder *f; + + gp_log (GP_LOG_DEBUG, "gphoto2-filesys", "%*sFolder %s", depth, " ", folder->name); + f = folder->folders; + while (f) { + recursive_fs_dump (f, depth+4); + f = f->next; + } +} +/** * \brief Dump the current filesystem. * \param fs the #CameraFilesystem * \return a gphoto error code @@ -737,64 +758,52 @@ gp_filesystem_append (CameraFilesystem *fs, const char *folder, int gp_filesystem_dump (CameraFilesystem *fs) { - int i, j; - GP_DEBUG("Dumping Filesystem:"); - for (i = 0; i < fs->count; i++) { - GP_DEBUG(" Folder: %s", fs->folder[i].name); - for (j = 0; j < fs->folder[i].count; j++) { - GP_DEBUG(" %2i: %s", j, fs->folder[i].file[j].name); - } - } - + recursive_fs_dump (fs->rootfolder, 0); return (GP_OK); } static int -delete_file (CameraFilesystem *fs, int x, int y) +delete_file (CameraFilesystem *fs, CameraFilesystemFolder *folder, CameraFilesystemFile *file) { - CameraFilesystemFile *new_fip; + CameraFilesystemFile **prev; - gp_filesystem_lru_remove_one (fs, &fs->folder[x].file[y]); + gp_filesystem_lru_remove_one (fs, file); /* Get rid of cached files */ - if (fs->folder[x].file[y].preview) { - gp_file_unref (fs->folder[x].file[y].preview); - fs->folder[x].file[y].preview = NULL; + if (file->preview) { + gp_file_unref (file->preview); + file->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 (file->normal) { + gp_file_unref (file->normal); + file->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 (file->raw) { + gp_file_unref (file->raw); + file->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 (file->audio) { + gp_file_unref (file->audio); + file->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; + if (file->exif) { + gp_file_unref (file->exif); + file->exif = NULL; } - if (fs->folder[x].file[y].metadata) { - gp_file_unref (fs->folder[x].file[y].metadata); - fs->folder[x].file[y].metadata = NULL; + if (file->metadata) { + gp_file_unref (file->metadata); + file->metadata = NULL; } - /* Move all files behind one position up */ - if (y < fs->folder[x].count - 1) - memmove (&fs->folder[x].file[y], &fs->folder[x].file[y + 1], - sizeof (CameraFilesystemFile) * - (fs->folder[x].count - y - 1)); - fs->folder[x].count--; - - /* Get rid of the last one */ - new_fip = realloc (fs->folder[x].file, - sizeof (CameraFilesystemFile) * (fs->folder[x].count)); - if (!fs->folder[x].count || (fs->folder[x].count && new_fip)) - fs->folder[x].file = new_fip; - + prev = &(folder->files); + while ((*prev) && ((*prev) != file)) + prev = &((*prev)->next); + if (!*prev) + return GP_ERROR; + *prev = file->next; + file->next = NULL; + free (file->name); + free (file); return (GP_OK); } @@ -806,6 +815,7 @@ gp_filesystem_delete_all_one_by_one (CameraFilesystem *fs, const char *folder, int count, x; const char *name; + gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", "Deleting all 1 by 1 from %s", folder); CR (gp_list_new (&list)); CL (gp_filesystem_list_files (fs, folder, list, context), list); CL (count = gp_list_count (list), list); @@ -835,48 +845,47 @@ int gp_filesystem_delete_all (CameraFilesystem *fs, const char *folder, GPContext *context) { - int x, r; + int r; + CameraFilesystemFolder *f; CHECK_NULL (fs && folder); CC (context); CA (folder, context); + gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", "Deleting all from %s", folder); /* Make sure this folder exists */ - CR (x = gp_filesystem_folder_number (fs, folder, context)); + f = lookup_folder (fs, fs->rootfolder, folder, context); + if (!f) return (GP_ERROR_DIRECTORY_NOT_FOUND); if (!fs->delete_all_func) - CR (gp_filesystem_delete_all_one_by_one (fs, folder, context)) - else { - - /* - * Mark the folder dirty - it could be that an error - * happens, and then we don't know which files have been - * deleted and which not. - */ - fs->folder[x].files_dirty = 1; - - /* - * First try to use the delete_all function. If that fails, - * fall back to deletion one-by-one. - */ - r = fs->delete_all_func (fs, folder, fs->folder_data, context); - if (r < 0) { - gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", - "delete_all failed (%s). Falling back to " - "deletion one-by-one.", - gp_result_as_string (r)); - CR (gp_filesystem_delete_all_one_by_one (fs, folder, - context)); - } else - CR (delete_all_files (fs, x)); - - /* - * No error happened. We can be sure that all files have been - * deleted. - */ - fs->folder[x].files_dirty = 0; + return gp_filesystem_delete_all_one_by_one (fs, folder, context); + /* + * Mark the folder dirty - it could be that an error + * happens, and then we don't know which files have been + * deleted and which not. + */ + f->files_dirty = 1; + /* + * First try to use the delete_all function. If that fails, + * fall back to deletion one-by-one. + */ + r = fs->delete_all_func (fs, folder, fs->folder_data, context); + if (r < 0) { + gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", + "delete_all failed (%s). Falling back to " + "deletion one-by-one.", + gp_result_as_string (r)); + CR (gp_filesystem_delete_all_one_by_one (fs, folder, + context)); + } else { + /* delete from filesystem view too now */ + CR (delete_all_files (fs, f)); } - + /* + * No error happened. We can be sure that all files have been + * deleted. + */ + f->files_dirty = 0; return (GP_OK); } @@ -894,14 +903,15 @@ gp_filesystem_delete_all (CameraFilesystem *fs, const char *folder, * \return a gphoto2 error code. **/ int -gp_filesystem_list_files (CameraFilesystem *fs, const char *folder, - CameraList *list, GPContext *context) +gp_filesystem_list_files (CameraFilesystem *fs, const char *folder, + CameraList *list, GPContext *context) { - int x, y, count; + int count, y; const char *name; + CameraFilesystemFolder *f; + CameraFilesystemFile *file; - gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", - "Listing files in '%s'...", folder); + gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", "Listing files in %s", folder); CHECK_NULL (fs && list && folder); CC (context); @@ -910,14 +920,14 @@ gp_filesystem_list_files (CameraFilesystem *fs, const char *folder, gp_list_reset (list); /* Search the folder */ - CR (x = gp_filesystem_folder_number (fs, folder, context)); + f = lookup_folder (fs, fs->rootfolder, folder, context); + if (!f) return (GP_ERROR_DIRECTORY_NOT_FOUND); /* If the folder is dirty, delete the contents and query the camera */ - if (fs->folder[x].files_dirty && fs->file_list_func) { - + if (f->files_dirty && fs->file_list_func) { gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", "Querying folder %s...", folder); - CR (delete_all_files (fs, x)); + CR (delete_all_files (fs, f)); CR (fs->file_list_func (fs, folder, list, fs->list_data, context)); @@ -932,14 +942,15 @@ gp_filesystem_list_files (CameraFilesystem *fs, const char *folder, } /* The folder is clean now */ - fs->folder[x].files_dirty = 0; + f->files_dirty = 0; - for (y = 0; y < fs->folder[x].count; y++) { + file = f->files; + while (file) { gp_log (GP_LOG_DEBUG, "filesys", - "Listed '%s'", fs->folder[x].file[y].name); - CR (gp_list_append (list, fs->folder[x].file[y].name, NULL)); + "Listed '%s'", file->name); + CR (gp_list_append (list, file->name, NULL)); + file = file->next; } - return (GP_OK); } @@ -950,9 +961,9 @@ gp_filesystem_list_files (CameraFilesystem *fs, const char *folder, * \param list a #CameraList where subfolders should be listed * \param context a #GPContext * - * Generates a list of subfolders of the supplied folder either using - * cached values (if there are any) or the folder_list_func if it has been - * supplied previously. If not, it is assumed that only a root folder + * Generates a list of subfolders of the supplied folder either using + * cached values (if there are any) or the folder_list_func if it has been + * supplied previously. If not, it is assumed that only a root folder * exists (which is the case for many cameras). * * \return a gphoto2 error code. @@ -961,88 +972,48 @@ int gp_filesystem_list_folders (CameraFilesystem *fs, const char *folder, CameraList *list, GPContext *context) { - int x, y, j, offset, count; - char buf[PATH_MAX]; + int y, count; const char *name; - unsigned int len; + CameraFilesystemFolder *f, *new; - gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", - "Listing folders in '%s'...", folder); + gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", "Listing folders in %s", folder); CHECK_NULL (fs && folder && list); CC (context); CA (folder, context); - /* Guard against trailing slashes */ - len = strlen (folder); - if ((len > 1) && (folder[len - 1] == '/')) - len--; - gp_list_reset (list); /* Search the folder */ - CR (x = gp_filesystem_folder_number (fs, folder, context)); + f = lookup_folder (fs, fs->rootfolder, folder, context); + if (!f) return (GP_ERROR_DIRECTORY_NOT_FOUND); + /* If the folder is dirty, query the contents. */ - if (fs->folder[x].folders_dirty && fs->folder_list_func) { + if (f->folders_dirty && fs->folder_list_func) { + gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", "... is dirty, getting from camera"); CR (fs->folder_list_func (fs, folder, list, fs->list_data, context)); CR (delete_all_folders (fs, folder, context)); + CR (count = gp_list_count (list)); for (y = 0; y < count; y++) { CR (gp_list_get_name (list, y, &name)); - memset (buf, 0, sizeof (buf)); - strncpy (buf, folder, MIN (sizeof (buf), len)); - if (buf[strlen (buf) - 1] != '/') - strncat (buf, "/", sizeof (buf) - strlen (buf) - 1); - strncat (buf, name, sizeof (buf) - strlen (buf) - 1); - CR (append_folder (fs, buf, context)); + CR (append_folder_one (f, name, NULL)); } + /* FIXME: why not just return (GP_OK); ? the list should be fine */ gp_list_reset (list); } - for (x = 0; x < fs->count; x++) - if (!strncmp (fs->folder[x].name, folder, len)) { - - /* - * Is this really a subfolder (and not the folder - * itself)? - */ - if (strlen (fs->folder[x].name) <= len) - continue; - - /* handle "/Music/foo " "/Music/foo bar" style directories */ - if ( (len > 1) && /* ignore for / directory, see len calculation above. */ - (fs->folder[x].name[len] != '/') && - (fs->folder[x].name[len] != '\0') - ) - continue; - - /* - * Is this really a direct subfolder (and not a - * subsubfolder)? - */ - for (j = len + 1; fs->folder[x].name[j] != '\0'; j++) - if (fs->folder[x].name[j] == '/') - break; - if (j == strlen (fs->folder[x].name)) { - if (!strcmp (folder, "/")) - offset = 1; - else - offset = len + 1; - CR (gp_list_append (list, - fs->folder[x].name + offset, - NULL)); - } - } - + new = f->folders; + while (new) { + CR (gp_list_append (list, new->name, NULL)); + new = new->next; + } /* The folder is clean now */ - CR (x = gp_filesystem_folder_number (fs, folder, context)); - fs->folder[x].folders_dirty = 0; - + f->folders_dirty = 0; gp_log (GP_LOG_DEBUG, "gphoto2-filesystem", "Folder %s contains %i " - "files.", folder, fs->folder[x].count); - + "subfolders.", folder, gp_list_count (list)); return (GP_OK); } @@ -1060,15 +1031,24 @@ int gp_filesystem_count (CameraFilesystem *fs, const char *folder, GPContext *context) { - int x; + int x; + CameraFilesystemFolder *f; + CameraFilesystemFile *file; CHECK_NULL (fs && folder); CC (context); CA (folder, context); - CR (x = gp_filesystem_folder_number (fs, folder, context)); + f = lookup_folder (fs, fs->rootfolder, folder, context); + if (!f) return (GP_ERROR_DIRECTORY_NOT_FOUND); - return (fs->folder[x].count); + x = 0; + file = f->files; + while (file) { + x++; + file = file->next; + } + return x; } /** @@ -1079,16 +1059,17 @@ gp_filesystem_count (CameraFilesystem *fs, const char *folder, * \param context a #GPContext * * If a delete_file_func has been supplied to the fs, this function will - * be called and, if this function returns without error, the file will be + * be called and, if this function returns without error, the file will be * removed from the fs. * * \return a gphoto2 error code. **/ int -gp_filesystem_delete_file (CameraFilesystem *fs, const char *folder, +gp_filesystem_delete_file (CameraFilesystem *fs, const char *folder, const char *filename, GPContext *context) { - int x, y; + CameraFilesystemFolder *f; + CameraFilesystemFile *file; CHECK_NULL (fs && folder && filename); CC (context); @@ -1103,16 +1084,14 @@ gp_filesystem_delete_file (CameraFilesystem *fs, const char *folder, } /* Search the folder and the file */ - CR (x = gp_filesystem_folder_number (fs, folder, context)); - CR (y = gp_filesystem_number (fs, folder, filename, context)); + CR (lookup_folder_file (fs, folder, filename, &f, &file, context)); gp_context_status (context, _("Deleting '%s' from folder '%s'..."), filename, folder); /* Delete the file */ CR (fs->delete_file_func (fs, folder, filename, fs->file_data, context)); - CR (delete_file (fs, x, y)); - + CR (delete_file (fs, f, file)); return (GP_OK); } @@ -1133,18 +1112,15 @@ int gp_filesystem_delete_file_noop (CameraFilesystem *fs, const char *folder, const char *filename, GPContext *context) { - int x, y; + CameraFilesystemFolder *f; + CameraFilesystemFile *file; CHECK_NULL (fs && folder && filename); CC (context); CA (folder, context); - /* Search the folder and the file */ - CR (x = gp_filesystem_folder_number (fs, folder, context)); - CR (y = gp_filesystem_number (fs, folder, filename, context)); - CR (delete_file (fs, x, y)); - - return (GP_OK); + CR (lookup_folder_file (fs, folder, filename, &f, &file, context)); + return delete_file (fs, f, file); } /** @@ -1162,8 +1138,7 @@ int gp_filesystem_make_dir (CameraFilesystem *fs, const char *folder, const char *name, GPContext *context) { - int x; - char path[2048]; + CameraFilesystemFolder *f; CHECK_NULL (fs && folder && name); CC (context); @@ -1173,18 +1148,13 @@ gp_filesystem_make_dir (CameraFilesystem *fs, const char *folder, return (GP_ERROR_NOT_SUPPORTED); /* Search the folder */ - CR (x = gp_filesystem_folder_number (fs, folder, context)); - - strncpy (path, folder, sizeof (path)); - if (path[strlen (path) - 1] != '/') - strncat (path, "/", sizeof (path) - strlen (path) - 1); - strncat (path, name, sizeof (path) - strlen (path) - 1); + f = lookup_folder (fs, fs->rootfolder, folder, context); + if (!f) return (GP_ERROR_DIRECTORY_NOT_FOUND); /* Create the directory */ CR (fs->make_dir_func (fs, folder, name, fs->folder_data, context)); - CR (append_folder (fs, path, context)); - - return (GP_OK); + /* and append to internal fs */ + return append_folder_one (f, name, NULL); } /** @@ -1202,50 +1172,44 @@ int gp_filesystem_remove_dir (CameraFilesystem *fs, const char *folder, const char *name, GPContext *context) { - int x; - char path[2048]; - CameraList *list; + CameraFilesystemFolder *f; + CameraFilesystemFolder **prev; CHECK_NULL (fs && folder && name); CC (context); CA (folder, context); - if (!fs->remove_dir_func) return (GP_ERROR_NOT_SUPPORTED); - CR (gp_list_new (&list)); /* * Make sure there are neither files nor folders in the folder * that is to be removed. */ - strncpy (path, folder, sizeof (path)); - if (path[strlen (path) - 1] != '/') - strncat (path, "/", sizeof (path) - strlen (path) - 1); - strncat (path, name, sizeof (path) - strlen (path) - 1); - CL (gp_filesystem_list_folders (fs, path, list, context), list); - if (gp_list_count (list)) { + f = lookup_folder (fs, fs->rootfolder, folder, context); + if (!f) return (GP_ERROR_DIRECTORY_NOT_FOUND); + prev = &(f->folders); + while (*prev) { + if (!strcmp (name, (*prev)->name)) + break; + prev = &((*prev)->next); + } + if (!*prev) return (GP_ERROR_DIRECTORY_NOT_FOUND); + + if ((*prev)->folders) { gp_context_error (context, _("There are still subfolders in " - "folder '%s' that you are trying to remove."), path); - gp_list_free (list); + "folder '%s/%s' that you are trying to remove."), folder, name); return (GP_ERROR_DIRECTORY_EXISTS); } - CL (gp_filesystem_list_files (fs, path, list, context), list); - if (gp_list_count (list)) { + if ((*prev)->files) { gp_context_error (context, _("There are still files in " - "folder '%s' that you are trying to remove."), path); - gp_list_free(list); + "folder '%s/%s' that you are trying to remove."), folder,name); return (GP_ERROR_FILE_EXISTS); } - gp_list_free(list); - - /* Search the folder */ - CR (x = gp_filesystem_folder_number (fs, path, context)); /* Remove the directory */ CR (fs->remove_dir_func (fs, folder, name, fs->folder_data, context)); - CR (delete_folder (fs, x)); - + CR (delete_folder (fs, prev)); return (GP_OK); } @@ -1256,7 +1220,7 @@ gp_filesystem_remove_dir (CameraFilesystem *fs, const char *folder, * \param file the file * \param context a #GPContext * - * Uploads a file to the camera if a put_file_func has been previously + * Uploads a file to the camera if a put_file_func has been previously * supplied to the fs. If the upload is successful, the file will get * cached in the fs. * @@ -1266,7 +1230,7 @@ int gp_filesystem_put_file (CameraFilesystem *fs, const char *folder, CameraFile *file, GPContext *context) { - int x; + CameraFilesystemFolder *f; CHECK_NULL (fs && folder && file); CC (context); @@ -1280,13 +1244,13 @@ gp_filesystem_put_file (CameraFilesystem *fs, const char *folder, } /* Search the folder */ - CR (x = gp_filesystem_folder_number (fs, folder, context)); + f = lookup_folder (fs, fs->rootfolder, folder, context); + if (!f) return (GP_ERROR_DIRECTORY_NOT_FOUND); /* Upload the file */ CR (fs->put_file_func (fs, folder, file, fs->folder_data, context)); - CR (append_file (fs, x, file, context)); - - return (GP_OK); + /* And upload it to internal structure too */ + return append_file (fs, f, file, context); } /** @@ -1299,29 +1263,40 @@ gp_filesystem_put_file (CameraFilesystem *fs, const char *folder, * * Looks up the filename of file with given filenumber in given folder. * See gp_filesystem_number for exactly the opposite functionality. - * + * * \return a gphoto2 error code. **/ int gp_filesystem_name (CameraFilesystem *fs, const char *folder, int filenumber, const char **filename, GPContext *context) { - int x; - + CameraFilesystemFolder *f; + CameraFilesystemFile *file; + int count; CHECK_NULL (fs && folder); CC (context); CA (folder, context); - CR (x = gp_filesystem_folder_number (fs, folder, context)); - - if (filenumber > fs->folder[x].count) { + f = lookup_folder (fs, fs->rootfolder, folder, context); + if (!f) return (GP_ERROR_DIRECTORY_NOT_FOUND); + + file = f->files; + count = 0; + while (file) { + if (filenumber == 0) + break; + filenumber--; + count++; + file = file->next; + } + + if (!file) { gp_context_error (context, _("Folder '%s' only contains " "%i files, but you requested a file with number %i."), - folder, fs->folder[x].count, filenumber); + folder, count, filenumber); return (GP_ERROR_FILE_NOT_FOUND); } - - *filename = fs->folder[x].file[filenumber].name; + *filename = file->name; return (GP_OK); } @@ -1338,38 +1313,41 @@ gp_filesystem_name (CameraFilesystem *fs, const char *folder, int filenumber, * \return a gphoto2 error code. **/ int -gp_filesystem_number (CameraFilesystem *fs, const char *folder, +gp_filesystem_number (CameraFilesystem *fs, const char *folder, const char *filename, GPContext *context) { + CameraFilesystemFolder *f; + CameraFilesystemFile *file; CameraList *list; - int x, y; + int num; CHECK_NULL (fs && folder && filename); CC (context); CA (folder, context); - CR (gp_list_new(&list)); + f = lookup_folder (fs, fs->rootfolder, folder, context); + if (!f) return (GP_ERROR_DIRECTORY_NOT_FOUND); - CL (x = gp_filesystem_folder_number (fs, folder, context), list); - - for (y = 0; y < fs->folder[x].count; y++) - if (!strcmp (fs->folder[x].file[y].name, filename)) { - gp_list_free (list); - return (y); - } + file = f->files; + num = 0; + while (file) { + if (!strcmp (file->name, filename)) + return num; + num++; + file = file->next; + } /* Ok, we didn't find the file. Is the folder dirty? */ - if (!fs->folder[x].files_dirty) { + if (!f->files_dirty) { gp_context_error (context, _("File '%s' could not be found " "in folder '%s'."), filename, folder); - gp_list_free (list); return (GP_ERROR_FILE_NOT_FOUND); } - /* The folder is dirty. List all files to make it clean */ + CR (gp_list_new(&list)); CL (gp_filesystem_list_files (fs, folder, list, context), list); gp_list_free(list); - return (gp_filesystem_number (fs, folder, filename, context)); + return (gp_filesystem_number (fs, folder, filename, context)); } static int @@ -1413,6 +1391,40 @@ gp_filesystem_scan (CameraFilesystem *fs, const char *folder, return (GP_OK); } +static int +recursive_folder_scan ( + CameraFilesystemFolder *folder, const char *lookforfile, + char **foldername +) { + CameraFilesystemFile *file; + CameraFilesystemFolder *f; + int ret; + + file = folder->files; + while (file) { + if (!strcmp(file->name, lookforfile)) { + *foldername = strdup (folder->name); + return GP_OK; + } + file = file->next; + } + f = folder->folders; + while (f) { + char *xfolder; + ret = recursive_folder_scan (f, lookforfile, &xfolder); + if (ret == GP_OK) { + (*foldername) = malloc (strlen (folder->name) + 1 + strlen (xfolder) + 1); + strcpy ((*foldername),folder->name); + strcat ((*foldername),"/"); + strcat ((*foldername),xfolder); + free (xfolder); + return GP_OK; + } + f = f->next; + } + /* thorugh all subfoilders */ + return GP_ERROR_FILE_NOT_FOUND; +} /** * \brief Search a folder that contains a given filename * \param fs a #CameraFilesystem @@ -1420,11 +1432,11 @@ gp_filesystem_scan (CameraFilesystem *fs, const char *folder, * \param folder pointer to value where the string is stored in * \param context a #GPContext * - * Searches a file called filename in the fs and returns the first + * Searches a file called filename in the fs and returns the first * occurrency. This functionality is needed for camera drivers that cannot * figure out where a file gets created after capturing an image although the * name of the image is known. Usually, those drivers will call - * gp_filesystem_reset in order to tell the fs that something has + * gp_filesystem_reset in order to tell the fs that something has * changed and then gp_filesystem_get_folder in order to find the file. * * Note that you get a reference to the string stored in the filesystem structure, @@ -1433,23 +1445,18 @@ gp_filesystem_scan (CameraFilesystem *fs, const char *folder, * \return a gphoto2 error code. **/ int -gp_filesystem_get_folder (CameraFilesystem *fs, const char *filename, - const char **folder, GPContext *context) +gp_filesystem_get_folder (CameraFilesystem *fs, const char *filename, + char **folder, GPContext *context) { - int x, y; + int ret; CHECK_NULL (fs && filename && folder); CC (context); CR (gp_filesystem_scan (fs, "/", filename, context)); - for (x = 0; x < fs->count; x++) - for (y = 0; y < fs->folder[x].count; y++) - if (!strcmp (fs->folder[x].file[y].name, filename)) { - *folder = fs->folder[x].name; - return (GP_OK); - } - + ret = recursive_folder_scan ( fs->rootfolder, filename, folder); + if (ret == GP_OK) return ret; gp_context_error (context, _("Could not find file '%s'."), filename); return (GP_ERROR_FILE_NOT_FOUND); } @@ -1461,9 +1468,9 @@ gp_filesystem_get_folder (CameraFilesystem *fs, const char *filename, * \param folder_list_func the function that will return listings of folders * \param data private data structure * - * Tells the fs which functions to use to retrieve listings of folders + * Tells the fs which functions to use to retrieve listings of folders * and/or files. Typically, a camera driver would call this function - * on initialization. Each function can be NULL indicating that this + * on initialization. Each function can be NULL indicating that this * functionality is not supported. For example, many cameras don't support * folders. In this case, you would supply NULL for folder_list_func. Then, * the fs assumes that there is only a root folder. @@ -1495,7 +1502,7 @@ gp_filesystem_set_list_funcs (CameraFilesystem *fs, * Tells the fs which functions to use for file download or file deletion. * Typically, a camera driver would call this function on initialization. * A function can be NULL indicating that this functionality is not supported. - * For example, if a camera does not support file deletion, you would supply + * For example, if a camera does not support file deletion, you would supply * NULL for del_file_func. * * \return a gphoto2 error code. @@ -1525,9 +1532,9 @@ gp_filesystem_set_file_funcs (CameraFilesystem *fs, * \param data a data object that will passed to all called functions * * Tells the filesystem which functions to call for file upload, deletion - * of all files in a given folder, creation or removal of a folder. - * Typically, a camera driver would call this function on initialization. - * If one functionality is not supported, NULL can be supplied. + * of all files in a given folder, creation or removal of a folder. + * Typically, a camera driver would call this function on initialization. + * If one functionality is not supported, NULL can be supplied. * If you don't call this function, the fs will assume that neither * of these features is supported. * @@ -1561,7 +1568,8 @@ gp_filesystem_get_file_impl (CameraFilesystem *fs, const char *folder, const char *filename, CameraFileType type, CameraFile *file, GPContext *context) { - int x, y; + CameraFilesystemFolder *xfolder; + CameraFilesystemFile *xfile; CHECK_NULL (fs && folder && file && filename); CC (context); @@ -1580,39 +1588,32 @@ gp_filesystem_get_file_impl (CameraFilesystem *fs, const char *folder, } /* Search folder and file */ - CR (x = gp_filesystem_folder_number (fs, folder, context)); - CR (y = gp_filesystem_number (fs, folder, filename, context)); + CR( lookup_folder_file (fs, folder, filename, &xfolder, &xfile, context)); switch (type) { case GP_FILE_TYPE_PREVIEW: - if (fs->folder[x].file[y].preview) - return (gp_file_copy (file, - fs->folder[x].file[y].preview)); + if (xfile->preview) + return (gp_file_copy (file, xfile->preview)); break; case GP_FILE_TYPE_NORMAL: - if (fs->folder[x].file[y].normal) - return (gp_file_copy (file, - fs->folder[x].file[y].normal)); + if (xfile->normal) + return (gp_file_copy (file, xfile->normal)); break; case GP_FILE_TYPE_RAW: - if (fs->folder[x].file[y].raw) - return (gp_file_copy (file, - fs->folder[x].file[y].raw)); + if (xfile->raw) + return (gp_file_copy (file, xfile->raw)); break; case GP_FILE_TYPE_AUDIO: - if (fs->folder[x].file[y].audio) - return (gp_file_copy (file, - fs->folder[x].file[y].audio)); + if (xfile->audio) + return (gp_file_copy (file, xfile->audio)); break; case GP_FILE_TYPE_EXIF: - if (fs->folder[x].file[y].exif) - return (gp_file_copy (file, - fs->folder[x].file[y].exif)); + if (xfile->exif) + return (gp_file_copy (file, xfile->exif)); break; case GP_FILE_TYPE_METADATA: - if (fs->folder[x].file[y].metadata) - return (gp_file_copy (file, - fs->folder[x].file[y].metadata)); + if (xfile->metadata) + return (gp_file_copy (file, xfile->metadata)); break; default: gp_context_error (context, _("Unknown file type %i."), type); @@ -1650,8 +1651,8 @@ gp_filesystem_get_file_impl (CameraFilesystem *fs, const char *folder, * \param file the file that receives the data * \param context a #GPContext * - * Downloads the file called filename from the folder using the - * get_file_func if such a function has been previously supplied. If the + * Downloads the file called filename from the folder using the + * get_file_func if such a function has been previously supplied. If the * file has been previously downloaded, the file is retrieved from cache. * The result is stored in the passed file structure. * @@ -1659,8 +1660,8 @@ gp_filesystem_get_file_impl (CameraFilesystem *fs, const char *folder, **/ int gp_filesystem_get_file (CameraFilesystem *fs, const char *folder, - const char *filename, CameraFileType type, - CameraFile *file, GPContext *context) + const char *filename, CameraFileType type, + CameraFile *file, GPContext *context) { int r; #ifdef HAVE_LIBEXIF @@ -1679,7 +1680,7 @@ gp_filesystem_get_file (CameraFilesystem *fs, const char *folder, (type == GP_FILE_TYPE_PREVIEW)) { /* - * Could not get preview (unsupported operation). Some + * Could not get preview (unsupported operation). Some * cameras hide the thumbnail in EXIF data. Check it out. */ #ifdef HAVE_LIBEXIF @@ -1703,7 +1704,7 @@ gp_filesystem_get_file (CameraFilesystem *fs, const char *folder, } /* - * We found a thumbnail in EXIF data! Those + * We found a thumbnail in EXIF data! Those * thumbnails are always JPEG. Set up the file. */ r = gp_file_set_data_and_size (file, (char*)ed->data, ed->size); @@ -1780,9 +1781,9 @@ gp_filesystem_get_file (CameraFilesystem *fs, const char *folder, * \param set_info_func the function to set file information * \param data private data * - * Tells the filesystem which functions to call when file information + * Tells the filesystem which functions to call when file information * about a file should be retrieved or set. Typically, this function will - * get called by the camera driver on initialization. + * get called by the camera driver on initialization. * * \return a gphoto2 error code. **/ @@ -1807,7 +1808,7 @@ gp_filesystem_set_info_funcs (CameraFilesystem *fs, * \param funcs pointer to a struct of filesystem functions * \param data private data * - * Tells the filesystem which functions to call for camera/filesystem specific + * Tells the filesystem which functions to call for camera/filesystem specific * functions, like listing, retrieving, uploading files and so on. * * \return a gphoto2 error code. @@ -1856,7 +1857,8 @@ gp_filesystem_get_info (CameraFilesystem *fs, const char *folder, const char *filename, CameraFileInfo *info, GPContext *context) { - int x, y; + CameraFilesystemFolder *f; + CameraFilesystemFile *file; #ifdef HAVE_LIBEXIF time_t t; #endif @@ -1876,13 +1878,13 @@ gp_filesystem_get_info (CameraFilesystem *fs, const char *folder, } /* Search folder and file and get info if needed */ - CR (x = gp_filesystem_folder_number (fs, folder, context)); - CR (y = gp_filesystem_number (fs, folder, filename, context)); - if (fs->folder[x].file[y].info_dirty) { - CR (fs->get_info_func (fs, folder, filename, - &fs->folder[x].file[y].info, + CR ( lookup_folder_file (fs, folder, filename, &f, &file, context)); + + if (file->info_dirty) { + CR (fs->get_info_func (fs, folder, filename, + &file->info, fs->info_data, context)); - fs->folder[x].file[y].info_dirty = 0; + file->info_dirty = 0; } /* @@ -1890,19 +1892,16 @@ gp_filesystem_get_info (CameraFilesystem *fs, const char *folder, * can get it from EXIF data. */ #ifdef HAVE_LIBEXIF - if (!(fs->folder[x].file[y].info.file.fields & GP_FILE_INFO_MTIME)) { + if (!(file->info.file.fields & GP_FILE_INFO_MTIME)) { GP_DEBUG ("Did not get mtime. Trying EXIF information..."); t = gp_filesystem_get_exif_mtime (fs, folder, filename); if (t) { - fs->folder[x].file[y].info.file.mtime = t; - fs->folder[x].file[y].info.file.fields |= - GP_FILE_INFO_MTIME; + file->info.file.mtime = t; + file->info.file.fields |= GP_FILE_INFO_MTIME; } } #endif - - memcpy (info, &fs->folder[x].file[y].info, sizeof (CameraFileInfo)); - + memcpy (info, &file->info, sizeof (CameraFileInfo)); return (GP_OK); } @@ -2042,11 +2041,13 @@ static int gp_filesystem_lru_update (CameraFilesystem *fs, const char *folder, CameraFile *file, GPContext *context) { + CameraFilesystemFolder *f; + CameraFilesystemFile *xfile; CameraFileType type; CameraFile *oldfile = NULL; const char *filename; unsigned long int size; - int x, y; + int x; char cached_images[1024]; CHECK_NULL (fs && folder && file); @@ -2090,24 +2091,23 @@ gp_filesystem_lru_update (CameraFilesystem *fs, const char *folder, "(type %i)...", filename, folder, type); /* Search folder and file */ - CR (x = gp_filesystem_folder_number (fs, folder, context)); - CR (y = gp_filesystem_number (fs, folder, filename, context)); + CR (lookup_folder_file (fs, folder, filename, &f, &xfile, context)); /* * If the file is already in the lru, we first remove it. Note that * we will only remove 'normal', 'raw' and 'audio' from cache. * See gp_filesystem_lru_free. */ - if (fs->folder[x].file[y].lru_prev != NULL) { + if (xfile->lru_prev != NULL) { switch (type) { case GP_FILE_TYPE_NORMAL: - oldfile = fs->folder[x].file[y].normal; + oldfile = xfile->normal; break; case GP_FILE_TYPE_RAW: - oldfile = fs->folder[x].file[y].raw; + oldfile = xfile->raw; break; case GP_FILE_TYPE_AUDIO: - oldfile = fs->folder[x].file[y].audio; + oldfile = xfile->audio; break; case GP_FILE_TYPE_PREVIEW: case GP_FILE_TYPE_EXIF: @@ -2123,25 +2123,25 @@ gp_filesystem_lru_update (CameraFilesystem *fs, const char *folder, fs->lru_size -= size; } - CR (gp_filesystem_lru_remove_one (fs, &fs->folder[x].file[y])); + CR (gp_filesystem_lru_remove_one (fs, xfile)); } /* Then add the file at the end of the LRU. */ if (fs->lru_first == NULL) { - fs->lru_first = &fs->folder[x].file[y]; - fs->lru_last = &fs->folder[x].file[y]; + fs->lru_first = xfile; + fs->lru_last = xfile; /* * For the first item, prev point it itself to show that the * item is in the list. */ - fs->folder[x].file[y].lru_prev = &fs->folder[x].file[y]; + xfile->lru_prev = xfile; } else { - fs->folder[x].file[y].lru_next = NULL; - fs->folder[x].file[y].lru_prev = fs->lru_last; - fs->lru_last->lru_next = &fs->folder[x].file[y]; - fs->lru_last = &fs->folder[x].file[y]; + xfile->lru_next = NULL; + xfile->lru_prev = fs->lru_last; + fs->lru_last->lru_next = xfile; + fs->lru_last = xfile; } CR( gp_file_get_data_and_size (file, NULL, &size)); @@ -2185,7 +2185,7 @@ gp_filesystem_lru_check (CameraFilesystem *fs) /** * \brief Attach file content to a specified file. - * + * * \param fs a #CameraFilesystem * \param folder a folder in the filesystem * \param file a #CameraFile @@ -2203,8 +2203,10 @@ gp_filesystem_set_file_noop (CameraFilesystem *fs, const char *folder, { CameraFileType type; CameraFileInfo info; + CameraFilesystemFolder *f; + CameraFilesystemFile *xfile; const char *filename; - int x, y, r; + int r; time_t t; CHECK_NULL (fs && folder && file); @@ -2217,8 +2219,7 @@ gp_filesystem_set_file_noop (CameraFilesystem *fs, const char *folder, filename, folder, type); /* Search folder and file */ - CR (x = gp_filesystem_folder_number (fs, folder, context)); - CR (y = gp_filesystem_number (fs, folder, filename, context)); + CR (lookup_folder_file (fs, folder, filename, &f, &xfile, context)); /* * If we add a significant amount of data in the cache, we put (or @@ -2235,39 +2236,39 @@ gp_filesystem_set_file_noop (CameraFilesystem *fs, const char *folder, switch (type) { case GP_FILE_TYPE_PREVIEW: - if (fs->folder[x].file[y].preview) - gp_file_unref (fs->folder[x].file[y].preview); - fs->folder[x].file[y].preview = file; + if (xfile->preview) + gp_file_unref (xfile->preview); + xfile->preview = file; gp_file_ref (file); break; case GP_FILE_TYPE_NORMAL: - if (fs->folder[x].file[y].normal) - gp_file_unref (fs->folder[x].file[y].normal); - fs->folder[x].file[y].normal = file; + if (xfile->normal) + gp_file_unref (xfile->normal); + xfile->normal = file; gp_file_ref (file); break; case GP_FILE_TYPE_RAW: - if (fs->folder[x].file[y].raw) - gp_file_unref (fs->folder[x].file[y].raw); - fs->folder[x].file[y].raw = file; + if (xfile->raw) + gp_file_unref (xfile->raw); + xfile->raw = file; gp_file_ref (file); break; case GP_FILE_TYPE_AUDIO: - if (fs->folder[x].file[y].audio) - gp_file_unref (fs->folder[x].file[y].audio); - fs->folder[x].file[y].audio = file; + if (xfile->audio) + gp_file_unref (xfile->audio); + xfile->audio = file; gp_file_ref (file); break; case GP_FILE_TYPE_EXIF: - if (fs->folder[x].file[y].exif) - gp_file_unref (fs->folder[x].file[y].exif); - fs->folder[x].file[y].exif = file; + if (xfile->exif) + gp_file_unref (xfile->exif); + xfile->exif = file; gp_file_ref (file); break; case GP_FILE_TYPE_METADATA: - if (fs->folder[x].file[y].metadata) - gp_file_unref (fs->folder[x].file[y].metadata); - fs->folder[x].file[y].metadata = file; + if (xfile->metadata) + gp_file_unref (xfile->metadata); + xfile->metadata = file; gp_file_ref (file); break; default: @@ -2275,7 +2276,7 @@ gp_filesystem_set_file_noop (CameraFilesystem *fs, const char *folder, return (GP_ERROR); } - /* + /* * If we didn't get a mtime, try to get it from the CameraFileInfo. */ CR (gp_file_get_mtime (file, &t)); @@ -2292,8 +2293,8 @@ gp_filesystem_set_file_noop (CameraFilesystem *fs, const char *folder, * file, check if there is EXIF data in the file that contains * information on the mtime. */ -#ifdef HAVE_LIBEXIF - if (!t && (type == GP_FILE_TYPE_NORMAL)) { +#ifdef HAVE_LIBEXIF + if (!t && (type == GP_FILE_TYPE_NORMAL)) { unsigned long int size; const char *data; @@ -2338,19 +2339,18 @@ int gp_filesystem_set_info_noop (CameraFilesystem *fs, const char *folder, CameraFileInfo info, GPContext *context) { - int x, y; + CameraFilesystemFolder *f; + CameraFilesystemFile *xfile; CHECK_NULL (fs && folder); CC (context); CA (folder, context); /* Search folder and file */ - CR (x = gp_filesystem_folder_number (fs, folder, context)); - CR (y = gp_filesystem_number (fs, folder, info.file.name, context)); - - memcpy (&fs->folder[x].file[y].info, &info, sizeof (CameraFileInfo)); - fs->folder[x].file[y].info_dirty = 0; + CR (lookup_folder_file (fs, folder, info.file.name, &f, &xfile, context)); + memcpy (&xfile->info, &info, sizeof (CameraFileInfo)); + xfile->info_dirty = 0; return (GP_OK); } @@ -2371,22 +2371,23 @@ gp_filesystem_set_info (CameraFilesystem *fs, const char *folder, const char *filename, CameraFileInfo info, GPContext *context) { - int x, y, result, name, e; + int result, name, e; + CameraFilesystemFolder *f; + CameraFilesystemFile *xfile; CHECK_NULL (fs && folder && filename); CC (context); CA (folder, context); if (!fs->set_info_func) { - gp_context_error (context, + gp_context_error (context, _("The filesystem doesn't support setting file " "information")); return (GP_ERROR_NOT_SUPPORTED); } /* Search folder and file */ - CR (x = gp_filesystem_folder_number (fs, folder, context)); - CR (y = gp_filesystem_number (fs, folder, filename, context)); + CR (lookup_folder_file (fs, folder, filename, &f, &xfile, context)); /* Check if people want to set read-only attributes */ if ((info.file.fields & GP_FILE_INFO_TYPE) || @@ -2408,7 +2409,7 @@ gp_filesystem_set_info (CameraFilesystem *fs, const char *folder, } /* - * Set the info. If anything goes wrong, mark info as dirty, + * Set the info. If anything goes wrong, mark info as dirty, * because the operation could have been partially successful. * * Handle name changes in a separate round. @@ -2418,30 +2419,32 @@ gp_filesystem_set_info (CameraFilesystem *fs, const char *folder, result = fs->set_info_func (fs, folder, filename, info, fs->info_data, context); if (result < 0) { - fs->folder[x].file[y].info_dirty = 1; + xfile->info_dirty = 1; return (result); } if (info.file.fields & GP_FILE_INFO_PERMISSIONS) - fs->folder[x].file[y].info.file.permissions = - info.file.permissions; + xfile->info.file.permissions = info.file.permissions; /* Handle name change */ if (name) { - + char *xname; /* Make sure the file does not exist */ e = gp_filesystem_number (fs, folder, info.file.name, context); if (e != GP_ERROR_FILE_NOT_FOUND) return (e); - + info.preview.fields = GP_FILE_INFO_NONE; info.file.fields = GP_FILE_INFO_NAME; info.audio.fields = GP_FILE_INFO_NONE; CR (fs->set_info_func (fs, folder, filename, info, fs->info_data, context)); - strncpy (fs->folder[x].file[y].info.file.name, info.file.name, - sizeof (fs->folder[x].file[y].info.file.name)); - strncpy (fs->folder[x].file[y].name, info.file.name, - sizeof (fs->folder[x].file[y].name)); + strncpy (xfile->info.file.name, info.file.name, + sizeof (xfile->info.file.name)); + xname = strdup(info.file.name); + if (xname) { + free (xfile->name); + xfile->name = xname; + } } return (GP_OK); @@ -2457,7 +2460,7 @@ gp_filesystem_set_info (CameraFilesystem *fs, const char *folder, * This function is only called from gp_camera_get_storageinfo(). You may * want to make sure this information is consistent with the information on * gp_camera_get_storageinfo(). - * + * * Retrieves the storage information, like maximum and free space, for * the specified filesystem, if supported by the device. The storage * information is returned in an newly allocated array of @@ -2487,7 +2490,7 @@ gp_filesystem_get_storageinfo ( "information")); return (GP_ERROR_NOT_SUPPORTED); } - return fs->storage_info_func (fs, + return fs->storage_info_func (fs, storageinfo, nrofstorageinfos, fs->info_data, context); } |