1213 lines
30 KiB
C
1213 lines
30 KiB
C
/*
|
|
cole - A free C OLE library.
|
|
Copyright 1998, 1999 Roberto Arturo Tena Sanchez
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
/*
|
|
Arturo Tena <arturo@directmail.org>
|
|
*/
|
|
|
|
#if !(defined( __BORLANDC__ ) || defined( __WIN32__ ))
|
|
#include "config.h"
|
|
#include "cole.h"
|
|
#else
|
|
#include "cole.h.in"
|
|
#endif
|
|
|
|
#include "internal.h"
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
/**
|
|
* cole_perror:
|
|
* @s: string to print before the error message. It can be NULL.
|
|
* @colerrno: error value of which a message will be printed.
|
|
*
|
|
* Prints a message on the standard error output, describing the error
|
|
* @colerrno, preceding it with the string @s, a semicolon and a space.
|
|
* It handles COLE_EERRNO value too, calling perror(3).
|
|
*/
|
|
void
|
|
cole_perror (const char *s, COLERRNO colerrno)
|
|
{
|
|
if (s != NULL) fprintf (stderr, "%s: ", s);
|
|
switch (colerrno) {
|
|
case COLE_EMEMORY:
|
|
case COLE_ECLOSEFILE:
|
|
case COLE_EWRITE:
|
|
case COLE_EREMOVE:
|
|
case COLE_ETMPNAM:
|
|
case COLE_ESEEK:
|
|
case COLE_EERRNO:
|
|
perror ("cole");
|
|
break;
|
|
case COLE_EOPENFILE:
|
|
fprintf (stderr, "cole - Cannot open the file\n");
|
|
exit(1);
|
|
break;
|
|
case COLE_ENOFILESYSTEM:
|
|
fprintf (stderr, "cole - The file is not a OLE2 file\n");
|
|
break;
|
|
case COLE_EINVALIDFILESYSTEM:
|
|
fprintf (stderr, "cole - The file has a short OLE2 header or it is not really an OLE2 file.\n");
|
|
break;
|
|
case COLE_EISNOTDIR:
|
|
fprintf (stderr, "cole - The OLE2 entry is not a substorage object\n");
|
|
break;
|
|
case COLE_EISNOTFILE:
|
|
fprintf (stderr, "cole - The substorage object is not valid\n");
|
|
break;
|
|
case COLE_EFILENOTFOUND:
|
|
fprintf (stderr, "cole - OLE2 object not found\n");
|
|
break;
|
|
case COLE_EOF:
|
|
fprintf (stderr, "cole - End of stream object has been reached\n");
|
|
break;
|
|
case COLE_EMEMBERISNOTDIR:
|
|
fprintf (stderr, "cole - The OLE2 object searched for is not "
|
|
"a substorage object\n");
|
|
break;
|
|
case COLE_EBROKENFILENAME:
|
|
fprintf (stderr, "cole - Illegal OLE object name\n");
|
|
break;
|
|
case COLE_EFILENAMEISNOTFILE:
|
|
fprintf (stderr, "cole - The OLE2 object is not a stream\n");
|
|
break;
|
|
case COLE_EFSEEKDELTA:
|
|
fprintf (stderr, "cole - The seek offset is an illegal value\n");
|
|
break;
|
|
case COLE_EFSEEKFLAG:
|
|
fprintf (stderr, "cole - The Seek Flag is not valid\n");
|
|
break;
|
|
case COLE_EREAD:
|
|
fprintf (stderr, "cole - Short read length returned...the file is probably corrupted\n");
|
|
break;
|
|
case COLE_EUNKNOWN:
|
|
fprintf (stderr, "cole - An unknown error ocurred in the cole libary (might be a bug)\n");
|
|
break;
|
|
default:
|
|
fprintf (stderr, "cole - An unknown error %d ocurred in the cole libabry (might be a bug)\n",
|
|
colerrno);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* cole_mount:
|
|
* @filename: name of the file with the filesystem.
|
|
* @colerrno: error value (COLE_EMEMORY, COLE_EOPENFILE, COLE_ENOFILESYSTEM,
|
|
* COLE_EINVALIDFILESYSTEM, COLE_EUNKNOWN).
|
|
*
|
|
* Mounts the filesystem which is in @filename.
|
|
*
|
|
* Returns: a filesystem in success, or NULL in other case.
|
|
*/
|
|
COLEFS *
|
|
cole_mount (char *filename, COLERRNO *colerrno)
|
|
{
|
|
COLEFS * ret;
|
|
|
|
ret = (COLEFS *)malloc (sizeof (COLEFS));
|
|
if (ret == NULL) {
|
|
if (colerrno != NULL) *colerrno = COLE_EMEMORY;
|
|
return NULL;
|
|
}
|
|
|
|
switch (__OLEdecode (filename, &ret->tree, &ret->root, &ret->BDepot,
|
|
&ret->SDepot, &ret->sbfile, &ret->sbfilename,
|
|
&ret->file, 0)) {
|
|
case 0:
|
|
/* success */
|
|
break;
|
|
case 10:
|
|
if (colerrno != NULL) *colerrno = COLE_EMEMORY;
|
|
free (ret);
|
|
return NULL;
|
|
case 7:
|
|
case 4:
|
|
if (colerrno != NULL) *colerrno = COLE_EOPENFILE;
|
|
free (ret);
|
|
return NULL;
|
|
case 8:
|
|
case 9:
|
|
if (colerrno != NULL) *colerrno = COLE_ENOFILESYSTEM;
|
|
free (ret);
|
|
return NULL;
|
|
case 5:
|
|
if (colerrno != NULL) *colerrno = COLE_EINVALIDFILESYSTEM;
|
|
free (ret);
|
|
return NULL;
|
|
default:
|
|
if (colerrno != NULL) *colerrno = COLE_EUNKNOWN;
|
|
free (ret);
|
|
return NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* cole_umount:
|
|
* @colefilesystem: filesystem to umount.
|
|
* @colerrno: error value (COLE_ECLOSEFILE, COLE_EREMOVE).
|
|
*
|
|
* Umounts the filesystem @colefilesystem.
|
|
*
|
|
* Returns: zero in success, no zero in other case.
|
|
*/
|
|
int
|
|
cole_umount (COLEFS *colefilesystem, COLERRNO *colerrno)
|
|
{
|
|
int ret;
|
|
|
|
ret = 0;
|
|
free (colefilesystem->BDepot);
|
|
free (colefilesystem->tree);
|
|
if (fclose (colefilesystem->file) && !ret) {
|
|
if (colerrno != NULL) *colerrno = COLE_ECLOSEFILE;
|
|
ret = 1;
|
|
}
|
|
if (colefilesystem->SDepot != NULL) {
|
|
free (colefilesystem->SDepot);
|
|
/* may no exist SDepot because there are not small files */
|
|
/* assert (colefilesystem->sbfile != NULL); */
|
|
/* assert (colefilesystem->sbfilename != NULL); */
|
|
if (fclose (colefilesystem->sbfile) && !ret) {
|
|
if (colerrno != NULL) *colerrno = COLE_ECLOSEFILE;
|
|
ret = 1;
|
|
}
|
|
#if defined(__WIN32__) || (__BORLANDC__)
|
|
if (remove (colefilesystem->sbfilename) && !ret) {
|
|
if (colerrno != NULL) *colerrno = COLE_EREMOVE;
|
|
ret = 1;
|
|
}
|
|
#endif
|
|
free (colefilesystem->sbfilename);
|
|
}
|
|
free (colefilesystem);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* cole_print_tree:
|
|
* @colefilesystem: filesystem of which the tree will be printed.
|
|
* @colerrno: error value (errors from call cole_recurse_tree()).
|
|
*
|
|
* Prints on the standard output the tree of files and directories contained
|
|
* in @colefilesystem.
|
|
* Currently this call always success.
|
|
*
|
|
* Returns: zero in success, no zero in other case.
|
|
*/
|
|
static COLE_RECURSE_DIR_FUNC __cole_print_tree_indir;
|
|
static COLE_RECURSE_DIR_FUNC __cole_print_tree_outdir;
|
|
static COLE_RECURSE_DIR_FUNC __cole_print_tree_inroot;
|
|
static COLE_RECURSE_DIRENT_FUNC __cole_print_tree_indirentry;
|
|
|
|
int cole_print_tree (COLEFS *colefilesystem, COLERRNO *colerrno)
|
|
{
|
|
long level;
|
|
|
|
level = 1;
|
|
if (cole_recurse_tree (colefilesystem, &level,
|
|
__cole_print_tree_inroot,
|
|
__cole_print_tree_indirentry,
|
|
__cole_print_tree_indir,
|
|
__cole_print_tree_outdir, NULL, colerrno)) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
__cole_print_tree_indir(COLEDIR *cd, void *info, COLERRNO *colerrno)
|
|
{
|
|
/*
|
|
* ATTENTION: if you modify this function so it modifies colerrno:
|
|
* Modify colerrno comment in the functions that call it,
|
|
* ie. cole_print_tree().
|
|
*/
|
|
(*((long*)info))++;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
__cole_print_tree_outdir(COLEDIR *cd, void *info, COLERRNO *colerrno)
|
|
{
|
|
/*
|
|
* ATTENTION: if you modify this function so it modifies colerrno:
|
|
* Modify colerrno comment in the functions that call it,
|
|
* ie. cole_print_tree().
|
|
*/
|
|
(*((long*)info))--;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
__cole_print_tree_inroot(COLEDIR *cd, void *info, COLERRNO *colerrno)
|
|
{
|
|
/*
|
|
* ATTENTION: if you modify this function so it modifies colerrno:
|
|
* Modify colerrno comment in the functions that call it,
|
|
* ie. cole_print_tree().
|
|
*/
|
|
char *entry_name;
|
|
|
|
printf ("DIR ");
|
|
printf (" %7u", cole_dir_getsize (cd));
|
|
printf (" %08lx-%08lx %08lx-%08lx",
|
|
cole_dir_getdays1 (cd),
|
|
cole_dir_getsec1 (cd),
|
|
cole_dir_getdays2 (cd),
|
|
cole_dir_getsec2 (cd));
|
|
entry_name = cole_dir_getname (cd);
|
|
if (!isprint ((int)entry_name[0]))
|
|
printf (" '\\x%02x%s'\n", entry_name[0], entry_name+1);
|
|
else
|
|
printf (" '%s'\n", entry_name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
__cole_print_tree_indirentry(COLEDIRENT *cde, void *info, COLERRNO *colerrno)
|
|
{
|
|
/*
|
|
* ATTENTION: if you modify this function so it modifies colerrno:
|
|
* Modify colerrno comment in the functions that call it,
|
|
* ie. cole_print_tree().
|
|
*/
|
|
char *entry_name;
|
|
long level;
|
|
long i;
|
|
|
|
level = *((long*)info);
|
|
for (i = 0; i < level; i++) {
|
|
if (i == level - 1)
|
|
printf ("\\--");
|
|
else
|
|
printf ("| ");
|
|
}
|
|
|
|
if (cole_direntry_isdir (cde))
|
|
printf ("DIR ");
|
|
else if (cole_direntry_isfile (cde))
|
|
printf ("FILE");
|
|
else
|
|
printf ("????");
|
|
printf (" %7u", cole_direntry_getsize (cde));
|
|
printf (" %08lx-%08lx %08lx-%08lx",
|
|
cole_direntry_getdays1 (cde),
|
|
cole_direntry_getsec1 (cde),
|
|
cole_direntry_getdays2 (cde),
|
|
cole_direntry_getsec2 (cde));
|
|
entry_name = cole_direntry_getname (cde);
|
|
if (!isprint ((int)entry_name[0]))
|
|
printf (" '\\x%02x%s'\n", entry_name[0], entry_name+1);
|
|
else
|
|
printf (" '%s'\n", entry_name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* cole_opendir_rootdir:
|
|
* @colefilesystem: filesystem of which the root directory will be opened.
|
|
* @colerrno: error value (COLE_EMEMORY).
|
|
*
|
|
* Opens the root directory of the filesystem @colefilesystem as directory.
|
|
*
|
|
* Returns: a directory in success, or NULL in other case.
|
|
*/
|
|
COLEDIR *
|
|
cole_opendir_rootdir (COLEFS *colefilesystem, COLERRNO *colerrno)
|
|
{
|
|
COLEDIR *ret;
|
|
|
|
ret = (COLEDIR *)malloc (sizeof (COLEDIR));
|
|
if (ret == NULL) {
|
|
if (colerrno != NULL) *colerrno = COLE_EMEMORY;
|
|
return NULL;
|
|
}
|
|
ret->fs = colefilesystem;
|
|
ret->entry = ret->fs->root;
|
|
ret->visited_entry.dir = ret;
|
|
ret->visited_entry.entry = ret->fs->tree[ ret->entry ].dir;
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* cole_opendir_direntry:
|
|
* @coledirentry: directory entry to be opened as directory.
|
|
* @colerrno: error value (COLE_EISNOTDIR, COLE_EMEMORY).
|
|
*
|
|
* Opens a directory entry as directory.
|
|
*
|
|
* Returns: a directory in success, or NULL in other case.
|
|
*/
|
|
COLEDIR *
|
|
cole_opendir_direntry (COLEDIRENT *coledirentry, COLERRNO *colerrno)
|
|
{
|
|
COLEDIR *ret;
|
|
|
|
if (!cole_direntry_isdir (coledirentry)) {
|
|
if (colerrno != NULL) *colerrno = COLE_EISNOTDIR;
|
|
return NULL;
|
|
}
|
|
|
|
ret = (COLEDIR *)malloc (sizeof (COLEDIR));
|
|
if (ret == NULL) {
|
|
if (colerrno != NULL) *colerrno = COLE_EMEMORY;
|
|
return NULL;
|
|
}
|
|
ret->fs = coledirentry->dir->fs;
|
|
ret->entry = coledirentry->entry;
|
|
ret->visited_entry.dir = ret;
|
|
ret->visited_entry.entry = ret->fs->tree[ ret->entry ].dir;
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* cole_closedir:
|
|
* @coledir: directory to be closed.
|
|
* @colerrno: error value ().
|
|
*
|
|
* Closes the directory @coledir.
|
|
* Currently this call always success.
|
|
*
|
|
* Returns: zero in success, no zero in other case.
|
|
*/
|
|
int
|
|
cole_closedir (COLEDIR *coledir, COLERRNO *colerrno)
|
|
{
|
|
free (coledir);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
COLEDIRENT *
|
|
cole_visiteddirentry (COLEDIR *coledir)
|
|
{
|
|
if (coledir->visited_entry.entry == 0xffffffffUL)
|
|
return NULL;
|
|
|
|
return &coledir->visited_entry;
|
|
}
|
|
|
|
|
|
COLEDIRENT *
|
|
cole_nextdirentry (COLEDIR *coledir)
|
|
{
|
|
if (coledir->visited_entry.entry == 0xffffffffUL)
|
|
return NULL;
|
|
|
|
coledir->visited_entry.entry =
|
|
coledir->fs->tree [ coledir->visited_entry.entry ].next;
|
|
|
|
if (coledir->visited_entry.entry == 0xffffffffUL)
|
|
return NULL;
|
|
|
|
return &coledir->visited_entry;
|
|
}
|
|
|
|
|
|
/**
|
|
* cole_direntry_isdir:
|
|
* @coledirentry: directory entry to be tested.
|
|
*
|
|
* Tests if the directory entry @coledirentry is a directory.
|
|
*
|
|
* Returns: no zero if it is a directory, zero in other case.
|
|
*/
|
|
int
|
|
cole_direntry_isdir (COLEDIRENT *coledirentry)
|
|
{
|
|
return coledirentry->dir->fs->tree[ coledirentry->entry ].type ==
|
|
_COLE_TYPE_DIR;
|
|
}
|
|
|
|
|
|
/**
|
|
* cole_direntry_isfile:
|
|
* @coledirentry: directory entry to be tested.
|
|
*
|
|
* Tests if the directory entry @coledirentry is a file.
|
|
*
|
|
* Returns: no zero if it is a directory, zero in other case.
|
|
*/
|
|
int
|
|
cole_direntry_isfile (COLEDIRENT *coledirentry)
|
|
{
|
|
return coledirentry->dir->fs->tree[ coledirentry->entry ].type ==
|
|
_COLE_TYPE_FILE;
|
|
}
|
|
|
|
|
|
char *
|
|
cole_direntry_getname (COLEDIRENT *coledirentry)
|
|
{
|
|
return coledirentry->dir->fs->tree[ coledirentry->entry ].name;
|
|
}
|
|
|
|
|
|
size_t
|
|
cole_direntry_getsize (COLEDIRENT *coledirentry)
|
|
{
|
|
/* FIXME is it cast needed? */
|
|
return coledirentry->dir->fs->tree[ coledirentry->entry ].size;
|
|
}
|
|
|
|
|
|
long
|
|
cole_direntry_getsec1 (COLEDIRENT *coledirentry)
|
|
{
|
|
/* seconds1 is U32, long is at least U32 in all plattforms, isn't it? */
|
|
return coledirentry->dir->fs->tree[ coledirentry->entry ].seconds1;
|
|
}
|
|
|
|
|
|
long
|
|
cole_direntry_getsec2 (COLEDIRENT *coledirentry)
|
|
{
|
|
/* seconds2 is U32, long is at least U32 in all plattforms, isn't it? */
|
|
return coledirentry->dir->fs->tree[ coledirentry->entry ].seconds2;
|
|
}
|
|
|
|
|
|
long
|
|
cole_direntry_getdays1 (COLEDIRENT *coledirentry)
|
|
{
|
|
/* days1 is U32, long is at least U32 in all plattforms, isn't it? */
|
|
return coledirentry->dir->fs->tree[ coledirentry->entry ].days1;
|
|
}
|
|
|
|
|
|
long
|
|
cole_direntry_getdays2 (COLEDIRENT *coledirentry)
|
|
{
|
|
/* days2 is U32, long is at least U32 in all plattforms, isn't it? */
|
|
return coledirentry->dir->fs->tree[ coledirentry->entry ].days2;
|
|
}
|
|
|
|
|
|
char *
|
|
cole_dir_getname (COLEDIR *coledir)
|
|
{
|
|
return coledir->fs->tree[ coledir->entry ].name;
|
|
}
|
|
|
|
|
|
size_t
|
|
cole_dir_getsize (COLEDIR *coledir)
|
|
{
|
|
/* FIXME is it cast needed? */
|
|
return coledir->fs->tree[ coledir->entry ].size;
|
|
}
|
|
|
|
|
|
long
|
|
cole_dir_getsec1 (COLEDIR *coledir)
|
|
{
|
|
/* seconds1 is U32, long is at least U32 in all plattforms, isn't it? */
|
|
return coledir->fs->tree[ coledir->entry ].seconds1;
|
|
}
|
|
|
|
|
|
long
|
|
cole_dir_getsec2 (COLEDIR *coledir)
|
|
{
|
|
/* seconds2 is U32, long is at least U32 in all plattforms, isn't it? */
|
|
return coledir->fs->tree[ coledir->entry ].seconds2;
|
|
}
|
|
|
|
|
|
long
|
|
cole_dir_getdays1 (COLEDIR *coledir)
|
|
{
|
|
/* days1 is U32, long is at least U32 in all plattforms, isn't it? */
|
|
return coledir->fs->tree[ coledir->entry ].days1;
|
|
}
|
|
|
|
|
|
long
|
|
cole_dir_getdays2 (COLEDIR *coledir)
|
|
{
|
|
/* days2 is U32, long is at least U32 in all plattforms, isn't it? */
|
|
return coledir->fs->tree[ coledir->entry ].days2;
|
|
}
|
|
|
|
|
|
/**
|
|
* cole_fopen:
|
|
* @colefilesystem: filesystem in which @filename is in.
|
|
* @filename: name of the file to open.
|
|
* @colerrno: error value (COLE_EFILENOTFOUND, errors from calls
|
|
* cole_opendir_rootdir(), cole_fopen_direntry() and
|
|
* cole_locate_filename()).
|
|
*
|
|
* Opens the file with the name @filename in the filesystem @colefilesystem.
|
|
* Currently, @filename must begin with a '/' character, it means @filename is
|
|
* the absolute filename.
|
|
*
|
|
* Returns: a file in success, or NULL in other case.
|
|
*/
|
|
struct _cole_fopen_info {
|
|
COLEFILE *file;
|
|
int succ;
|
|
COLERRNO colerrno;
|
|
};
|
|
static COLE_LOCATE_ACTION_FUNC _cole_fopen_action;
|
|
COLEFILE *
|
|
cole_fopen (COLEFS *colefilesystem, char *filename, COLERRNO *colerrno)
|
|
{
|
|
struct _cole_fopen_info info;
|
|
|
|
if (cole_locate_filename (colefilesystem, filename, &info,
|
|
_cole_fopen_action, colerrno)) {
|
|
/* couldn't locate the filename */
|
|
/* colerrno is set */
|
|
return NULL;
|
|
}
|
|
|
|
if (info.succ)
|
|
return info.file;
|
|
|
|
if (colerrno != NULL) *colerrno = info.colerrno;
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
_cole_fopen_action (COLEDIRENT *cde, void *_info)
|
|
{
|
|
struct _cole_fopen_info *info;
|
|
|
|
info = (struct _cole_fopen_info *)_info;
|
|
|
|
if (!cole_direntry_isfile(cde)) {
|
|
info->colerrno = COLE_EFILENAMEISNOTFILE;
|
|
info->succ = 0;
|
|
return;
|
|
}
|
|
|
|
info->file = cole_fopen_direntry (cde, &info->colerrno);
|
|
if (info->file == NULL) {
|
|
/* colerrno is set */
|
|
info->succ = 0;
|
|
return;
|
|
}
|
|
|
|
info->succ = 1;
|
|
}
|
|
|
|
|
|
/**
|
|
* cole_fopen_direntry:
|
|
* @coledirentry: directory entry to be opened as file.
|
|
* @colerrno: error value (COLE_EISNOTFILE, COLE_EMEMORY, COLE_ETMPNAM,
|
|
* COLE_EOPENFILE, COLE_EINVALIDFILESYSTEM, COLE_EREAD,
|
|
* COLE_EWRITE, COLE_EUNKNOWN).
|
|
*
|
|
* Opens a directory entry as file.
|
|
*
|
|
* Returns: a file in success, or NULL in other case.
|
|
*/
|
|
COLEFILE *
|
|
cole_fopen_direntry (COLEDIRENT *coledirentry, COLERRNO *colerrno)
|
|
{
|
|
COLEFILE *ret;
|
|
|
|
if (!cole_direntry_isfile (coledirentry)) {
|
|
if (colerrno != NULL) *colerrno = COLE_EISNOTFILE;
|
|
return NULL;
|
|
}
|
|
|
|
ret = (COLEFILE *)malloc (sizeof (COLEFILE));
|
|
if (ret == NULL) {
|
|
if (colerrno != NULL) *colerrno = COLE_EMEMORY;
|
|
return NULL;
|
|
}
|
|
ret->fs = coledirentry->dir->fs;
|
|
ret->entry = coledirentry->entry;
|
|
switch (__cole_extract_file (&ret->file, &ret->filename,
|
|
ret->fs->tree[ ret->entry ].size,
|
|
ret->fs->tree[ ret->entry ].start,
|
|
ret->fs->BDepot, ret->fs->SDepot,
|
|
ret->fs->sbfile, ret->fs->file)) {
|
|
case 0:
|
|
/* success */
|
|
break;
|
|
case 1:
|
|
if (colerrno != NULL) *colerrno = COLE_EMEMORY;
|
|
free (ret);
|
|
return NULL;
|
|
case 2:
|
|
if (colerrno != NULL) *colerrno = COLE_ETMPNAM;
|
|
free (ret);
|
|
return NULL;
|
|
case 3:
|
|
if (colerrno != NULL) *colerrno = COLE_EOPENFILE;
|
|
free (ret);
|
|
return NULL;
|
|
case 4:
|
|
if (colerrno != NULL) *colerrno = COLE_EINVALIDFILESYSTEM;
|
|
free (ret);
|
|
return NULL;
|
|
case 5:
|
|
if (colerrno != NULL) *colerrno = COLE_EREAD;
|
|
free (ret);
|
|
return NULL;
|
|
case 6:
|
|
if (colerrno != NULL) *colerrno = COLE_EWRITE;
|
|
free (ret);
|
|
return NULL;
|
|
default:
|
|
if (colerrno != NULL) *colerrno = COLE_EUNKNOWN;
|
|
free (ret);
|
|
return NULL;
|
|
}
|
|
/* because the original fopen(3) leaves the file pointer
|
|
in the beginning */
|
|
rewind (ret->file);
|
|
ret->pos = 0;
|
|
ret->filesize = ret->fs->tree[ ret->entry ].size;
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* cole_fclose:
|
|
* @colefile: file to be closed.
|
|
* @colerrno: error value (COLE_ECLOSEFILE, CLOSE_EREMOVE).
|
|
*
|
|
* Closes the file @colefile.
|
|
*
|
|
* Returns: zero in sucess, no zero in other case.
|
|
*/
|
|
int
|
|
cole_fclose (COLEFILE *colefile, COLERRNO *colerrno)
|
|
{
|
|
int ret;
|
|
|
|
ret = 0;
|
|
if (fclose (colefile->file) && !ret) {
|
|
if (colerrno != NULL) *colerrno = COLE_ECLOSEFILE;
|
|
ret = 1;
|
|
}
|
|
if (remove (colefile->filename) && !ret) {
|
|
if (colerrno != NULL) *colerrno = COLE_EREMOVE;
|
|
ret = 1;
|
|
}
|
|
free (colefile->filename);
|
|
free (colefile);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* cole_fread:
|
|
* @colefile: file to be read.
|
|
* @ptr: memory location where the bytes will be stored.
|
|
* @size: how many bytes will be read.
|
|
* @colerrno: error value (COLE_EOF, COLE_EREAD, COLE_ESEEK).
|
|
*
|
|
* Reads @size bytes from @colefile and store them in the location given
|
|
* by @ptr. If not success, the file position indicator is not changed.
|
|
*
|
|
* Returns: in sucess the number of bytes actually readed (maximum @size)
|
|
* or zero in other case.
|
|
*/
|
|
size_t
|
|
cole_fread (COLEFILE *colefile, void *ptr, size_t size, COLERRNO *colerrno)
|
|
{
|
|
size_t bytes_read, bytes_to_read;
|
|
long tbytes;
|
|
|
|
/* Check to see if going past end... */
|
|
if ((colefile->pos + size) > colefile->filesize)
|
|
tbytes = colefile->filesize - colefile->pos;
|
|
else
|
|
tbytes = size;
|
|
if (tbytes <= 0)
|
|
return 0;
|
|
bytes_to_read = (size_t)tbytes;
|
|
bytes_read = fread (ptr, 1, bytes_to_read, colefile->file);
|
|
/* assert (size && (colefile->pos + bytes_read > colefile->pos)); */
|
|
colefile->pos += bytes_read;
|
|
/* assert (!feof (colefile->file)
|
|
&& ftell (colefile->file) == colefile->pos); */
|
|
if (bytes_read != size) {
|
|
/* Linux man page says that fread returns a `short item count
|
|
(or zero)' when an error of end of file ocurrs. A short count
|
|
OR zero? We are hopping that fread always returns zero. */
|
|
if (feof (colefile->file)) {
|
|
if (colerrno != NULL) *colerrno = COLE_EOF;
|
|
return 0;
|
|
} else if (ferror (colefile->file)) {
|
|
if (colerrno != NULL) *colerrno = COLE_EREAD;
|
|
return 0;
|
|
}
|
|
}
|
|
/* assert (size != 0 && bytes_read != 0); */
|
|
/* assert (bytes_read <= size); */
|
|
|
|
return bytes_read;
|
|
}
|
|
|
|
|
|
/**
|
|
* cole_ftell:
|
|
* @colefile: file of which the file position indicator will be get.
|
|
*
|
|
* Get the current value of the file position indicator for the file
|
|
* @colefile.
|
|
*
|
|
* Returns: The file position.
|
|
*/
|
|
size_t
|
|
cole_ftell (COLEFILE *colefile)
|
|
{
|
|
return colefile->pos;
|
|
}
|
|
|
|
|
|
/**
|
|
* cole_fseek:
|
|
* @colefile: file of which its file position indicator will be set.
|
|
* @delta: number of bytes that the file position indicator will be moved.
|
|
* @direction: from where start to count the @delta bytes.
|
|
* @colerrno: error value (COLE_EFSEEKDELTA).
|
|
*
|
|
* Sets the value of the file position indicator for the file @colefile
|
|
* @delta bytes from the beginning of the file, forward from the current
|
|
* position, backward from the current position, or from the end of the
|
|
* file, if @direction is COLE_SEEK_SET, COLE_SEEK_BACKWARD,
|
|
* COLE_SEEK_FORWARD or COLE_SEEK_END, respectively.
|
|
* The file position indicator will always be <= @colefile->filesize.
|
|
* If you @delta is such that the previous line would not true, cole_fseek
|
|
* fails.
|
|
*
|
|
* Returns: zero in success, no zero in other case.
|
|
*/
|
|
int
|
|
cole_fseek (COLEFILE *colefile, size_t delta, COLE_SEEK_FLAG direction,
|
|
COLERRNO *colerrno)
|
|
{
|
|
if ((int)delta < 0) {
|
|
if (colerrno != NULL) *colerrno = COLE_EFSEEKDELTA;
|
|
return 1;
|
|
}
|
|
|
|
switch (direction) {
|
|
case COLE_SEEK_SET:
|
|
if (delta <= colefile->filesize) {
|
|
colefile->pos = delta;
|
|
return 0;
|
|
} else {
|
|
if (colerrno != NULL) *colerrno = COLE_EFSEEKDELTA;
|
|
return 1;
|
|
}
|
|
case COLE_SEEK_END:
|
|
if (delta <= colefile->filesize) {
|
|
colefile->pos = colefile->filesize - delta;
|
|
return 0;
|
|
} else {
|
|
if (colerrno != NULL) *colerrno = COLE_EFSEEKDELTA;
|
|
return 1;
|
|
}
|
|
case COLE_SEEK_BACKWARD:
|
|
if (delta <= colefile->pos) {
|
|
colefile->pos = colefile->pos - delta;
|
|
return 0;
|
|
} else {
|
|
if (colerrno != NULL) *colerrno = COLE_EFSEEKDELTA;
|
|
return 1;
|
|
}
|
|
case COLE_SEEK_FORWARD:
|
|
if (delta <= colefile->filesize - colefile->pos) {
|
|
colefile->pos = colefile->pos + delta;
|
|
return 0;
|
|
} else {
|
|
if (colerrno != NULL) *colerrno = COLE_EFSEEKDELTA;
|
|
return 1;
|
|
}
|
|
default:
|
|
if (colerrno != NULL) *colerrno = COLE_EFSEEKFLAG;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* cole_frewind:
|
|
* @colefile: file of which its file position indicator will be rewind.
|
|
* @colerrno: error value (error from call cole_fseek()).
|
|
*
|
|
* Sets the value of the file position indicator for the file @colefile
|
|
* in the beginning of the file.
|
|
*
|
|
* Returns: zero in success, no zero in other case.
|
|
*/
|
|
int
|
|
cole_frewind (COLEFILE *colefile, COLERRNO *colerrno)
|
|
{
|
|
if (cole_fseek (colefile, 0, COLE_SEEK_SET, colerrno))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* cole_fsize:
|
|
* @colefile: file of which its size will be returned.
|
|
*
|
|
* Returns the size in bytes of the file @colefile.
|
|
*/
|
|
size_t
|
|
cole_fsize (COLEFILE *colefile)
|
|
{
|
|
return colefile->filesize;
|
|
}
|
|
|
|
|
|
/**
|
|
* cole_feof:
|
|
* @colefile: file to be tested.
|
|
*
|
|
* Tests if the end of file has been reached in @colefile.
|
|
*
|
|
* Returns: no zero if the end of file has been reached, zero in other case.
|
|
*/
|
|
int
|
|
cole_feof (COLEFILE *colefile)
|
|
{
|
|
/* assert ((colefile->pos == colefile->fs->tree[ colefile->entry ].size)
|
|
&& feof (colefile->file)); */
|
|
return (colefile->pos == colefile->filesize);
|
|
}
|
|
|
|
|
|
/**
|
|
* cole_recurse_tree:
|
|
* @colefilesystem: filesystem to recurse.
|
|
* @info: arbitrary pointer passed to the functions.
|
|
* @inroot: pointer to the function that is called when start visiting root
|
|
* directory. It can be NULL.
|
|
* @indirentry: pointer to the function that is called when start visiting any
|
|
* directory entry (file or directory). It can be NULL.
|
|
* @indir: pointer to the function that is called when start visiting a
|
|
* directory. It can be NULL.
|
|
* @outdir: pointer to the function that is called when end visiting a
|
|
* directory. It can be NULL.
|
|
* @visitdir: pointer to the function that is called to know if visit a
|
|
* directory. It can be NULL.
|
|
* @colerrno: error value (errors from calls cole_opendir_rootdir(),
|
|
* cole_opendir_direntry(), cole_closedir() and
|
|
* inroot, indirentry, indir, and outdir functions).
|
|
*
|
|
* Recurse the filesystem @colefilesystem, calling the functions pointed by
|
|
* @inroot, @indirentry, @indir and @outdirectory when start visiting
|
|
* root directory, start visiting any directory entry (file or directory),
|
|
* start visiting a directory or end visiting a directory, respectively.
|
|
* If @visitdir returns no zero or it's NULL, the directory is visited,
|
|
* otherwise is not visited.
|
|
* @info is a arbitrary pointer which is passed to the functions pointed by
|
|
* @inroot, @indirentry, @indir and @outdirectory: it may be used to share
|
|
* arbitrary information between them.
|
|
*
|
|
* Returns: zero if recursed all the tree, no zero in other case.
|
|
*/
|
|
static int
|
|
__cole_recurse_tree (COLEDIR *_cd, long level, void *info,
|
|
COLE_RECURSE_DIR_FUNC *inroot,
|
|
COLE_RECURSE_DIRENT_FUNC *indirentry,
|
|
COLE_RECURSE_DIR_FUNC *indir,
|
|
COLE_RECURSE_DIR_FUNC *outdir,
|
|
COLE_RECURSE_VISIT_DIR_FUNC *visitdir,
|
|
COLERRNO *colerrno);
|
|
int
|
|
cole_recurse_tree (COLEFS *colefilesystem, void *info,
|
|
COLE_RECURSE_DIR_FUNC *inroot,
|
|
COLE_RECURSE_DIRENT_FUNC *indirentry,
|
|
COLE_RECURSE_DIR_FUNC *indir,
|
|
COLE_RECURSE_DIR_FUNC *outdir,
|
|
COLE_RECURSE_VISIT_DIR_FUNC *visitdir,
|
|
COLERRNO *colerrno)
|
|
{
|
|
COLEDIR * cd;
|
|
|
|
cd = cole_opendir_rootdir (colefilesystem, colerrno);
|
|
if (cd == NULL)
|
|
return 1;
|
|
|
|
if (__cole_recurse_tree (cd, 1, info, inroot, indirentry, indir,
|
|
outdir, visitdir, colerrno)) {
|
|
cole_closedir (cd, NULL);
|
|
/* colerrno is set */
|
|
return 1;
|
|
}
|
|
|
|
if (cole_closedir (cd, colerrno)) {
|
|
/* colerrno is set */
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
__cole_recurse_tree (COLEDIR *_cd, long level, void *info,
|
|
COLE_RECURSE_DIR_FUNC *inroot,
|
|
COLE_RECURSE_DIRENT_FUNC *indirentry,
|
|
COLE_RECURSE_DIR_FUNC *indir,
|
|
COLE_RECURSE_DIR_FUNC *outdir,
|
|
COLE_RECURSE_VISIT_DIR_FUNC *visitdir,
|
|
COLERRNO *colerrno)
|
|
{
|
|
/*
|
|
* ATTENTION: if you modify __cole_recurse_tree() so it modifies colerrno
|
|
* besides in calling inroot, indirentry, indir, outdir, cole_opendir_direntry
|
|
* or cole_closedir:
|
|
* Modify colerrno comment in the functions that call it,
|
|
* ie. cole_recurse_tree().
|
|
*/
|
|
|
|
/* ATTENTION: This is a recursive function */
|
|
COLEDIRENT * cde;
|
|
COLEDIR * cd;
|
|
|
|
if (level == 1) {
|
|
/* The following lines are only executed on Root Entry */
|
|
if (inroot != NULL) {
|
|
if ( (*inroot) (_cd, info, colerrno) ) {
|
|
/* colerrno is set */
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Iterate through childrens */
|
|
for (cde = cole_visiteddirentry (_cd); cde != NULL;
|
|
cde = cole_nextdirentry (_cd)) {
|
|
if (indirentry != NULL) {
|
|
if ( (*indirentry) (cde, info, colerrno) ) {
|
|
/* colerrno is set */
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* RECURSIVE CALL */
|
|
if (cole_direntry_isdir (cde)) {
|
|
cd = cole_opendir_direntry (cde, colerrno);
|
|
if (cd == NULL) {
|
|
/* colerrno is set */
|
|
return 1;
|
|
}
|
|
|
|
if (indir != NULL) {
|
|
if ( (*indir) (cd, info, colerrno) ) {
|
|
/* colerrno is set */
|
|
cole_closedir (cd, NULL);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if ( (visitdir == NULL) ||
|
|
((*visitdir)(cd, info)) ) {
|
|
if (__cole_recurse_tree (cd, level + 1, info,
|
|
inroot, indirentry,
|
|
indir, outdir,
|
|
visitdir, colerrno)) {
|
|
/* colerrno is set */
|
|
cole_closedir (cd, NULL);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (outdir != NULL) {
|
|
if ( (*outdir) (cd, info, colerrno) ) {
|
|
/* colerrno is set */
|
|
cole_closedir (cd, NULL);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (cole_closedir (cd, colerrno)) {
|
|
/* colerrno is set */
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* cole_locate_filename:
|
|
* @colefilesystem: filesystem where to locate @filename.
|
|
* @filename: name of the file or directory to be located.
|
|
* @info: arbitrary pointer passed to @action.
|
|
* @action: pointer to the function that is called when founding @filename.
|
|
* @colerrno: error value (COLE_EUNKNOWN, COLE_EMEMBERISNOTDIR,
|
|
* COLE_EFILENOTFOUND, COLE_EBROKENFILENAME, errors
|
|
* from call cole_recurse_tree()).
|
|
*
|
|
* Locate the @filename in the filesystem @colefilesystem, calling @action when
|
|
* it's found. @info is arbitrary pointer passed to @action.
|
|
* Currently, @filename must begin with a '/' character, it means @filename is
|
|
* the absolute filename.
|
|
*
|
|
* Returns: zero in success, 1 in other case.
|
|
*/
|
|
struct __cole_locate_filenameinfo {
|
|
COLE_LOCATE_ACTION_FUNC *action;
|
|
void *info;
|
|
char *filename;
|
|
char *current;
|
|
int visitdir;
|
|
};
|
|
static COLE_RECURSE_DIRENT_FUNC __cole_locate_filename_indirentry;
|
|
static COLE_RECURSE_VISIT_DIR_FUNC __cole_locate_filename_visitdir;
|
|
int
|
|
cole_locate_filename (COLEFS *colefilesystem, char *filename,
|
|
void *info,
|
|
COLE_LOCATE_ACTION_FUNC *action,
|
|
COLERRNO *colerrno)
|
|
{
|
|
struct __cole_locate_filenameinfo _info;
|
|
COLERRNO _colerrno;
|
|
|
|
/* FIXME allow no absolute paths */
|
|
if (filename[0] != '/') {
|
|
if (colerrno != NULL) *colerrno = COLE_EBROKENFILENAME;
|
|
return 1;
|
|
}
|
|
|
|
_info.action = action;
|
|
_info.info = info;
|
|
_info.filename = filename;
|
|
_info.current = filename + 1;
|
|
|
|
if (cole_recurse_tree (colefilesystem, &_info, NULL,
|
|
__cole_locate_filename_indirentry, NULL, NULL,
|
|
__cole_locate_filename_visitdir,
|
|
&_colerrno)) {
|
|
if (_colerrno == COLE_ELAST+1) {
|
|
/* file was found */
|
|
return 0;
|
|
}
|
|
if (colerrno != NULL) *colerrno = _colerrno;
|
|
return 1;
|
|
}
|
|
|
|
if (colerrno != NULL) *colerrno = COLE_EFILENOTFOUND;
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
__cole_locate_filename_visitdir (COLEDIR *cd, void *info)
|
|
{
|
|
return ((struct __cole_locate_filenameinfo *)info)->visitdir;
|
|
}
|
|
|
|
static int
|
|
__cole_locate_filename_indirentry (COLEDIRENT *cde, void *_info,
|
|
COLERRNO *colerrno)
|
|
{
|
|
char *entry_name;
|
|
struct __cole_locate_filenameinfo *info;
|
|
char *pcurrent;
|
|
char *pentry_name;
|
|
|
|
info = (struct __cole_locate_filenameinfo *)_info;
|
|
entry_name = cole_direntry_getname (cde);
|
|
for (pcurrent = info->current, pentry_name = entry_name;
|
|
*pcurrent && *pentry_name && *pcurrent != '/';
|
|
pcurrent++, pentry_name++) {
|
|
if (*pcurrent != *pentry_name) {
|
|
info->visitdir = 0; /* don't visit this directory */
|
|
return 0; /* don't break recurse */
|
|
}
|
|
}
|
|
switch (*pentry_name) {
|
|
case 0:
|
|
switch (*pcurrent) {
|
|
case '/':
|
|
if (!cole_direntry_isdir (cde)) {
|
|
if (colerrno != NULL)
|
|
*colerrno = COLE_EMEMBERISNOTDIR;
|
|
return 1; /* break recurse */
|
|
}
|
|
pcurrent++; /* jump the '/' character */
|
|
info->current = pcurrent;
|
|
|
|
/* check if it's the last component of filename */
|
|
if (!(*info->current)) {
|
|
/* last component of filename reached */
|
|
if (info->action != NULL) {
|
|
(*(info->action)) (cde, info->info);
|
|
}
|
|
|
|
if (colerrno != NULL) *colerrno = COLE_ELAST+1;
|
|
return 1; /* break recurse */
|
|
}
|
|
info->visitdir = 1; /* visit this directory */
|
|
return 0; /* don't break recurse */
|
|
case 0:
|
|
/* last component of filename reached */
|
|
if (info->action != NULL) {
|
|
(*(info->action)) (cde, info->info);
|
|
}
|
|
|
|
if (colerrno != NULL) *colerrno = COLE_ELAST+1;
|
|
return 1; /* break recurse */
|
|
default:
|
|
info->visitdir = 0; /* don't visit this directory */
|
|
return 0; /* don't break recurse */
|
|
}
|
|
default:
|
|
switch (*pcurrent) {
|
|
case 0:
|
|
info->visitdir = 0; /* don't visit this directory */
|
|
return 0; /* don't break recurse */
|
|
case '/':
|
|
info->visitdir = 0; /* don't visit this directory */
|
|
return 0; /* don't break recurse */
|
|
default:
|
|
if (colerrno != NULL) *colerrno = COLE_EUNKNOWN;
|
|
return 1; /* break recurse */
|
|
}
|
|
}
|
|
|
|
}
|
|
|