/** \file
*
* \author Copyright 2000 Scott Fritzinger
*
* \note
* 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.
*
* \note
* 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.
*
* \note
* 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., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*
* \note
* This file contains internal functions. Use of these functions from
* external software modules is considered deprecated.
*/
#define _POSIX_SOURCE
#define _DEFAULT_SOURCE
#define _DARWIN_C_SOURCE
#include "config.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define CHECK_RESULT(result) {int r = (result); if (r < 0) return (r);}
/* lengt of one path component */
#ifndef MAX_PATH
# define MAX_PATH 256
#endif
/*! The internals of the CameraFile struct are private.
* \internal
*/
struct _CameraFile {
char mime_type [64];
char name [MAX_PATH];
int ref_count;
time_t mtime;
CameraFileAccessType accesstype;
/* for GP_FILE_ACCESSTYPE_MEMORY files */
unsigned long size;
unsigned char *data;
unsigned long offset; /* read pointer */
/* for GP_FILE_ACCESSTYPE_FD files */
int fd;
/* for GP_FILE_ACCESSTYPE_HANDLER files */
CameraFileHandler*handler;
void *private;
};
/*! Create new #CameraFile object.
*
* \param file a pointer to a #CameraFile
* \return a gphoto2 error code.
*/
int
gp_file_new (CameraFile **file)
{
C_PARAMS (file);
C_MEM (*file = calloc (1, sizeof (CameraFile)));
strcpy ((*file)->mime_type, "unknown/unknown");
(*file)->ref_count = 1;
(*file)->accesstype = GP_FILE_ACCESSTYPE_MEMORY;
return (GP_OK);
}
/*! Create new #CameraFile object from a UNIX filedescriptor.
*
* This function takes ownership of the filedescriptor and will close it when closing the CameraFile.
*
* \param file a pointer to a #CameraFile
* \param fd a UNIX filedescriptor
* \return a gphoto2 error code.
*/
int
gp_file_new_from_fd (CameraFile **file, int fd)
{
C_PARAMS (file);
C_MEM (*file = calloc (1, sizeof (CameraFile)));
strcpy ((*file)->mime_type, "unknown/unknown");
(*file)->ref_count = 1;
(*file)->accesstype = GP_FILE_ACCESSTYPE_FD;
(*file)->fd = fd;
return (GP_OK);
}
/*! Create new #CameraFile object using a programmatic handler.
*
* \param file a pointer to a #CameraFile
* \param handler a #CameraFileHandler
* \param private a private pointer for frontend use
* \return a gphoto2 error code.
*/
int
gp_file_new_from_handler (CameraFile **file, CameraFileHandler* handler, void*private)
{
C_PARAMS (file);
C_MEM (*file = calloc (1, sizeof (CameraFile)));
strcpy ((*file)->mime_type, "unknown/unknown");
(*file)->ref_count = 1;
(*file)->accesstype = GP_FILE_ACCESSTYPE_HANDLER;
(*file)->handler = handler;
(*file)->private = private;
return (GP_OK);
}
/*! \brief descruct a #CameraFile object.
* @param file a #CameraFile
* @return a gphoto2 error code.
*
**/
int gp_file_free (CameraFile *file)
{
C_PARAMS (file);
CHECK_RESULT (gp_file_clean (file));
if (file->accesstype == GP_FILE_ACCESSTYPE_FD)
close (file->fd);
free (file);
return (GP_OK);
}
/*! \brief Increase reference counter for #CameraFile object
*
* \param file a #CameraFile
* \return a gphoto2 error code.
*/
int
gp_file_ref (CameraFile *file)
{
C_PARAMS (file);
file->ref_count += 1;
return (GP_OK);
}
/*! \brief Decrease reference counter for #CameraFile object
*
* \param file a #CameraFile
* \return a gphoto2 error code.
*
**/
int
gp_file_unref (CameraFile *file)
{
C_PARAMS (file);
file->ref_count -= 1;
if (file->ref_count == 0)
CHECK_RESULT (gp_file_free (file));
return (GP_OK);
}
/**
* @param file a #CameraFile
* @param data
* @param size
* @return a gphoto2 error code.
*
**/
int
gp_file_append (CameraFile *file, const char *data,
unsigned long int size)
{
C_PARAMS (file);
switch (file->accesstype) {
case GP_FILE_ACCESSTYPE_MEMORY:
C_MEM (file->data = realloc (file->data, sizeof (char) * (file->size + size)));
memcpy (&file->data[file->size], data, size);
file->size += size;
break;
case GP_FILE_ACCESSTYPE_FD: {
unsigned long int curwritten = 0;
while (curwritten < size) {
ssize_t res = write (file->fd, data+curwritten, size-curwritten);
if (res == -1) {
GP_LOG_E ("Encountered error %d writing to fd.", errno);
return GP_ERROR_IO_WRITE;
}
if (!res) { /* no progress is bad too */
GP_LOG_E ("Encountered 0 bytes written to fd.");
return GP_ERROR_IO_WRITE;
}
curwritten += res;
}
break;
}
case GP_FILE_ACCESSTYPE_HANDLER: {
uint64_t xsize = size;
/* FIXME: assume we write one blob */
C_PARAMS (file->handler->write);
return file->handler->write (file->private, (unsigned char*)data, &xsize);
}
default:
GP_LOG_E ("Unknown file access type %d", file->accesstype);
return GP_ERROR;
}
return (GP_OK);
}
/**
* @param file a #CameraFile
* @param data
* @param size
* @return a gphoto2 error code.
*
* Internal.
**/
int
gp_file_slurp (CameraFile *file, char *data,
size_t size, size_t *readlen
) {
C_PARAMS (file);
switch (file->accesstype) {
case GP_FILE_ACCESSTYPE_MEMORY:
if (size > file->size-file->offset)
size = file->size - file->offset;
memcpy (data, &file->data[file->offset], size);
file->offset += size;
if (readlen) *readlen = size;
break;
case GP_FILE_ACCESSTYPE_FD: {
unsigned long int curread = 0;
while (curread < size) {
ssize_t res = read (file->fd, data+curread, size-curread);
if (res == -1) {
GP_LOG_E ("Encountered error %d reading from fd.", errno);
return GP_ERROR_IO_READ;
}
if (!res) { /* no progress is bad too */
GP_LOG_E ("Encountered 0 bytes reading from fd.");
return GP_ERROR_IO_READ;
}
curread += res;
if (readlen)
*readlen = curread;
}
break;
}
case GP_FILE_ACCESSTYPE_HANDLER: {
uint64_t xsize = size;
int ret;
C_PARAMS (file->handler->read);
ret = file->handler->read (file->private, (unsigned char*)data, &xsize);
*readlen = xsize;
if (ret != GP_OK)
GP_LOG_E ("File handler read returned %d", ret);
return ret;
}
default:
GP_LOG_E ("Unknown file access type %d", file->accesstype);
return GP_ERROR;
}
return (GP_OK);
}
/**
* @param file a #CameraFile
* @param data
* @param size
* @return a gphoto2 error code.
*
**/
int
gp_file_set_data_and_size (CameraFile *file, char *data,
unsigned long int size)
{
C_PARAMS (file);
switch (file->accesstype) {
case GP_FILE_ACCESSTYPE_MEMORY:
free (file->data);
file->data = (unsigned char*)data;
file->size = size;
break;
case GP_FILE_ACCESSTYPE_FD: {
unsigned int curwritten = 0;
/* truncate */
if (-1 == lseek (file->fd, 0, SEEK_SET)) {
GP_LOG_E ("Encountered error %d lseeking to 0.", errno);
/* might happen on pipes ... just ignore it */
}
if (-1 == ftruncate (file->fd, 0)) {
GP_LOG_E ("Encountered error %d ftruncating to 0.", errno);
/* might happen on pipes ... just ignore it */
}
while (curwritten < size) {
ssize_t res = write (file->fd, data+curwritten, size-curwritten);
if (res == -1) {
GP_LOG_E ("Encountered error %d writing to fd.", errno);
return GP_ERROR_IO_WRITE;
}
if (!res) { /* no progress is bad too */
GP_LOG_E ("Encountered 0 bytes written to fd.");
return GP_ERROR_IO_WRITE;
}
curwritten += res;
}
/* This function takes over the responsibility for "data", aka
* it has to free it. So we do.
*/
free (data);
break;
}
case GP_FILE_ACCESSTYPE_HANDLER: {
uint64_t xsize = size;
int ret;
C_PARAMS (file->handler->write);
/* FIXME: handle multiple blob writes */
ret = file->handler->write (file->private, (unsigned char*)data, &xsize);
if (ret != GP_OK) {
GP_LOG_E ("Handler data() returned %d", ret);
return ret;
}
/* This function takes over the responsibility for "data", aka
* it has to free it. So we do.
*/
free (data);
return GP_OK;
}
default:
GP_LOG_E ("Unknown file access type %d", file->accesstype);
return GP_ERROR;
}
return (GP_OK);
}
/**
* Get a pointer to the data and the file's size.
*
* @param file a #CameraFile
* @param data
* @param size
* @return a gphoto2 error code.
*
* Both data and size can be NULL and will then be ignored.
*
* For regular CameraFiles, the pointer to data that is returned is
* still owned by libgphoto2 and its lifetime is the same as the #file.
*
* For filedescriptor or handler based CameraFile types, the returned
* data pointer is owned by the caller and needs to be free()d to avoid
* memory leaks.
*
**/
int
gp_file_get_data_and_size (CameraFile *file, const char **data,
unsigned long int *size)
{
C_PARAMS (file);
switch (file->accesstype) {
case GP_FILE_ACCESSTYPE_MEMORY:
if (data)
*data = (char*)file->data;
if (size)
*size = file->size;
break;
case GP_FILE_ACCESSTYPE_FD: {
off_t offset;
off_t curread = 0;
if (-1 == lseek (file->fd, 0, SEEK_END)) {
if (errno == EBADF) return GP_ERROR_IO;
/* Might happen for pipes or sockets. Umm. Hard. */
/* FIXME */
}
if (-1 == (offset = lseek (file->fd, 0, SEEK_CUR))) {
/* should not happen if we passed the above case */
GP_LOG_E ("Encountered error %d lseekin to CUR.", errno);
return GP_ERROR_IO_READ;
}
if (-1 == lseek (file->fd, 0, SEEK_SET)) {
/* should not happen if we passed the above cases */
GP_LOG_E ("Encountered error %d lseekin to CUR.", errno);
return GP_ERROR_IO_READ;
}
if (size) *size = offset;
if (!data) /* just the size... */
return GP_OK;
C_MEM (*data = malloc (offset));
while (curread < offset) {
ssize_t res = read (file->fd, (char*)((*data)+curread), offset-curread);
if (res == -1) {
free ((char*)*data);
GP_LOG_E ("Encountered error %d reading.", errno);
return GP_ERROR_IO_READ;
}
if (res == 0) {
free ((char*)*data);
GP_LOG_E ("No progress during reading.");
return GP_ERROR_IO_READ;
}
curread += res;
}
break;
}
case GP_FILE_ACCESSTYPE_HANDLER: {
uint64_t xsize = 0;
int ret;
C_PARAMS (file->handler->read);
ret = file->handler->size (file->private, &xsize);
if (ret != GP_OK) {
GP_LOG_E ("Encountered error %d querying size().", ret);
return ret;
}
if (size) *size = xsize;
if (!data) /* just the size... */
return GP_OK;
C_MEM (*data = malloc (xsize));
ret = file->handler->read (file->private, (unsigned char*)*data, &xsize);
if (ret != GP_OK) {
GP_LOG_E ("Encountered error %d getting data().", ret);
free ((char*)*data);
*data = NULL;
}
return ret;
}
default:
GP_LOG_E ("Unknown file access type %d", file->accesstype);
return GP_ERROR;
}
return (GP_OK);
}
/**
* @param file a #CameraFile
* @param filename
* @return a gphoto2 error code.
*
**/
int
gp_file_save (CameraFile *file, const char *filename)
{
FILE *fp;
struct utimbuf u;
C_PARAMS (file && filename);
switch (file->accesstype) {
case GP_FILE_ACCESSTYPE_MEMORY:
if (!(fp = fopen (filename, "wb")))
return GP_ERROR;
if (fwrite (file->data, (size_t)sizeof(char), (size_t)file->size, fp) != (size_t)file->size) {
GP_LOG_E ("Not enough space on device in order to save '%s'.", filename);
fclose (fp);
unlink (filename);
return GP_ERROR;
}
fclose (fp);
break;
case GP_FILE_ACCESSTYPE_FD: {
char *data;
off_t curread = 0;
off_t offset;
if (-1 == lseek (file->fd, 0, SEEK_END))
return GP_ERROR_IO;
if (-1 == (offset = lseek (file->fd, 0, SEEK_CUR))) {
/* should not happen if we passed the above case */
GP_LOG_E ("Encountered error %d lseekin to CUR.", errno);
return GP_ERROR_IO_READ;
}
if (-1 == lseek (file->fd, 0, SEEK_SET)) {
/* should not happen if we passed the above case */
GP_LOG_E ("Encountered error %d lseekin to BEGIN.", errno);
return GP_ERROR_IO_READ;
}
C_MEM (data = malloc(65536));
if (!(fp = fopen (filename, "wb"))) {
free (data);
return GP_ERROR;
}
while (curread < offset) {
int toread, res;
toread = 65536;
if (toread > (offset-curread))
toread = offset-curread;
res = read (file->fd, data, toread);
if (res <= 0) {
free (data);
fclose (fp);
unlink (filename);
return GP_ERROR_IO_READ;
}
if ((int)fwrite (data, 1, res, fp) != res) {
GP_LOG_E ("Not enough space on device in order to save '%s'.", filename);
free (data);
fclose (fp);
unlink (filename);
return GP_ERROR;
}
curread += res;
}
free (data);
fclose (fp);
break;
}
default:
GP_LOG_E ("Unknown file access type %d", file->accesstype);
return GP_ERROR;
}
if (file->mtime) {
u.actime = file->mtime;
u.modtime = file->mtime;
utime (filename, &u);
}
return (GP_OK);
}
/*
* mime types that cannot be determined by the filename
* extension. Better hack would be to use library that examine
* file content instead, like gnome-vfs mime handling, or
* gnome-mime, whatever.
* See also the GP_MIME_* definitions.
*/
static const char *mime_table[] = {
"bmp", GP_MIME_BMP,
"jpg", GP_MIME_JPEG,
"tif", GP_MIME_TIFF,
"ppm", GP_MIME_PPM,
"pgm", GP_MIME_PGM,
"pnm", GP_MIME_PNM,
"png", GP_MIME_PNG,
"wav", GP_MIME_WAV,
"avi", GP_MIME_AVI,
"mp3", GP_MIME_MP3,
"wma", GP_MIME_WMA,
"asf", GP_MIME_ASF,
"ogg", GP_MIME_OGG,
"mpg", GP_MIME_MPEG,
"raw", GP_MIME_RAW,
"mts", GP_MIME_AVCHD,
"m2ts", GP_MIME_AVCHD,
"arw", GP_MIME_ARW,
"txt", GP_MIME_TXT,
NULL};
/**
* @param file a #CameraFile
* @param filename
* @return a gphoto2 error code.
*
**/
int
gp_file_open (CameraFile *file, const char *filename)
{
FILE *fp;
char *name, *dot;
long size, size_read;
int i;
struct stat s;
C_PARAMS (file && filename);
CHECK_RESULT (gp_file_clean (file));
fp = fopen(filename, "r");
if (!fp)
return (GP_ERROR);
fseek (fp, 0, SEEK_END);
size = ftell (fp);
rewind (fp);
switch (file->accesstype) {
case GP_FILE_ACCESSTYPE_MEMORY:
file->data = malloc (sizeof(char)*(size + 1));
if (!file->data) {
fclose (fp);
return (GP_ERROR_NO_MEMORY);
}
size_read = fread (file->data, (size_t)sizeof(char), (size_t)size, fp);
if (ferror(fp)) {
gp_file_clean (file);
fclose (fp);
return (GP_ERROR);
}
fclose(fp);
file->size = size_read;
file->data[size_read] = 0;
break;
case GP_FILE_ACCESSTYPE_FD: {
if (file->fd == -1) {
file->fd = dup(fileno(fp));
fclose (fp);
break;
}
GP_LOG_E ("Needs to be initialized with fd=-1 to work");
fclose (fp);
return GP_ERROR;
}
default:
break;
}
name = strrchr (filename, '/');
if (name)
strncpy (file->name, name + 1, sizeof (file->name));
else
strncpy (file->name, filename, sizeof (file->name));
/* MIME lookup */
dot = strrchr (filename, '.');
if (dot) {
for (i = 0; mime_table[i] ; i+=2)
if (!strcasecmp (mime_table[i], dot+1)) {
strncpy (file->mime_type, mime_table[i+1], sizeof(file->mime_type));
break;
}
if (!mime_table[i])
/*
* We did not found the type in the lookup table,
* so we use the file suffix as mime type.
* Note: This should probably use GP_MIME_UNKNOWN instead
* of returning a non-standard type.
*/
sprintf(file->mime_type, "image/%s", dot + 1);
} else
/*
* Damn, no filename suffix...
*/
strncpy (file->mime_type, GP_MIME_UNKNOWN,
sizeof (file->mime_type));
if (stat (filename, &s) != -1) {
file->mtime = s.st_mtime;
} else {
file->mtime = time (NULL);
}
return (GP_OK);
}
/**
* @param file a #CameraFile
* @return a gphoto2 error code.
*
**/
int
gp_file_clean (CameraFile *file)
{
/*
* Frees a CameraFile's components, not the CameraFile itself.
* This is used to prep a CameraFile struct to be filled.
*/
C_PARAMS (file);
switch (file->accesstype) {
case GP_FILE_ACCESSTYPE_MEMORY:
free (file->data);
file->data = NULL;
file->size = 0;
break;
case GP_FILE_ACCESSTYPE_FD:
break;
default:break;
}
strcpy (file->name, "");
return (GP_OK);
}
/**
* @param destination a #CameraFile
* @param source a #CameraFile
* @return a gphoto2 error code.
*
**/
int
gp_file_copy (CameraFile *destination, CameraFile *source)
{
C_PARAMS (destination && source);
GP_LOG_D ("Copying '%s' onto '%s'...", source->name, destination->name);
/* struct members we can just copy. All generic ones, but not refcount. */
memcpy (destination->name, source->name, sizeof (source->name));
memcpy (destination->mime_type, source->mime_type, sizeof (source->mime_type));
destination->mtime = source->mtime;
if ((destination->accesstype == GP_FILE_ACCESSTYPE_MEMORY) &&
(source->accesstype == GP_FILE_ACCESSTYPE_MEMORY)) {
free (destination->data);
destination->data = NULL;
destination->size = source->size;
C_MEM (destination->data = malloc (sizeof (char) * source->size));
memcpy (destination->data, source->data, source->size);
return (GP_OK);
}
if ( (destination->accesstype == GP_FILE_ACCESSTYPE_MEMORY) &&
(source->accesstype == GP_FILE_ACCESSTYPE_FD)
) {
off_t offset;
off_t curread = 0;
free (destination->data);
destination->data = NULL;
if (-1 == lseek (source->fd, 0, SEEK_END)) {
if (errno == EBADF) return GP_ERROR_IO;
/* Might happen for pipes or sockets. Umm. Hard. */
/* FIXME */
}
if (-1 == (offset = lseek (source->fd, 0, SEEK_CUR))) {
/* should not happen if we passed the above case */
GP_LOG_E ("Encountered error %d lseekin to CUR.", errno);
return GP_ERROR_IO_READ;
}
if (-1 == lseek (source->fd, 0, SEEK_SET)) {
/* should not happen if we passed the above cases */
GP_LOG_E ("Encountered error %d lseekin to CUR.", errno);
return GP_ERROR_IO_READ;
}
destination->size = offset;
C_MEM (destination->data = malloc (offset));
while (curread < offset) {
ssize_t res = read (source->fd, destination->data+curread, offset-curread);
if (res == -1) {
free (destination->data);
GP_LOG_E ("Encountered error %d reading.", errno);
return GP_ERROR_IO_READ;
}
if (res == 0) {
free (destination->data);
GP_LOG_E ("No progress during reading.");
return GP_ERROR_IO_READ;
}
curread += res;
}
return GP_OK;
}
if ( (destination->accesstype == GP_FILE_ACCESSTYPE_FD) &&
(source->accesstype == GP_FILE_ACCESSTYPE_FD)
) {
char *data;
lseek (destination->fd, 0, SEEK_SET);
if (-1 == ftruncate (destination->fd, 0))
perror("ftruncate");
lseek (source->fd, 0, SEEK_SET);
C_MEM (data = malloc (65536));
while (1) {
ssize_t curwritten = 0;
ssize_t res;
res = read (source->fd, data, 65536);
if (res == -1) {
free (data);
return GP_ERROR_IO_READ;
}
if (res == 0)
break;
while (curwritten < res) {
ssize_t res2 = write (destination->fd, data+curwritten, res-curwritten);
if (res2 == -1) {
free (data);
return GP_ERROR_IO_WRITE;
}
if (res2 == 0)
break;
curwritten += res2;
}
if (res < 65536) /* end of file */
break;
}
free (data);
return GP_OK;
}
if ( (destination->accesstype == GP_FILE_ACCESSTYPE_FD) &&
(source->accesstype == GP_FILE_ACCESSTYPE_MEMORY)
) {
unsigned long curwritten = 0;
while (curwritten < source->size) {
int res = write (destination->fd, source->data+curwritten, source->size-curwritten);
if (res == -1)
return GP_ERROR_IO_WRITE;
if (!res) /* no progress? */
return GP_ERROR_IO_WRITE;
curwritten += res;
}
return GP_OK;
}
if ( (destination->accesstype == GP_FILE_ACCESSTYPE_HANDLER) &&
(source->accesstype == GP_FILE_ACCESSTYPE_MEMORY)
) {
uint64_t xsize = source->size;
unsigned long curwritten = 0;
destination->handler->size (destination->private, &xsize);
while (curwritten < source->size) {
uint64_t tmpsize = source->size - curwritten;
int res = destination->handler->write (destination->private, source->data+curwritten, &tmpsize);
if (res < GP_OK)
return res;
if (!tmpsize) /* no progress? */
return GP_ERROR_IO_WRITE;
curwritten += tmpsize;
}
return GP_OK;
}
GP_LOG_E ("Unhandled cases in gp_copy_file. Bad!");
return GP_ERROR;
}
/**
* @param file a #CameraFile
* @param name a pointer to a name string
* @return a gphoto2 error code.
*
**/
int
gp_file_get_name (CameraFile *file, const char **name)
{
C_PARAMS (file && name);
*name = file->name;
return (GP_OK);
}
/**
* @param file a #CameraFile
* @param basename the basename of the file
* @param type the gphoto type of the file
* @param newname the new name generated
* @return a gphoto2 error code.
*
* This function takes the basename and generates a filename out of
* it depending on the gphoto filetype and the mime type in the file.
* The gphoto filetype will be converted to a prefix, like
* thumb_ or raw_, the mimetype will replace the current suffix by
* a different one (if necessary).
*
* This can be used so that saving thumbnails or metadata will not
* overwrite the normal files.
**/
int
gp_file_get_name_by_type (CameraFile *file, const char *basename, CameraFileType type, char **newname)
{
char *prefix = NULL, *s, *new, *slash = NULL;
const char *suffix = NULL;
int i;
C_PARAMS (file && basename && newname);
*newname = NULL;
/* the easy case, always map 1:1, if it has a suffix already. */
if ((type == GP_FILE_TYPE_NORMAL) && strchr(basename,'.')) {
C_MEM (*newname = strdup (basename));
return GP_OK;
}
for (i=0;mime_table[i];i+=2) {
if (!strcmp (mime_table[i+1],file->mime_type)) {
suffix = mime_table[i];
break;
}
}
s = strrchr(basename,'.');
slash = strrchr(basename,'/');
if (slash > s) /* --filename foo.bar/foo */
s = NULL;
switch (type) {
case GP_FILE_TYPE_RAW: prefix = "raw_";break;
case GP_FILE_TYPE_EXIF: prefix = "exif_";break;
case GP_FILE_TYPE_PREVIEW: prefix = "thumb_";break;
case GP_FILE_TYPE_METADATA: prefix = "meta_";break;
case GP_FILE_TYPE_AUDIO: prefix = "audio_";break;
default: prefix = ""; break;
}
if (s) {
int xlen;
if (!suffix)
suffix = s+1;
C_MEM (new = calloc (strlen(prefix) + (s-basename+1) + strlen (suffix) + 1, 1));
xlen = strlen (prefix);
if (slash) {
memcpy (new, basename, slash-basename+1);
strcat (new, prefix);
memcpy (new + strlen (new), slash+1, s-slash+1);
} else {
strcpy (new, prefix);
memcpy (new + strlen (new), basename, s-basename+1);
}
new[xlen+(s-basename)+1]='\0';
strcat (new, suffix);
} else { /* no dot in basename? */
if (!suffix) suffix = "";
C_MEM (new = calloc (strlen(prefix) + strlen(basename) + 1 + strlen (suffix) + 1, 1));
if (slash) {
memcpy (new, basename, slash-basename+1); /* with / */
strcat (new, prefix);
strcat (new, slash+1);
} else {
strcpy (new, prefix);
strcat (new, basename);
}
if (strlen(suffix)) {
strcat (new, ".");
strcat (new, suffix);
}
}
*newname = new;
return (GP_OK);
}
/**
* @param file a #CameraFile
* @param mime_type a pointer to a MIME type string
* @return a gphoto2 error code.
*
**/
int
gp_file_get_mime_type (CameraFile *file, const char **mime_type)
{
C_PARAMS (file && mime_type);
*mime_type = file->mime_type;
return (GP_OK);
}
/**
* @param file a #CameraFile
* @param name a pointer to a MIME type string
* @return a gphoto2 error code.
*
**/
int
gp_file_set_name (CameraFile *file, const char *name)
{
C_PARAMS (file && name);
strncpy (file->name, name, sizeof (file->name));
return (GP_OK);
}
/**
* @param file a #CameraFile
* @param mime_type a MIME type string
* @return a gphoto2 error code.
*
**/
int
gp_file_set_mime_type (CameraFile *file, const char *mime_type)
{
C_PARAMS (file && mime_type);
strncpy (file->mime_type, mime_type, sizeof (file->mime_type));
return (GP_OK);
}
/**
* @param file a #CameraFile
* @return a gphoto2 error code.
*
**/
int
gp_file_detect_mime_type (CameraFile *file)
{
const char TIFF_SOI_MARKER[] = {(char) 0x49, (char) 0x49, (char) 0x2A,
(char) 0x00, (char) 0x08, '\0' };
const char JPEG_SOI_MARKER[] = {(char) 0xFF, (char) 0xD8, '\0' };
C_PARAMS (file);
switch (file->accesstype) {
case GP_FILE_ACCESSTYPE_MEMORY:
/* image/tiff */
if ((file->size >= 5) && !memcmp (file->data, TIFF_SOI_MARKER, 5))
CHECK_RESULT (gp_file_set_mime_type (file, GP_MIME_TIFF))
/* image/jpeg */
else if ((file->size >= 2) && !memcmp (file->data, JPEG_SOI_MARKER, 2))
CHECK_RESULT (gp_file_set_mime_type (file, GP_MIME_JPEG))
else
CHECK_RESULT (gp_file_set_mime_type (file, GP_MIME_RAW));
return GP_OK;
case GP_FILE_ACCESSTYPE_FD: {
char data[5];
off_t offset;
int res;
offset = lseek (file->fd, 0, SEEK_SET);
res = read (file->fd, data, sizeof(data));
if (res == -1)
return GP_ERROR_IO_READ;
/* image/tiff */
if ((res >= 5) && !memcmp (data, TIFF_SOI_MARKER, 5))
CHECK_RESULT (gp_file_set_mime_type (file, GP_MIME_TIFF))
/* image/jpeg */
else if ((res >= 2) && !memcmp (data, JPEG_SOI_MARKER, 2))
CHECK_RESULT (gp_file_set_mime_type (file, GP_MIME_JPEG))
else
CHECK_RESULT (gp_file_set_mime_type (file, GP_MIME_RAW));
lseek (file->fd, offset, SEEK_SET);
break;
}
default:
break;
}
return GP_OK;
}
/**
* @param file a #CameraFile
* @return a gphoto2 error code.
*
**/
int
gp_file_adjust_name_for_mime_type (CameraFile *file)
{
int x;
char *suffix;
const char *table[] = {
GP_MIME_RAW, "raw",
GP_MIME_JPEG, "jpg",
GP_MIME_PNG, "png",
GP_MIME_PPM, "ppm",
GP_MIME_PGM, "pgm",
GP_MIME_PNM, "pnm",
GP_MIME_TIFF, "tif",
GP_MIME_WAV, "wav",
GP_MIME_BMP, "bmp",
GP_MIME_AVI, "avi",
GP_MIME_CRW, "crw",
GP_MIME_CR2, "cr2",
GP_MIME_CR3, "cr3",
GP_MIME_ARW, "arw",
GP_MIME_NEF, "nef",
GP_MIME_TXT, "txt",
NULL};
C_PARAMS (file);
GP_LOG_D ("Adjusting file name for mime type '%s'...", file->mime_type);
for (x = 0; table[x]; x += 2)
if (!strcmp (file->mime_type, table[x])) {
/* Search the current suffix and erase it */
suffix = strrchr (file->name, '.');
if (suffix++)
*suffix = '\0';
strcat (file->name, table[x + 1]);
break;
}
GP_LOG_D ("Name adjusted to '%s'.", file->name);
return (GP_OK);
}
/**
* @param file a #CameraFile
* @param mtime
* @return a gphoto2 error code.
*
**/
int
gp_file_get_mtime (CameraFile *file, time_t *mtime)
{
C_PARAMS (file && mtime);
*mtime = file->mtime;
return (GP_OK);
}
/**
* @param file a #CameraFile
* @param mtime
* @return a gphoto2 error code.
*
**/
int
gp_file_set_mtime (CameraFile *file, time_t mtime)
{
C_PARAMS (file);
file->mtime = mtime;
return (GP_OK);
}