123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- #include <stdint.h>
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include "espfs.h"
- #ifdef __MINGW32__
- #include "mman-win32/mman.h"
- #else
- #include <sys/mman.h>
- #endif
- #ifdef __WIN32__
- #include <winsock2.h>
- #else
- #include <arpa/inet.h>
- #endif
- #include "espfsformat.h"
- //Gzip
- #ifdef ESPFS_GZIP
- // If compiler complains about missing header, try running "sudo apt-get install zlib1g-dev"
- // to install missing package.
- #include <zlib.h>
- #endif
- //Routines to convert host format to the endianness used in the xtensa
- short htoxs(short in) {
- char r[2];
- r[0]=in;
- r[1]=in>>8;
- return *((short *)r);
- }
- int htoxl(int in) {
- unsigned char r[4];
- r[0]=in;
- r[1]=in>>8;
- r[2]=in>>16;
- r[3]=in>>24;
- return *((int *)r);
- }
- #ifdef ESPFS_GZIP
- size_t compressGzip(char *in, int insize, char *out, int outsize, int level) {
- z_stream stream;
- int zresult;
- stream.zalloc = Z_NULL;
- stream.zfree = Z_NULL;
- stream.opaque = Z_NULL;
- stream.next_in = in;
- stream.avail_in = insize;
- stream.next_out = out;
- stream.avail_out = outsize;
- // 31 -> 15 window bits + 16 for gzip
- zresult = deflateInit2 (&stream, level, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY);
- if (zresult != Z_OK) {
- fprintf(stderr, "DeflateInit2 failed with code %d\n", zresult);
- exit(1);
- }
- zresult = deflate(&stream, Z_FINISH);
- if (zresult != Z_STREAM_END) {
- fprintf(stderr, "Deflate failed with code %d\n", zresult);
- exit(1);
- }
- zresult = deflateEnd(&stream);
- if (zresult != Z_OK) {
- fprintf(stderr, "DeflateEnd failed with code %d\n", zresult);
- exit(1);
- }
- return stream.total_out;
- }
- char **gzipExtensions = NULL;
- int shouldCompressGzip(char *name) {
- char *ext = name + strlen(name);
- while (*ext != '.') {
- ext--;
- if (ext < name) {
- // no dot in file name -> no extension -> nothing to match against
- return 0;
- }
- }
- ext++;
- int i = 0;
- while (gzipExtensions[i] != NULL) {
- if (strcmp(ext,gzipExtensions[i]) == 0) {
- return 1;
- }
- i++;
- }
- return 0;
- }
- int parseGzipExtensions(char *input) {
- char *token;
- char *extList = input;
- int count = 2; // one for first element, second for terminator
- // count elements
- while (*extList != 0) {
- if (*extList == ',') count++;
- extList++;
- }
- // split string
- extList = input;
- gzipExtensions = malloc(count * sizeof(char*));
- count = 0;
- token = strtok(extList, ",");
- while (token) {
- gzipExtensions[count++] = token;
- token = strtok(NULL, ",");
- }
- // terminate list
- gzipExtensions[count] = NULL;
- return 1;
- }
- #endif
- int handleFile(int f, char *name, int compression, int level, char **compName, off_t *csizePtr) {
- char *fdat, *cdat;
- off_t size, csize;
- EspFsHeader h;
- int nameLen;
- int8_t flags = 0;
- size=lseek(f, 0, SEEK_END);
- fdat=mmap(NULL, size, PROT_READ, MAP_SHARED, f, 0);
- if (fdat==MAP_FAILED) {
- perror("mmap");
- return 0;
- }
- #ifdef ESPFS_GZIP
- if (shouldCompressGzip(name)) {
- csize = size*3;
- if (csize<100) // gzip has some headers that do not fit when trying to compress small files
- csize = 100; // enlarge buffer if this is the case
- cdat=malloc(csize);
- csize=compressGzip(fdat, size, cdat, csize, level);
- compression = COMPRESS_NONE;
- flags = FLAG_GZIP;
- } else
- #endif
- if (compression==COMPRESS_NONE) {
- csize=size;
- cdat=fdat;
- } else {
- fprintf(stderr, "Unknown compression - %d\n", compression);
- exit(1);
- }
- if (csize>size) {
- //Compressing enbiggened this file. Revert to uncompressed store.
- compression=COMPRESS_NONE;
- csize=size;
- cdat=fdat;
- flags=0;
- }
- //Fill header data
- h.magic=('E'<<0)+('S'<<8)+('f'<<16)+('s'<<24);
- h.flags=flags;
- h.compression=compression;
- h.nameLen=nameLen=strlen(name)+1;
- if (h.nameLen&3) h.nameLen+=4-(h.nameLen&3); //Round to next 32bit boundary
- h.nameLen=htoxs(h.nameLen);
- h.fileLenComp=htoxl(csize);
- h.fileLenDecomp=htoxl(size);
- write(1, &h, sizeof(EspFsHeader));
- write(1, name, nameLen);
- while (nameLen&3) {
- write(1, "\000", 1);
- nameLen++;
- }
- write(1, cdat, csize);
- //Pad out to 32bit boundary
- while (csize&3) {
- write(1, "\000", 1);
- csize++;
- }
- munmap(fdat, size);
- if (compName != NULL) {
- if (h.compression==COMPRESS_NONE) {
- if (h.flags & FLAG_GZIP) {
- *compName = "gzip";
- } else {
- *compName = "none";
- }
- } else {
- *compName = "unknown";
- }
- }
- *csizePtr = csize;
- return (csize*100)/size;
- }
- //Write final dummy header with FLAG_LASTFILE set.
- void finishArchive() {
- EspFsHeader h;
- h.magic=('E'<<0)+('S'<<8)+('f'<<16)+('s'<<24);
- h.flags=FLAG_LASTFILE;
- h.compression=COMPRESS_NONE;
- h.nameLen=htoxs(0);
- h.fileLenComp=htoxl(0);
- h.fileLenDecomp=htoxl(0);
- write(1, &h, sizeof(EspFsHeader));
- }
- int main(int argc, char **argv) {
- int f, x;
- char fileName[1024];
- char *realName;
- struct stat statBuf;
- int serr;
- int rate;
- int err=0;
- int compType; //default compression type - heatshrink
- int compLvl=-1;
- compType = COMPRESS_NONE;
- for (x=1; x<argc; x++) {
- if (strcmp(argv[x], "-c")==0 && argc>=x-2) {
- compType=atoi(argv[x+1]);
- x++;
- } else if (strcmp(argv[x], "-l")==0 && argc>=x-2) {
- compLvl=atoi(argv[x+1]);
- if (compLvl<1 || compLvl>9) err=1;
- x++;
- #ifdef ESPFS_GZIP
- } else if (strcmp(argv[x], "-g")==0 && argc>=x-2) {
- if (!parseGzipExtensions(argv[x+1])) err=1;
- x++;
- #endif
- } else {
- err=1;
- }
- }
- #ifdef ESPFS_GZIP
- if (gzipExtensions == NULL) {
- parseGzipExtensions(strdup("html,css,js,ico"));
- }
- #endif
- if (err) {
- fprintf(stderr, "%s - Program to create espfs images\n", argv[0]);
- fprintf(stderr, "Usage: \nfind | %s [-c compressor] [-l compression_level] ", argv[0]);
- #ifdef ESPFS_GZIP
- fprintf(stderr, "[-g gzipped_extensions] ");
- #endif
- fprintf(stderr, "> out.espfs\n");
- fprintf(stderr, "Compressors:\n");
- fprintf(stderr, "0 - None(default)\n");
- fprintf(stderr, "\nCompression level: 1 is worst but low RAM usage, higher is better compression \nbut uses more ram on decompression. -1 = compressors default.\n");
- #ifdef ESPFS_GZIP
- fprintf(stderr, "\nGzipped extensions: list of comma separated, case sensitive file extensions \nthat will be gzipped. Defaults to 'html,css,js'\n");
- #endif
- exit(0);
- }
- #ifdef __WIN32__
- setmode(fileno(stdout), _O_BINARY);
- #endif
- while(fgets(fileName, sizeof(fileName), stdin)) {
- //Kill off '\n' at the end
- fileName[strlen(fileName)-1]=0;
- //Only include files
- serr=stat(fileName, &statBuf);
- if ((serr==0) && S_ISREG(statBuf.st_mode)) {
- //Strip off './' or '/' madness.
- realName=fileName;
- if (fileName[0]=='.') realName++;
- if (realName[0]=='/') realName++;
- f=open(fileName, O_RDONLY);
- if (f>0) {
- char *compName = "unknown";
- off_t csize;
- rate=handleFile(f, realName, compType, compLvl, &compName, &csize);
- fprintf(stderr, "%-16s (%3d%%, %s, %4u bytes)\n", realName, rate, compName, (uint32_t)csize);
- close(f);
- } else {
- perror(fileName);
- }
- } else {
- if (serr!=0) {
- perror(fileName);
- }
- }
- }
- finishArchive();
- return 0;
- }
|