| Archive-Chm documentation | Contained in the Archive-Chm distribution. |
Archive::Chm - Performs some read-only operations on HTML help (.chm) files. Range of operations includes enumerating contents, extracting contents and getting information about one certain part of the archive.
The module supersedes Text::Chm written by Domenico Delle Side. The method get_filelist() and all it's dependencies are taken nearly "as-is" from Text::Chm as written by Domenico.
my $test = Archive::Chm->new("TestPrj.chm");
#make the module log it's activity
$test->set_verbose(1);
$test->set_logfile("chmfile.log");
#set the auto-overwrite function to off
$test->set_overwrite(0);
#enumerate the contents of the archive
$test->enum_files("listing.txt", 1);
#extract all items in a certain directory
$test->extract_all("./out");
#extract a single item from the archive
$item = $test->("/Secret of Monkey Island Solution.html";
#or just get the length of the item
$test->get_item_length("/Secret of Monkey Island Solution.html");
#get complete information about the chm archive
@content = $test->get_filelist();
foreach (@content) {
print $_->{title} . "\n" if defined $_->{title};
print $_->{path} . "\n";
print $_->{size} . "\n";
}
#p.s. There are ways to check for errors, just look up each method and see. :)
Archive::Chm is a module that provides access to Microsoft Compiled HTML Help files (chm files). A lot of today's software ships with documentation in .chm format. However Microsoft only provides viewing tools for their own OS and the company doesn't disclose the format specification.
Unofficial specs can be found at Matthew T. Russotto's site: http://www.speakeasy.org/~russotto/chm/chmformat.html
The module is basically a wrapper of Jed Wing's chmlib, a C library that provides access to all ITSS archives, though .chm is the only ITSS type file in use today. To use this module you need chmlib installed on your system. You can get it at: http://66.93.236.84/~jedwin/projects/chmlib/
Currently access to .chm files is read-only and this will change over time if Jed Wing upgrades his library. Supported operations are getting a listing of the contents, extracting one or all items in the archive and retrieving an item's length.
Archive::Chm has various methods, which can be divided into two categories: methods for working with the chm archive and methods that control how the module works (i.e. logging, overwrite).
These are methods to effectively work with the archive. All operations that can be performed on the archive are contained herein.
$chmobj = Archive::Chm->new($filename)
Constructor of the Archive::Chm class. It only takes the filename as input and opens the target file, checking for errors. The name of the file is also saved.
$chmobj->enum_files($out_file, $mode)
Method for enumerating files in the archive. It takes as its input the output file (if NULL then stdout) and the mode. There are two modes currently supported: mode 1 prints all files, including dependencies and mode 2 prints only the base .html files, without their dependencies, like pictures and such.
Return values and meanings are: 0 (All OK!), 1 (file exists, not overwriting due to AUTO_OVERWRITE = 0), 2 (output file cannot be created/overwritten), 3 (unkown error in enumeration API), 4 (unknown mode requested), 5 (no chm archive open).
Note that the method was "successfull" with a return value of 1 as well. The err variable is set to the return value unless that value is 0 or 1.
$chmobj->extract_all($out_dir)
Method for extracting all files from the .chm archive to a given directory. It returns 0 when all went well, 1 when there was an unkown error in enumeration API and 2 when there is no open archive. The err value is set to the return value unless all went well.
$html = $chmobj->extract_item($item_path)
Method for retrieving an item, transmitted by it's relative path from the .chm archive's root. It returns a string with the file's contents. If there was an error, returns NULL and sets the error flag and message.
@contents = $chmobj->get_filelist()
Metod for getting a list of hash references for all elements of the archive. Each hash has a maximum of 3 keys, "title", "path" and "size". They are self-explanatory.
$length = $chmobj->get_item_length($item_path)
Method for getting a certain item's length. The item is transmitted by it's relative path from the archive's root. The return value is 0 and the error variable set to 1 if the item could not be resolved, otherwise the return value is the actual length of the item.
$filename = $chmobj->get_name()
Method for getting the filename of the attached .chm file.
$chmobj->close_file();
Sometimes you may want to close the associated .chm file while letting the Archive::Chm object live on. If you do so, you'll need to open it again by using open_file.
$chmobj->open_file();
While the file is automatically opened at object creation, if you close it during the object's lifetime, you will need to reopen it using this method. Returns 0 on success, 1 on error.
Methods for module control. It should be noted that the error flag is never reset by the module and should be manually reset after it has been checked. Archive::Chm only sets the error flag when an error occurs.
$chmobj->err($code);
Gets the current error code if $code = -1, otherwise sets it to $code.
$chmobj->errmsg();
Gets the string containing the error message corresponding to the last error encountered.
$chmobj->set_logfile($log_filename); $log_filename = $chmobj->set_logfile();
Method used to get/set the logfile of the module. Notable that this is actually a static data member and as such common for all the Archive::Chm objects.
$chmobj->set_overwrite($owr); $owr = $chmobj->set_overwrite(-1);
Method used to get/set the static AUTO_OVERWRITE flag. Works just like the logfile function above, except that getting the flag requires a value of -1 to be passed.
$chmobj->set_verbose($verb) $verb = $chmobj->set_verbose(-1)
Yet another function to get/set the VERBOSE flag. Works just like the previous one for AUTO_OVERWRITE.
ChmLIB: http://66.93.236.84/~jedwin/projects/chmlib/
HMTL Help specs: http://www.speakeasy.org/~russotto/chm/chmformat.html
Domenico Delle Side's module, Text::Chm. It is simpler than Archive::Chm, but still offers good support for HTML Help archives, including the very useful get_filelist() method.
Alexandru Palade <apalade@netsoft.ro>, Netsoft S.R.L.
The Text::Chm functions are the work of Domenico Delle Side <dds@gnulinux.it>
Copyright (C) 2005 Alexandru Palade, Netsoft S.R.L.
All rights reserved.
| Archive-Chm documentation | Contained in the Archive-Chm distribution. |
package Archive::Chm; our $VERSION = '0.06'; use strict; use warnings; use diagnostics; use Inline ( C => Config => LIBS => '-lchm', VERSION => '0.06', NAME => __PACKAGE__, CLEAN_AFTER_BUILD => 0 ); use Inline 'C'; 1;
__DATA__ __C__ #include "chm_lib.h" #define ALL_FILES (1) #define BASE_FILES (2) typedef struct { char* filename; struct chmFile* target; int is_open; int error; char* errmsg; } ChmFile; struct ChmObjData { char *path; char *title; size_t size; struct ChmObjData *next; }; struct ChmObjData *data = NULL; struct extract_context { const char *base_path; }; int _enum_print_ui(struct chmFile *h, struct chmUnitInfo *ui, void *context); int _enum_get_ui(struct chmFile *h, struct chmUnitInfo *ui, void *context); int _extract_callback(struct chmFile *h, struct chmUnitInfo *ui, void *context); static FILE* _set_outfile(FILE *newfile); static FILE* _set_logfile(char *filename); static int _rmkdir(char *path); static int _dir_exists(const char *path); static int file_is_normal(const char *filename); static int file_is_html(const char *path); static struct ChmObjData *chm_data_add(char *path, char *title, size_t size); static char *get_html_title(SV *obj, const char *filename); static char *find_title(char *s); char* set_logfile(char* class, char* filename); int set_overwrite(char* class, int owr); int set_verbose(char* class, int verb); void _set_errmsg(SV* obj, char* msg); int err(SV* obj, int error); char* errmsg(SV* obj); static char * my_strndup(const char *src, size_t len); /******************************************************************************** * * * THE PUBLIC INTERFACE FOR THE CHMFILE CLASS * * * ********************************************************************************/
SV* new(char* class, char* filename) { ChmFile* chmfile = malloc(sizeof(ChmFile)); SV* obj_ref = newSViv(0); SV* obj = newSVrv(obj_ref, class); chmfile->target = chm_open(filename); if (!(chmfile->target)) return NULL; chmfile->is_open = 1; chmfile->error = 0; _set_errmsg("No error whatsoever"); chmfile->filename = strdup(filename); sv_setiv(obj, (IV)chmfile); SvREADONLY_on(obj); return obj_ref; } /*------------------------------------------------------------------------------- // Destructor for the Archive::Chm class. It closes the .chm file and frees // the space occupied by the filename. //------------------------------------------------------------------------------*/ void DESTROY(SV* obj) { ChmFile* chmfile = (ChmFile*)SvIV(SvRV(obj)); free(chmfile->filename); if (chmfile->errmsg) free(chmfile->errmsg); if (chmfile->is_open) chm_close(chmfile->target); _set_logfile("stdout"); }
int enum_files(SV* obj, char *out_file, int mode) { ChmFile* chm = (ChmFile*)SvIV(SvRV(obj)); FILE* fout; //get the "static" data members VERBOSE, AUTO_OVERWRITE and log FILE* log = _set_logfile(NULL); int VERBOSE = set_verbose("Archive::Chm", -1); int AUTO_OVERWRITE = set_overwrite("Archive::Chm", -1); //if there is no open .chm archive if (!(chm->is_open)) { chm->error = 5; _set_errmsg(obj, "No archive open."); return 5; } //if no output file is specified, just use stdout if (!strcmp(out_file,"")) { /*errno = 0; register IO* io = GvIOn(PL_defoutgv); PerlIO* ofp = IoOFP(io); PerlIO_printf(ofp, "This is... evolution!"); fout = PerlIO_exportFILE(ofp, "w");*/ fout = stdout; } else { //if the file exists and we don't want to overwrite, abort if (!AUTO_OVERWRITE && _dir_exists(out_file)) { if (VERBOSE) fprintf(log, "%s already exists. Operation aborted.\n\n", out_file); return 1; } if ((fout = fopen(out_file, "w")) == NULL) { char *msg = (char*)malloc(strlen(out_file) + strlen(" could not be opened for writing.")); chm->error = 2; sprintf(msg, "%s could not be opened for writing.", out_file); _set_errmsg(obj, msg); if (VERBOSE) fprintf(log, "Operation failed.\n\n"); return 2; } } if (strcmp(out_file, "") && VERBOSE) fprintf(log, "Outputting structure of %s to %s\n", chm->filename, out_file); //print the header fprintf(fout, " %s:\n\n", chm->filename); fprintf(fout, " spc start length name\n"); fprintf(fout, " === ===== ====== ====\n"); _set_outfile(fout); //use the enumerate API to print the contents, according to the chosen mode switch (mode) { case BASE_FILES: { if (!chm_enumerate_dir(chm->target,"/", CHM_ENUMERATE_ALL, _enum_print_ui, NULL)) { chm->error = 3; _set_errmsg(obj, "Error in the enumeration API."); fclose(fout); if (VERBOSE) fprintf(log, "Operation failed.\n\n"); return 3; } break; } case ALL_FILES: { if (!chm_enumerate(chm->target, CHM_ENUMERATE_ALL, _enum_print_ui, NULL)) { chm->error = 3; _set_errmsg(obj, "Error in the enumeration API."); fclose(fout); if (VERBOSE) fprintf(log, "Operation failed.\n\n"); return 3; } break; } default: { chm->error = 4; _set_errmsg(obj, "Uknown mode for enum_files."); if (VERBOSE) fprintf(log, "Operation failed.\n\n"); return 4; } } if (strcmp(out_file, "")) fclose(fout); if (VERBOSE) fprintf(log, "Operation succesfull\n\n"); return 0; }
int extract_all(SV* obj, char *out_dir) { ChmFile* chm = (ChmFile*)SvIV(SvRV(obj)); struct extract_context ec; //get the "static" data members VERBOSE and log FILE* log = _set_logfile(NULL); int VERBOSE = set_verbose("Archive::Chm", -1); if (!(chm->is_open)) { chm->error = 2; _set_errmsg(obj, "No archive open."); return 2; } if (VERBOSE) fprintf(log, "Extracting contents of %s to %s\n", chm->filename, out_dir); ec.base_path = out_dir; if (!chm_enumerate(chm->target, CHM_ENUMERATE_ALL, _extract_callback, (void *)&ec)) { if (VERBOSE) fprintf(log, "Operation failed\n\n"); chm->error = 1; _set_errmsg(obj, "Error in enumeration API."); return 1; } if (VERBOSE) fprintf(log, "Operation succesfull\n\n"); return 0; }
unsigned char* extract_item(SV* obj, char *item_name) { ChmFile* chm = (ChmFile*)SvIV(SvRV(obj)); struct chmUnitInfo ui; //get the "static" data members VERBOSE, AUTO_OVERWRITE and log FILE* log = _set_logfile(NULL); int VERBOSE = set_verbose("Archive::Chm", -1); int AUTO_OVERWRITE = set_overwrite("Archive::Chm", -1); if (!(chm->is_open)) { chm->error = 1; _set_errmsg(obj, "No archive open."); return NULL; } //resolve the given item if (VERBOSE) fprintf(log, "Resolving %s...", item_name); if (CHM_RESOLVE_SUCCESS == chm_resolve_object(chm->target, item_name, &ui)) { #ifdef WIN32 unsigned char *buffer = (unsigned char *)alloca((unsigned int)ui.length); #else unsigned char buffer[ui.length]; #endif LONGINT64 gotLen; //object succesfully resolved, so print info about it if (VERBOSE) fprintf(log, " object <space=%d, start=%lu, length=%lu>\n", ui.space, (unsigned long)ui.start, (unsigned long)ui.length); gotLen = chm_retrieve_object(chm->target, &ui, buffer, 0, ui.length); if (gotLen == 0) { chm->error = 3; _set_errmsg(obj, "Extract failed."); if (VERBOSE) fprintf(log, "Operation failed\n\n"); return NULL; } else { if (VERBOSE) fprintf(log, "Operation succesfull\n\n"); return (unsigned char*)strdup((char*) buffer); } } //if the resolve failed say so else { chm->error = 2; _set_errmsg(obj, "Could not resolve item."); if (VERBOSE) fprintf(log, "failed\n\n"); return NULL; } }
void get_filelist(SV* obj) { struct ChmObjData* contents = NULL; ChmFile* chm = (ChmFile*)SvIV(SvRV(obj)); HV* hash; int i = 0; contents = chm_data_add("start", "start", 0); data = contents; if (!chm_enumerate(chm->target, CHM_ENUMERATE_ALL, _enum_get_ui, obj)) croak("Errors getting filelist\n"); data = contents->next; Inline_Stack_Vars; Inline_Stack_Reset; while (data) { SV* refr; hash = newHV(); hv_store(hash, "path", 4, newSVpv(data->path, strlen(data->path)), 0); hv_store(hash, "size", 4, newSViv(data->size), 0); if (data->title) hv_store(hash, "title", 5, newSVpv(data->title, strlen(data->title)), 0); else hv_store(hash, "title", 5, newSV(0), 0); Inline_Stack_Push(sv_2mortal(newRV((SV*) hash))); data = data->next; i++; } Inline_Stack_Done; }
unsigned long get_item_length(SV* obj, char *item_name) { ChmFile* chm = (ChmFile*)SvIV(SvRV(obj)); struct chmUnitInfo ui; //get the "static" data members VERBOSE, AUTO_OVERWRITE and log FILE* log = _set_logfile(NULL); int VERBOSE = set_verbose("Archive::Chm", -1); int AUTO_OVERWRITE = set_overwrite("Archive::Chm", -1); if (!(chm->is_open)) { chm->error = 2; _set_errmsg(obj, "No archive open."); return 2; } //resolve the given item if (VERBOSE) fprintf(log, "Resolving %s...", item_name); if (CHM_RESOLVE_SUCCESS == chm_resolve_object(chm->target, item_name, &ui)) { //object succesfully resolved, so print info about it if (VERBOSE) fprintf(log, " object <space=%d, start=%lu, length=%lu>\n", ui.space, (unsigned long)ui.start, (unsigned long)ui.length); return (unsigned long)ui.length; } //if the resolve failed say so else { chm->error = 1; _set_errmsg(obj, "Could not resolve item."); if (VERBOSE) fprintf(log, "failed\n\n"); return 0; } }
char* get_name(SV* obj) { ChmFile* chm = (ChmFile*)SvIV(SvRV(obj)); char *ret = strdup(chm->filename); return ret; }
void close_file(SV* obj) { ChmFile* chm = (ChmFile*)SvIV(SvRV(obj)); if (chm->is_open) { chm_close(chm->target); chm->is_open = 0; if (set_verbose("Archive::Chm", -1)) fprintf(_set_logfile(NULL), "Closed %s\n\n", get_name(obj)); } }
int open_file(SV* obj) { ChmFile* chm = (ChmFile*)SvIV(SvRV(obj)); if (!(chm->is_open)) { chm->target = chm_open(chm->filename); if (!(chm->target)) { chm->error = 1; _set_errmsg(obj, "Could not open target archive."); return 1; } chm->is_open = 1; if (set_verbose("Archive::Chm", -1)) fprintf(_set_logfile(NULL), "Opened %s\n\n", get_name(obj)); } return 0; }
int err(SV* obj, int error) { ChmFile* chm = (ChmFile*)SvIV(SvRV(obj)); if (error != -1) chm->error = error; return chm->error; }
char* errmsg(SV* obj) { ChmFile* chm = (ChmFile*)SvIV(SvRV(obj)); return chm->errmsg; } /******************************************************************************** * * * SIMULATED STATIC MEMBERS WITH GETTERS/SETTERS * * * ********************************************************************************/ /*------------------------------------------------------------------------------- // Function used to encapsulate the "global" outfile needed for the extract- // callback function. It can set the outfile or get it (if the parameter is NULL). // This function is needed so that we don't make outfile global and cause problems // with multithreading (i.e. polluting the namespace). //------------------------------------------------------------------------------*/ static FILE* _set_outfile(FILE *newfile) { static FILE* out = NULL; if (newfile) out = newfile; return out; } /*------------------------------------------------------------------------------- // Another function similar to _set_outfile. This one is used to get/set the // log file used to output the result of operations. //------------------------------------------------------------------------------*/ static FILE* _set_logfile(char *filename) { static FILE* log = NULL; if (!log) log = stdout; //if we wanted to set the logfile, do it if (filename) { //if we had another logfile open, close it if (log && log != stdout) fclose(log); //we can set the logging to stdout, also used as a finishing routine if (!strcmp(filename, "stdout")) return log = stdout; //now open the new logfile and if we are verbose, note the change if ((log = fopen(filename, "a")) == NULL) { fprintf(stderr, "Error opening logfile %s.\n", filename); fprintf(stderr, "Log is reset to stdout.\n"); log = stdout; } else if (set_verbose("Archive::Chm", -1)) fprintf(log, "Succesfully switched to new logfile %s.\n\n", filename); } return log; }
char* set_logfile(char* class, char* filename) { static char* logfile_name = NULL; //check for proper usage if (strstr(class, "Archive::Chm") != class) { fprintf(stderr, "Warning! Improper usage of set_logfile.\n"); fprintf(stderr, "Usage: Archive::Chm->set_logfile(char* logname)\n"); } if (strcmp(filename, "")) { logfile_name = strdup(filename); _set_logfile(filename); } return strdup(logfile_name); }
int set_overwrite(char* class, int owr) { static int AUTO_OVERWRITE = 1; if (strstr(class, "Archive::Chm") != class) { fprintf(stderr, "Class is: '%s'\n", class); fprintf(stderr, "Warning! Improper usage of set_overwrite.\n"); fprintf(stderr, "Usage: Archive::Chm->set_overwrite(int owr)\n"); } if (owr != -1) AUTO_OVERWRITE = owr; return AUTO_OVERWRITE; }
int set_verbose(char* class, int verb) { static int VERBOSE = 0; if (strstr(class, "Archive::Chm") != class) { fprintf(stderr, "Warning! Improper usage of set_verbose.\n"); fprintf(stderr, "Usage: Archive::Chm->set_verbose(int verb)\n"); } if (verb != -1) VERBOSE = verb; return VERBOSE; } /******************************************************************************** * * * PRIVATE METHODS AND OTHER FUNCTIONS * * * ********************************************************************************/ /*------------------------------------------------------------------------------- // This function creates a new node for the linked list containing the // filelist of the chm file. //------------------------------------------------------------------------------*/ static struct ChmObjData *chm_data_add(char *path, char *title, size_t size) { struct ChmObjData *tmp = NULL; tmp = calloc(1, sizeof(struct ChmObjData)); if (tmp == NULL) croak("Out of memory\n"); tmp->path = my_strndup(path, strlen(path)); tmp->title = title; tmp->size = size; tmp->next = NULL; return tmp; } /*------------------------------------------------------------------------------- // Check that the file at "path" is an html one, taking this decision // depending on the extension of the file. //------------------------------------------------------------------------------*/ static int file_is_html(const char *path) { char *tmp; if ((int)strlen(path) < 4) /* Path must be at least 5 char long. */ return 0; if (NULL != (tmp = strrchr(path, '.'))) if (0 == strncasecmp(++tmp, "htm", (size_t)3) || 0 == strncasecmp(tmp, "html", (size_t)4)) return 1; return 0; } /*------------------------------------------------------------------------------- // For the moment, we are intrested only on normal files, such as html, css, // images, etc... So we limit to look to these. //------------------------------------------------------------------------------*/ static int file_is_normal(const char *filename) { if ( '/' == *filename ) /* SPECIAL or NORMAL file */ { if ( ('#' == *(filename + 1)) || ('$' == *(filename + 1)) ) /* SPECIAL file */ return 0; else /* NORMAL file */ return 1; } else /* META file */ return 0; } /*------------------------------------------------------------------------------- // Glue-function for getting the title of an html file and returning it. //------------------------------------------------------------------------------*/ static char *get_html_title(SV *obj, const char *filename) { char *content, *title; size_t len; content = extract_item(obj, filename); title = find_title(content); return title; } /*------------------------------------------------------------------------------- // Traverse the content of the string "s", which contains html code, // to find the text between <title> and </title>, and return it. //------------------------------------------------------------------------------*/ static char *find_title(char *s) { char *tmp = s; size_t len; while ( tmp++ ) { tmp = strchr(tmp, '<'); if ( 0 == strncasecmp(tmp, "<title>", (size_t)7) ) { tmp += 7; len = (size_t) (strchr(tmp, '<') - tmp); return my_strndup(tmp, len); } else continue; } return NULL; } /*------------------------------------------------------------------------------- // Function for use by the enumeration API when used to output the file names // from the archive. //------------------------------------------------------------------------------*/ int _enum_print_ui(struct chmFile *h, struct chmUnitInfo *ui, void *context) { fprintf(_set_outfile(NULL), " %1d %8d %8d %s\n", (int)ui->space, (int)ui->start, (int)ui->length, ui->path); return CHM_ENUMERATOR_CONTINUE; } /*------------------------------------------------------------------------------- // Callback function to be passed to chm_enumerate(), in order to build a linked // list of ChmObjData structures that contains some informations about the "normal" // members (html, css, xml and image files) of the chm file. //------------------------------------------------------------------------------*/ int _enum_get_ui(struct chmFile *h, struct chmUnitInfo *ui, void *context) { char *title; struct ChmObjData *tmp; SV* chm = (SV*) context; if (file_is_normal(ui->path)) { title = ((file_is_html(ui->path)) ? get_html_title(chm, ui->path) : NULL); tmp = chm_data_add(ui->path, title, ui->length); data->next = tmp; data = data->next; tmp = NULL; } return CHM_ENUMERATOR_CONTINUE; } /*------------------------------------------------------------------------------- // Function for use by the enumeration API when used to extract all files from // the archive. //------------------------------------------------------------------------------*/ int _extract_callback(struct chmFile *h, struct chmUnitInfo *ui, void *context) { unsigned char buffer[32768]; struct extract_context *ctx = (struct extract_context *)context; char *i; if (ui->path[0] != '/') return CHM_ENUMERATOR_CONTINUE; if (snprintf((char*)buffer, sizeof(buffer), "%s%s", ctx->base_path, ui->path) > 1024) return CHM_ENUMERATOR_FAILURE; if (ui->length != 0) { FILE *fout; LONGINT64 len, remain=ui->length; LONGUINT64 offset = 0; if (set_verbose("Archive::Chm", -1)) fprintf(_set_logfile(NULL), "--> %s\n", ui->path); if ((fout = fopen((const char*)buffer, "wb")) == NULL) { /* make sure that it isn't just a missing directory before we abort */ unsigned char newbuf[32768]; strcpy((char*)newbuf, (const char*)buffer); i = rindex((const char*)newbuf, '/'); *i = '\0'; _rmkdir((char*)newbuf); if ((fout = fopen((const char*)buffer, "wb")) == NULL) return CHM_ENUMERATOR_FAILURE; } while (remain != 0) { len = chm_retrieve_object(h, ui, buffer, offset, 32768); if (len > 0) { fwrite(buffer, 1, (size_t)len, fout); offset += len; remain -= len; } else { fprintf(stderr, "incomplete file: %s\n", ui->path); break; } } fclose(fout); } else { if (_rmkdir((char*)buffer) == -1) return CHM_ENUMERATOR_FAILURE; } return CHM_ENUMERATOR_CONTINUE; } /*------------------------------------------------------------------------------- // Function that checks if the file/directory given as an argument exists. //------------------------------------------------------------------------------*/ static int _dir_exists(const char *path) { struct stat statbuf; if (stat(path, &statbuf) != -1) return 1; else return 0; } /*------------------------------------------------------------------------------- // Function to make a given directory for use when extracting to a path. //------------------------------------------------------------------------------*/ static int _rmkdir(char *path) { //strip off trailing components unless we can stat the directory, or we //have run out of components char *i = rindex(path, '/'); if(path[0] == '\0' || _dir_exists(path)) return 0; if (i != NULL) { *i = '\0'; _rmkdir(path); *i = '/'; mkdir(path, 0777); } #ifdef WIN32 return 0; #else if (_dir_exists(path)) return 0; else return -1; #endif } /*------------------------------------------------------------------------------- // Function to set the error message associated with the Archive::Chm object. //------------------------------------------------------------------------------*/ void _set_errmsg(SV* obj, char* msg) { ChmFile* chm = (ChmFile*)SvIV(SvRV(obj)); if (chm->errmsg) free(chm->errmsg); chm->errmsg = strdup(msg); } /*------------------------------------------------------------------------------- // Get rid of faulty strndup implementations... //------------------------------------------------------------------------------*/ static char * my_strndup(const char *src, size_t len) { char *ret = NULL; ret = calloc(len + (size_t)1, sizeof(char)); if ( !ret ) croak("Out of memory\n"); strncpy(ret, src, len); *(ret + (int)len) = '\0'; return ret; }