@ -8,14 +8,18 @@
# include <zlib.h>
# include <zlib.h>
# include <getopt.h>
# include <getopt.h>
# include <stdbool.h>
# include <stdbool.h>
# include <errno.h>
# include "espfsformat.h"
# include "espfsformat.h"
# include "heatshrink_encoder.h"
# include "heatshrink_encoder.h"
# include "parsing.h"
# include "parsing.h"
# include "httpd-logging.h"
# define DEFAULT_GZIP_EXTS "html,css,js,svg,png,jpg,gif"
void show_version ( char * * argv ) ;
void show_help ( int retval , char * * argv ) ;
struct InputFileLinkedListEntry ;
# define DEFAULT_GZIP_EXTS "css,js,svg,png,jpg,jpeg,webm,ico,gif"
# define DEFAULT_C_VARNAME "espfs_image"
struct InputFileLinkedListEntry {
struct InputFileLinkedListEntry {
char * name ;
char * name ;
@ -27,7 +31,7 @@ static struct InputFileLinkedListEntry *s_inputFiles = NULL;
static struct InputFileLinkedListEntry * s_lastInputFile = NULL ;
static struct InputFileLinkedListEntry * s_lastInputFile = NULL ;
/// Output file FD
/// Output file FD
static int s_outFd = 1 ;
static int s_outFd = STDOUT_FILENO ;
/// Array of gzipped extensions, ends with a NULL pointer
/// Array of gzipped extensions, ends with a NULL pointer
static char * * s_gzipExtensions = NULL ;
static char * * s_gzipExtensions = NULL ;
@ -69,7 +73,7 @@ size_t compressHeatshrink(const uint8_t *in, size_t insize, uint8_t *out, size_t
level = ( level - 1 ) / 2 ; //level is now 0, 1, 2, 3, 4
level = ( level - 1 ) / 2 ; //level is now 0, 1, 2, 3, 4
heatshrink_encoder * enc = heatshrink_encoder_alloc ( ws [ level ] , ls [ level ] ) ;
heatshrink_encoder * enc = heatshrink_encoder_alloc ( ws [ level ] , ls [ level ] ) ;
if ( enc = = NULL ) {
if ( enc = = NULL ) {
perror ( " allocating mem for heatshrink " ) ;
es pfs_ error( " allocating mem for heatshrink " ) ;
exit ( 1 ) ;
exit ( 1 ) ;
}
}
//Save encoder parms as first byte
//Save encoder parms as first byte
@ -96,7 +100,7 @@ size_t compressHeatshrink(const uint8_t *in, size_t insize, uint8_t *out, size_t
} while ( insize ! = 0 ) ;
} while ( insize ! = 0 ) ;
if ( insize ! = 0 ) {
if ( insize ! = 0 ) {
fprintf ( stderr , " Heatshrink: Bug? insize is still %d. sres=%d pres=%d \n " , ( int ) insize , sres , pres ) ;
espfs_error ( " Heatshrink: Bug? insize is still %d. sres=%d pres=%d " , ( int ) insize , sres , pres ) ;
exit ( 1 ) ;
exit ( 1 ) ;
}
}
@ -111,10 +115,9 @@ size_t compressHeatshrink(const uint8_t *in, size_t insize, uint8_t *out, size_t
* @ param insize - len of the uncompressed input
* @ param insize - len of the uncompressed input
* @ param [ out ] out - destination buffer for the compressed data
* @ param [ out ] out - destination buffer for the compressed data
* @ param outcap - capacity of the output buffer
* @ param outcap - capacity of the output buffer
* @ param level - compression level , 1 - 9 ; - 1 for default .
* @ return actual length of the compressed data
* @ return actual length of the compressed data
*/
*/
size_t compressGzip ( const uint8_t * in , size_t insize , uint8_t * out , size_t outcap , int level )
size_t compressGzip ( const uint8_t * in , size_t insize , uint8_t * out , size_t outcap )
{
{
z_stream stream ;
z_stream stream ;
int zresult ;
int zresult ;
@ -127,21 +130,21 @@ size_t compressGzip(const uint8_t *in, size_t insize, uint8_t *out, size_t outca
stream . next_out = out ;
stream . next_out = out ;
stream . avail_out = ( uInt ) outcap ;
stream . avail_out = ( uInt ) outcap ;
// 31 -> 15 window bits + 16 for gzip
// 31 -> 15 window bits + 16 for gzip
zresult = deflateInit2 ( & stream , level , Z_DEFLATED , 31 , 8 , Z_DEFAULT_STRATEGY ) ;
zresult = deflateInit2 ( & stream , Z_BEST_COMPRESSION /* we want the smallest possible files */ , Z_DEFLATED , 31 , 8 , Z_DEFAULT_STRATEGY ) ;
if ( zresult ! = Z_OK ) {
if ( zresult ! = Z_OK ) {
fprintf ( stderr , " DeflateInit2 failed with code %d \n " , zresult ) ;
espfs_error ( " DeflateInit2 failed with code %d " , zresult ) ;
exit ( 1 ) ;
exit ( 1 ) ;
}
}
zresult = deflate ( & stream , Z_FINISH ) ;
zresult = deflate ( & stream , Z_FINISH ) ;
if ( zresult ! = Z_STREAM_END ) {
if ( zresult ! = Z_STREAM_END ) {
fprintf ( stderr , " Deflate failed with code %d \n " , zresult ) ;
espfs_error ( " Deflate failed with code %d " , zresult ) ;
exit ( 1 ) ;
exit ( 1 ) ;
}
}
zresult = deflateEnd ( & stream ) ;
zresult = deflateEnd ( & stream ) ;
if ( zresult ! = Z_OK ) {
if ( zresult ! = Z_OK ) {
fprintf ( stderr , " DeflateEnd failed with code %d \n " , zresult ) ;
espfs_error ( " DeflateEnd failed with code %d " , zresult ) ;
exit ( 1 ) ;
exit ( 1 ) ;
}
}
@ -239,31 +242,31 @@ int handleFile(int fd, const char *name, int compression_mode, int level, const
csize = 100 ;
csize = 100 ;
} // enlarge buffer if this is the case
} // enlarge buffer if this is the case
cdat = cdatbuf = malloc ( csize ) ;
cdat = cdatbuf = malloc ( csize ) ;
csize = compressGzip ( fdat , size , cdat , csize , level ) ;
csize = compressGzip ( fdat , size , cdat , csize ) ;
compression_mode = COMPRESS_NONE ; // don't use heatshrink if gzip was already used - it would only make it bigger
compression_mode = EFS_ COMPRESS_NONE; // don't use heatshrink if gzip was already used - it would only make it bigger
flags = FLAG_GZIP ;
flags = EFS_ FLAG_GZIP;
} else if ( compression_mode = = COMPRESS_NONE ) {
} else if ( compression_mode = = EFS_ COMPRESS_NONE) {
csize = size ;
csize = size ;
cdat = fdat ;
cdat = fdat ;
} else if ( compression_mode = = COMPRESS_HEATSHRINK ) {
} else if ( compression_mode = = EFS_ COMPRESS_HEATSHRINK) {
cdat = cdatbuf = malloc ( size * 2 ) ;
cdat = cdatbuf = malloc ( size * 2 ) ;
csize = compressHeatshrink ( fdat , size , cdat , size * 2 , level ) ;
csize = compressHeatshrink ( fdat , size , cdat , size * 2 , level ) ;
} else {
} else {
fprintf ( stderr , " Unknown compression - %d \n " , compression_mode ) ;
espfs_error ( " Unknown compression - %d " , compression_mode ) ;
exit ( 1 ) ;
exit ( 1 ) ;
}
}
if ( csize > size ) {
if ( csize > size ) {
fprintf ( stderr , " ! Compression enbiggened %s, embed as plain \ n " , name ) ;
espfs_dbg ( " ! Compression enbiggened %s, embed as plain " , name ) ;
//Compressing enbiggened this file. Revert to uncompressed store.
//Compressing enbiggened this file. Revert to uncompressed store.
compression_mode = COMPRESS_NONE ;
compression_mode = EFS_ COMPRESS_NONE;
csize = size ;
csize = size ;
cdat = fdat ;
cdat = fdat ;
flags = 0 ;
flags = 0 ;
}
}
//Fill header data
//Fill header data
h . magic = htole32 ( ESP FS_MAGIC ) ; // ('E' << 0) + ('S' << 8) + ('f' << 16) + ('s' << 24);
h . magic = htole32 ( EFS_MAGIC ) ; // ('E' << 0) + ('S' << 8) + ('f' << 16) + ('s' << 24);
h . flags = flags ;
h . flags = flags ;
h . compression = ( uint8_t ) compression_mode ;
h . compression = ( uint8_t ) compression_mode ;
h . nameLen = realNameLen = ( uint16_t ) strlen ( name ) + 1 ; // zero terminator
h . nameLen = realNameLen = ( uint16_t ) strlen ( name ) + 1 ; // zero terminator
@ -299,10 +302,10 @@ int handleFile(int fd, const char *name, int compression_mode, int level, const
// debug outputs ...
// debug outputs ...
if ( compName ! = NULL ) {
if ( compName ! = NULL ) {
if ( h . compression = = COMPRESS_HEATSHRINK ) {
if ( h . compression = = EFS_ COMPRESS_HEATSHRINK) {
* compName = " heatshrink " ;
* compName = " heatshrink " ;
} else if ( h . compression = = COMPRESS_NONE ) {
} else if ( h . compression = = EFS_ COMPRESS_NONE) {
if ( h . flags & FLAG_GZIP ) {
if ( h . flags & EFS_ FLAG_GZIP) {
* compName = " gzip " ;
* compName = " gzip " ;
} else {
} else {
* compName = " none " ;
* compName = " none " ;
@ -322,9 +325,9 @@ int handleFile(int fd, const char *name, int compression_mode, int level, const
void finishArchive ( void )
void finishArchive ( void )
{
{
EspFsHeader h ;
EspFsHeader h ;
h . magic = htole32 ( ESP FS_MAGIC ) ;
h . magic = htole32 ( EFS_MAGIC ) ;
h . flags = FLAG_LASTFILE ;
h . flags = EFS_ FLAG_LASTFILE;
h . compression = COMPRESS_NONE ;
h . compression = EFS_ COMPRESS_NONE;
h . nameLen = 0 ;
h . nameLen = 0 ;
h . fileLenComp = 0 ;
h . fileLenComp = 0 ;
h . fileLenDecomp = 0 ;
h . fileLenDecomp = 0 ;
@ -339,7 +342,7 @@ void finishArchive(void)
*/
*/
void queueInputFile ( const char * name )
void queueInputFile ( const char * name )
{
{
fprintf ( stderr , " INFILE: %s \n " , name ) ;
espfs_dbg ( " INFILE: %s " , name ) ;
struct InputFileLinkedListEntry * tmp = malloc ( sizeof ( struct InputFileLinkedListEntry ) ) ;
struct InputFileLinkedListEntry * tmp = malloc ( sizeof ( struct InputFileLinkedListEntry ) ) ;
tmp - > name = strdup ( name ) ;
tmp - > name = strdup ( name ) ;
@ -354,48 +357,70 @@ void queueInputFile(const char *name)
}
}
}
}
int main ( int argc , char * * argv )
enum OpMode {
OM_INVALID = 0 ,
OM_PACK = ' P ' ,
OM_LIST = ' L ' ,
OM_EXTRACT = ' X ' ,
OM_EMBED = ' M ' ,
} ;
# define BUFLEN 1024
int main ( const int argc , char * * argv )
{
{
int f ;
int f ;
char inputFileName [ 1024 ] ;
char inputFileName [ BUFLEN ] ;
char tempbuf [ BUFLEN ] ;
struct stat statBuf ;
struct stat statBuf ;
int serr ;
int serr ;
int rate ;
int rate ;
int err = 0 ;
int compType ; //default compression type - heatshrink
int compType ; //default compression type - heatshrink
int compLvl = - 1 ;
int compLvl = - 1 ;
bool use_gzip = false ;
bool use_gzip = false ;
enum OpMode opmode = OM_INVALID ;
compType = COMPRESS_HEATSHRINK ;
compType = EFS_ COMPRESS_HEATSHRINK;
int c ;
int c ;
char * outfile = NULL ;
char * outFileName = NULL ;
char * c_outfile = NULL ;
char * c_varname = NULL ;
char * c_varname = NULL ;
char * parseFile = NULL ;
char * stripPath = NULL ;
char * stripPath = NULL ;
char * extractFile = NULL ;
char * extractFileName = NULL ;
size_t num_input_files = 0 ;
bool read_from_stdin = false ;
while ( 1 ) {
while ( 1 ) {
int option_index = 0 ;
int option_index = 0 ;
static struct option long_options [ ] = {
static struct option long_options [ ] = {
{ " parse " , required_argument , 0 , ' p ' } ,
// Help
{ " extract " , required_argument , 0 , ' e ' } ,
{ " compress " , required_argument , 0 , ' c ' } ,
{ " gzip " , no_argument , 0 , ' z ' } ,
{ " gzip-all " , no_argument , 0 , ' G ' } ,
{ " level " , required_argument , 0 , ' l ' } ,
{ " gzip-exts " , required_argument , 0 , ' g ' } ,
{ " input " , required_argument , 0 , ' i ' } ,
{ " output " , required_argument , 0 , ' o ' } ,
{ " c-output " , required_argument , 0 , ' C ' } ,
{ " c-varname " , required_argument , 0 , ' N ' } ,
{ " strip-path " , required_argument , 0 , ' S ' } ,
{ " help " , no_argument , 0 , ' h ' } ,
{ " help " , no_argument , 0 , ' h ' } ,
{ " version " , no_argument , 0 , ' V ' } ,
// Main operation (one at a time)
{ " pack " , no_argument , 0 , ' P ' } ,
{ " list " , no_argument , 0 , ' L ' } ,
{ " extract " , required_argument , 0 , ' X ' } ,
{ " embed " , no_argument , 0 , ' M ' } ,
// Common options
{ " input " , required_argument , 0 , ' i ' } , // input file; "-" to read them as lines on stdin; can be repeated in case of --pack.
// Input files can also be given as stand-alone arguments at the end, e.g. as a result of glob
{ " output " , required_argument , 0 , ' o ' } , // output file; "-" for stdout (default)
// Options for --pack
{ " compress " , required_argument , 0 , ' c ' } , // 0 = no, 1 = heatshrink (def)
{ " level " , required_argument , 0 , ' l ' } , // Heatshrink compression level 1-9, -1 = default
{ " gzip " , optional_argument , 0 , ' z ' } , // Gzipped extensions; no arg = default, "*" = all, comma-separated list = custom exts
{ " strip " , required_argument , 0 , ' s ' } , // path removed from all input files, e.g. when they are in a subfolder
// Options for --embed
{ " varname " , required_argument , 0 , ' n ' } , // name of the array; the length variable is {varname}_len
{ /* end marker */ }
{ /* end marker */ }
} ;
} ;
c = getopt_long ( argc , argv , " c:l:g:zGS:e:hp:C:i:o:0123456789 " ,
c = getopt_long ( argc , argv , " h?VPLX:Mi:o:c:l:z::s:n: " ,
long_options , & option_index ) ;
long_options , & option_index ) ;
if ( c = = - 1 ) {
if ( c = = - 1 ) {
break ;
break ;
@ -403,223 +428,356 @@ int main(int argc, char **argv)
switch ( c ) {
switch ( c ) {
case ' h ' :
case ' h ' :
goto show_help ;
case ' ? ' :
show_help ( 0 , argv ) ;
case ' z ' :
case ' V ' :
use_gzip = true ;
show_version ( argv ) ;
break ;
case ' 0 ' . . . ' 9 ' :
case ' P ' :
compLvl = c - ' 0 ' ;
opmode = OM_PACK ;
break ;
break ;
case ' p ' :
case ' L ' :
parseFile = strdup ( optarg ) ;
opmode = OM_LIST ;
break ;
break ;
case ' e ' :
case ' X ' :
extractFile = strdup ( optarg ) ;
opmode = OM_EXTRACT ;
if ( extractFileName ) {
espfs_error ( " can extract only one file at a time! " ) ;
exit ( 1 ) ;
}
extractFileName = strdup ( optarg ) ;
break ;
break ;
case ' C ' :
case ' M ' :
c_outfile = strdup ( optarg ) ;
opmode = OM_EMBED ;
break ;
break ;
case ' N ' :
case ' i ' :
c_varname = strdup ( optarg ) ;
if ( 0 = = strcmp ( optarg , " - " ) ) {
read_from_stdin = true ;
} else {
queueInputFile ( optarg ) ;
}
num_input_files + + ;
break ;
break ;
case ' S ' :
case ' o ' :
stripPath = strdup ( optarg ) ;
outFileName = strdup ( optarg ) ;
break ;
break ;
case ' c ' :
case ' c ' :
compType = atoi ( optarg ) ;
errno = 0 ;
compType = ( int ) strtol ( optarg , NULL , 10 ) ;
if ( errno ! = 0 | | compType < 0 | | compType > 1 ) {
espfs_error ( " Bad compression mode: %s " , optarg ) ;
exit ( 1 ) ;
}
break ;
break ;
case ' G ' :
case ' l ' :
use_gzip = true ;
errno = 0 ;
s_gzipAll = true ;
compLvl = ( int ) strtol ( optarg , NULL , 10 ) ;
if ( errno ! = 0 | | compLvl < 1 | | compLvl > 9 ) {
espfs_error ( " Bad compression level: %s " , optarg ) ;
exit ( 1 ) ;
}
break ;
break ;
case ' g ' :
case ' z ' :
use_gzip = true ;
use_gzip = true ;
parseGzipExtensions ( optarg ) ;
if ( optarg ) {
break ;
if ( 0 = = strcmp ( " * " , optarg ) ) {
s_gzipAll = true ;
case ' l ' :
} else {
compLvl = atoi ( optarg ) ;
parseGzipExtensions ( optarg ) ;
if ( compLvl < 1 | | compLvl > 9 ) {
}
fprintf ( stderr , " Bad compression level: %d \n " , compLvl ) ;
} else {
err = 1 ;
parseGzipExtensions ( strdup ( DEFAULT_GZIP_EXTS ) ) ; // memory leak! ehh
goto show_help ;
}
}
break ;
break ;
case ' i ' :
case ' s ' :
queueInputFile ( optarg ) ;
stripPath = strdup ( optarg ) ;
break ;
break ;
case ' o ' :
case ' n ' :
outfil e = strdup ( optarg ) ;
c_varnam e = strdup ( optarg ) ;
break ;
break ;
case ' ? ' :
goto show_help ;
default :
default :
fprintf ( stderr , " Unknown option: %c \n " , c ) ;
espfs_error ( " Unknown option: %c " , c ) ;
err = 1 ;
exit ( 1 ) ;
goto show_help ;
}
}
}
}
FILE * outfp = NULL ;
if ( ! use_gzip ) {
if ( outfile & & 0 ! = strcmp ( " - " , outfile ) ) {
s_gzipExtensions = NULL ;
fprintf ( stderr , " Writing to %s \n " , outfile ) ;
outfp = fopen ( outfile , " w+ " ) ;
if ( ! outfp ) {
perror ( outfile ) ;
return 1 ;
}
s_outFd = fileno ( outfp ) ;
ftruncate ( s_outFd , 0 ) ;
} else {
if ( outfile ) {
free ( outfile ) ;
outfile = NULL ;
}
fprintf ( stderr , " Writing to stdout \n \n " ) ;
}
}
if ( parseFile ) {
bool want_output ;
parseEspfsImage ( parseFile , extractFile , s_outFd ) ;
bool allows_multiple_inputs ;
exit ( 0 ) ;
}
switch ( opmode ) {
case OM_PACK :
want_output = true ;
allows_multiple_inputs = true ;
break ;
if ( s_gzipExtensions = = NULL & & use_gzip ) {
case OM_LIST :
parseGzipExtensions ( strdup ( DEFAULT_GZIP_EXTS ) ) ;
want_output = false ;
allows_multiple_inputs = false ;
break ;
case OM_EXTRACT :
case OM_EMBED :
want_output = true ;
allows_multiple_inputs = false ;
break ;
default :
espfs_error ( " Specify one of the operation modes: -P, -L, -X, -M " ) ;
exit ( 1 ) ;
}
}
if ( optind < argc ) {
while ( optind < argc ) {
/* Input */
queueInputFile ( argv [ optind + + ] ) ;
while ( optind < argc ) {
char * s = argv [ optind + + ] ;
if ( 0 = = strcmp ( s , " - " ) ) {
read_from_stdin = true ;
} else {
queueInputFile ( s ) ;
num_input_files + + ;
}
}
}
}
if ( ! s_inputFiles ) {
if ( num_input_files = = 0 & & read_from_stdin & & opmode = = OM_PACK ) {
fprintf ( stderr , " Reading input file names from stdin \n " ) ;
read_from_stdin = false ;
espfs_dbg ( " Reading input file names from stdin " ) ;
while ( fgets ( inputFileName , sizeof ( inputFileName ) , stdin ) ) {
while ( fgets ( inputFileName , sizeof ( inputFileName ) , stdin ) ) {
//Kill off '\n' at the end
//Kill off '\n' at the end
inputFileName [ strlen ( inputFileName ) - 1 ] = 0 ;
inputFileName [ strlen ( inputFileName ) - 1 ] = 0 ;
queueInputFile ( inputFileName ) ;
queueInputFile ( inputFileName ) ;
num_input_files + + ;
}
}
}
}
struct InputFileLinkedListEntry * entry = s_inputFiles ;
if ( ! read_from_stdin ) {
while ( entry ) {
if ( num_input_files = = 0 ) {
char * name = entry - > name ;
if ( allows_multiple_inputs ) {
//Only include files
espfs_error ( " Specify input file(s)! " ) ;
serr = stat ( name , & statBuf ) ;
if ( ( serr = = 0 ) & & S_ISREG ( statBuf . st_mode ) ) {
//Strip off './' or '/' madness.
char * embeddedName = name ;
f = open ( name , O_RDONLY ) ;
// relative path starting with ./, remove that
if ( embeddedName [ 0 ] = = ' . ' & & embeddedName [ 1 ] = = ' / ' ) {
embeddedName + = 2 ;
}
// remove prefix
if ( stripPath & & 0 = = strncmp ( embeddedName , stripPath , strlen ( stripPath ) ) ) {
embeddedName + = strlen ( stripPath ) ;
}
// remove leading slash, if any
if ( embeddedName [ 0 ] = = ' / ' ) { embeddedName + + ; }
if ( f > 0 ) {
const char * compName = " unknown " ;
rate = handleFile ( f , embeddedName , compType , compLvl , & compName ) ;
fprintf ( stderr , " %s (%d%%, %s) \n " , embeddedName , rate , compName ) ;
close ( f ) ;
} else {
} else {
perror ( name ) ;
espfs_error ( " Specify input file! " ) ;
}
}
} else if ( serr ! = 0 ) {
exit ( 1 ) ;
perror ( name ) ;
} else if ( ! allows_multiple_inputs & & num_input_files > 1 ) {
espfs_error ( " Mode %c requires exactly one input file! " , opmode ) ;
exit ( 1 ) ;
}
}
}
char * inFileName = read_from_stdin ? NULL : s_inputFiles - > name ;
entry = entry - > next ;
/* Output */
if ( ! want_output & & outFileName ) {
espfs_error ( " Output file is not allowed in %c mode! " , opmode ) ;
exit ( 1 ) ;
}
}
finishArchive ( ) ;
FILE * outFile = NULL ;
fsync ( s_outFd ) ;
bool write_to_stdout = outFileName & & ( 0 = = strcmp ( " - " , outFileName ) ) ;
if ( outFileName & & ! write_to_stdout ) {
espfs_dbg ( " Writing to %s " , outFileName ) ;
outFile = fopen ( outFileName , " w+ " ) ;
if ( ! outFile ) {
perror ( outFileName ) ;
return 1 ;
}
s_outFd = fileno ( outFile ) ;
ftruncate ( s_outFd , 0 ) ;
} else {
if ( outFileName ) {
free ( outFileName ) ;
outFileName = NULL ;
}
if ( want_output & & ! write_to_stdout ) {
espfs_error ( " Specify output file! Use -o - for stdout " ) ;
exit ( 1 ) ;
}
}
if ( outfp ) {
if ( outfile & & c_outfile ) {
const size_t imagelen = ( size_t ) lseek ( s_outFd , 0 , SEEK_END ) ;
lseek ( s_outFd , 0 , SEEK_SET ) ;
FILE * coutf = fopen ( c_outfile , " w+ " ) ;
/* Do it! */
if ( ! coutf ) {
perror ( c_outfile ) ;
switch ( opmode ) {
return 1 ;
case OM_PACK : {
struct InputFileLinkedListEntry * entry = s_inputFiles ;
while ( entry ) {
char * name = entry - > name ;
//Only include files
serr = stat ( name , & statBuf ) ;
if ( ( serr = = 0 ) & & S_ISREG ( statBuf . st_mode ) ) {
//Strip off './' or '/' madness.
char * embeddedName = name ;
f = open ( name , O_RDONLY ) ;
if ( f > 0 ) {
// relative path starting with ./, remove that
if ( embeddedName [ 0 ] = = ' . ' & & embeddedName [ 1 ] = = ' / ' ) {
embeddedName + = 2 ;
}
// remove prefix
if ( stripPath & & 0 = = strncmp ( embeddedName , stripPath , strlen ( stripPath ) ) ) {
embeddedName + = strlen ( stripPath ) ;
}
// remove leading slash, if any
if ( embeddedName [ 0 ] = = ' / ' ) { embeddedName + + ; }
const char * compName = " unknown " ;
rate = handleFile ( f , embeddedName , compType , compLvl , & compName ) ;
( void ) rate ;
espfs_dbg ( " %s (%d%%, %s) " , embeddedName , rate , compName ) ;
close ( f ) ;
} else {
snprintf ( tempbuf , BUFLEN , " Open %s for reading: %s " , name , strerror ( errno ) ) ;
espfs_error ( " %s " , tempbuf ) ;
exit ( 1 ) ;
}
} else if ( serr ! = 0 ) {
snprintf ( tempbuf , BUFLEN , " Stat %s: %s " , name , strerror ( errno ) ) ;
espfs_error ( " %s " , tempbuf ) ;
exit ( 1 ) ;
}
entry = entry - > next ;
}
finishArchive ( ) ;
fsync ( s_outFd ) ;
if ( outFile ) {
fclose ( outFile ) ;
}
} break ;
case OM_LIST : {
if ( ! inFileName ) {
espfs_error ( " Input file required! " ) ;
exit ( 1 ) ;
}
parseEspfsImage ( inFileName , NULL , s_outFd ) ;
} break ;
case OM_EXTRACT : {
if ( ! inFileName ) {
espfs_error ( " Input file required! " ) ;
exit ( 1 ) ;
}
parseEspfsImage ( inFileName , extractFileName , s_outFd ) ;
} break ;
case OM_EMBED : {
FILE * inFile = NULL ;
int inFD = STDIN_FILENO ;
if ( ! read_from_stdin ) {
inFile = fopen ( inFileName , " r " ) ;
if ( ! inFile ) {
snprintf ( tempbuf , BUFLEN , " Open %s for reading: %s " , inFileName , strerror ( errno ) ) ;
espfs_error ( " %s " , tempbuf ) ;
exit ( 1 ) ;
}
inFD = fileno ( inFile ) ;
}
}
int cfd = fileno ( coutf ) ;
ftruncate ( cfd , 0 ) ;
if ( ! c_varname ) {
if ( ! c_varname ) {
c_varname = strdup ( " espfs_image " ) ;
c_varname = strdup ( DEFAULT_C_VARNAME ) ;
}
}
fprintf ( coutf , " unsigned char %s[%lu] = { " , c_varname , imagelen ) ;
size_t len ;
for ( size_t i = 0 ; i < imagelen ; i + + ) {
len = ( size_t ) snprintf ( tempbuf , BUFLEN , " unsigned char %s[] = { " , c_varname ) ;
fprintf ( coutf , ( i % 16 ) ? " " : " \n " ) ;
write ( s_outFd , tempbuf , len ) ;
uint8_t u ;
uint8_t u ;
read ( s_outFd , & u , 1 ) ;
size_t imageLen = 0 ;
fprintf ( coutf , " 0x%02x, " , u ) ;
while ( 1 = = read ( inFD , & u , 1 ) ) {
len = ( size_t ) snprintf ( tempbuf , BUFLEN , " %s0x%02x, " , ( ( imageLen % 16 ) ? " " : " \n " ) , u ) ;
write ( s_outFd , tempbuf , len ) ;
imageLen + + ;
}
}
fprintf ( coutf , " \n }; \n unsigned int %s_len = %lu; \n " , c_varname , imagelen ) ;
len = ( size_t ) snprintf ( tempbuf , BUFLEN , " \n }; \n unsigned int %s_len = %lu; \n " , c_varname , imageLen ) ;
fsync ( cfd ) ;
write ( s_outFd , tempbuf , len ) ;
fclose ( coutf ) ;
}
fclose ( outfp ) ;
fsync ( s_outFd ) ;
if ( outFile ) {
fclose ( outFile ) ;
}
if ( inFile ) {
fclose ( inFile ) ;
}
} break ;
default :
__builtin_unreachable ( ) ;
}
}
return 0 ;
return 0 ;
}
__attribute__ ( ( noreturn ) )
void show_version ( char * * argv )
{
// to stdout
printf ( " %s #%s \n " , argv [ 0 ] , GIT_HASH ) ;
exit ( 0 ) ;
}
__attribute__ ( ( noreturn ) )
void show_help ( int retval , char * * argv )
{
// to stderr, this can happen as a response to bad args
show_help :
// ##########**********##########**********##########----------$$$$$$$$$$----------$$$$$$$$$$----------| 80 chars
// ##########**********##########**********##########----------$$$$$$$$$$----------$$$$$$$$$$----------| 80 chars
fprintf ( stderr , " %s - Program to create espfs images \n " , argv [ 0 ] ) ;
fprintf ( stderr , " %s #%s - Program to create and parse espfs images \n " , argv [ 0 ] , GIT_HASH ) ;
fprintf ( stderr , " Options: \n " ) ;
fprintf ( stderr , " \n "
fprintf ( stderr , " [-p|--parse FILE] \n "
" One main operation mode must be selected: \n "
" Parse an espfs file and show a list of its contents. No other options apply in this mode. \n " ) ;
" -P, --pack Create a binary fs image \n "
fprintf ( stderr , " [-e|--extract FILE] \n "
" -L, --list Read a binary fs image and show the contents \n "
" Extract a file with the given name from the parsed file (-p) \n " ) ;
" -X, --extract=NAME Read a binary fs image and extract a file with the given name \n "
fprintf ( stderr , " [-S|--strip-path PATH] \n "
" -M, --embed Read a binary file (typically the binary fs image produced by -P) and \n "
" Remove the given path from input file names before packing \n " ) ;
" convert it to C syntax byte array and a constant with its length. \n "
fprintf ( stderr , " [-c|--compress COMPRESSOR] \n "
" -h, -?, --help Show this help (has precedence over other options) \n "
" 0 - None, 1 - Heatshrink (default) \n " ) ;
" \n "
fprintf ( stderr , " [-l|--level LEVEL] or [-0 through -9] \n "
" Additional arguments specify the input, output and parameters: \n "
" compression level 1-9, higher is better but uses more RAM \n " ) ;
" -i, --input=FILE Input file name. Can be used multiple times. For convenience with globs, \n "
fprintf ( stderr , " [-z|--gzip] \n "
" input files can be given at the end of the option string without -i. \n "
" use gzip for files with extensions matching " DEFAULT_GZIP_EXTS " \n " ) ;
" The \" - \" filename means \" read from stdin \" : \n "
fprintf ( stderr , " [-G|--gzip-all] \n "
" - pack: reads file names to pack from stdin (e.g. piped from `find`) \n "
" use gzip for all files \n " ) ;
" - embed: read the binary file from stdin (e.g. piped from the --pack \n "
fprintf ( stderr , " [-g|--gzip-exts GZIPPED_EXTENSIONS] \n "
" option, avoiding the creation of a temporary binary file) \n "
" use gzip for files with custom extensions, comma-separated \n " ) ;
" Stdin reading is not supported for options --extract and --list. \n "
fprintf ( stderr , " [-i|--input FILE] \n "
" \n "
" Input file, can be multiple. Files can also be passed at the end without -i, or as lines on stdin \n "
" -o, --output=FILE Output file, \" - \" for stdout. \n "
" if not specified by args \n " ) ;
" Output can't be changed in --list mode. \n "
fprintf ( stderr , " [-o|--output FILE] \n "
" \n "
" Output file name; if not specified or equal to \" - \" , outputs to stdout \n " ) ;
" Pack options: \n "
fprintf ( stderr , " [-C|--c-output FILE] \n "
" -c, --compress=MODE Compression mode, 0=none, 1=heatshrink (default). Not used if the file \n "
" C embed output file name (an uint8_t array for embedding); can be used only if a binary output \n "
" is gzipped - additional compression wouldn't be useful. \n "
" file is specified as well (using -o), as this reads the output file and converts it as a second step. \n " ) ;
" -l, --level=LEVEL Compression level, 1-9, -1=default. 1=worst, 9=best compression, but uses \n "
fprintf ( stderr , " [-h|--help \n "
" more RAM to unpack. \n "
" Show help. \n \n " ) ;
" -s, --strip=PATH Strip a path prefix from all packed files (e.g. a subfolder name) \n "
exit ( err ) ;
" -z, --gzip[=EXTS] Enable gzip compression for some file types (filename extensions). \n "
" By default, enabled for " DEFAULT_GZIP_EXTS " . \n "
" Set EXTS to * to enable gzip for all files. Custom extensions are set as \n "
" a comma-separated list. \n "
" \n "
" Embed options: \n "
" -n, --varname=VARNAME Set custom array name to use in the generated C code. The length constant \n "
" is called {VARNAME}_len. The default VARNAME is \" " DEFAULT_C_VARNAME " \" \n "
) ;
exit ( retval ) ;
}
}