main.c 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. #include <stdint.h>
  2. #include <unistd.h>
  3. #include <sys/types.h>
  4. #include <sys/stat.h>
  5. #include <fcntl.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include "espfs.h"
  10. #ifdef __MINGW32__
  11. #include "mman-win32/mman.h"
  12. #else
  13. #include <sys/mman.h>
  14. #endif
  15. #ifdef __WIN32__
  16. #include <winsock2.h>
  17. #else
  18. #include <arpa/inet.h>
  19. #endif
  20. #include "espfsformat.h"
  21. //Gzip
  22. #ifdef ESPFS_GZIP
  23. // If compiler complains about missing header, try running "sudo apt-get install zlib1g-dev"
  24. // to install missing package.
  25. #include <zlib.h>
  26. #endif
  27. //Routines to convert host format to the endianness used in the xtensa
  28. short htoxs(short in) {
  29. char r[2];
  30. r[0]=in;
  31. r[1]=in>>8;
  32. return *((short *)r);
  33. }
  34. int htoxl(int in) {
  35. unsigned char r[4];
  36. r[0]=in;
  37. r[1]=in>>8;
  38. r[2]=in>>16;
  39. r[3]=in>>24;
  40. return *((int *)r);
  41. }
  42. #ifdef ESPFS_GZIP
  43. size_t compressGzip(char *in, int insize, char *out, int outsize, int level) {
  44. z_stream stream;
  45. int zresult;
  46. stream.zalloc = Z_NULL;
  47. stream.zfree = Z_NULL;
  48. stream.opaque = Z_NULL;
  49. stream.next_in = in;
  50. stream.avail_in = insize;
  51. stream.next_out = out;
  52. stream.avail_out = outsize;
  53. // 31 -> 15 window bits + 16 for gzip
  54. zresult = deflateInit2 (&stream, level, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY);
  55. if (zresult != Z_OK) {
  56. fprintf(stderr, "DeflateInit2 failed with code %d\n", zresult);
  57. exit(1);
  58. }
  59. zresult = deflate(&stream, Z_FINISH);
  60. if (zresult != Z_STREAM_END) {
  61. fprintf(stderr, "Deflate failed with code %d\n", zresult);
  62. exit(1);
  63. }
  64. zresult = deflateEnd(&stream);
  65. if (zresult != Z_OK) {
  66. fprintf(stderr, "DeflateEnd failed with code %d\n", zresult);
  67. exit(1);
  68. }
  69. return stream.total_out;
  70. }
  71. char **gzipExtensions = NULL;
  72. int shouldCompressGzip(char *name) {
  73. char *ext = name + strlen(name);
  74. while (*ext != '.') {
  75. ext--;
  76. if (ext < name) {
  77. // no dot in file name -> no extension -> nothing to match against
  78. return 0;
  79. }
  80. }
  81. ext++;
  82. int i = 0;
  83. while (gzipExtensions[i] != NULL) {
  84. if (strcmp(ext,gzipExtensions[i]) == 0) {
  85. return 1;
  86. }
  87. i++;
  88. }
  89. return 0;
  90. }
  91. int parseGzipExtensions(char *input) {
  92. char *token;
  93. char *extList = input;
  94. int count = 2; // one for first element, second for terminator
  95. // count elements
  96. while (*extList != 0) {
  97. if (*extList == ',') count++;
  98. extList++;
  99. }
  100. // split string
  101. extList = input;
  102. gzipExtensions = malloc(count * sizeof(char*));
  103. count = 0;
  104. token = strtok(extList, ",");
  105. while (token) {
  106. gzipExtensions[count++] = token;
  107. token = strtok(NULL, ",");
  108. }
  109. // terminate list
  110. gzipExtensions[count] = NULL;
  111. return 1;
  112. }
  113. #endif
  114. int handleFile(int f, char *name, int compression, int level, char **compName, off_t *csizePtr) {
  115. char *fdat, *cdat;
  116. off_t size, csize;
  117. EspFsHeader h;
  118. int nameLen;
  119. int8_t flags = 0;
  120. size=lseek(f, 0, SEEK_END);
  121. fdat=mmap(NULL, size, PROT_READ, MAP_SHARED, f, 0);
  122. if (fdat==MAP_FAILED) {
  123. perror("mmap");
  124. return 0;
  125. }
  126. #ifdef ESPFS_GZIP
  127. if (shouldCompressGzip(name)) {
  128. csize = size*3;
  129. if (csize<100) // gzip has some headers that do not fit when trying to compress small files
  130. csize = 100; // enlarge buffer if this is the case
  131. cdat=malloc(csize);
  132. csize=compressGzip(fdat, size, cdat, csize, level);
  133. compression = COMPRESS_NONE;
  134. flags = FLAG_GZIP;
  135. } else
  136. #endif
  137. if (compression==COMPRESS_NONE) {
  138. csize=size;
  139. cdat=fdat;
  140. } else {
  141. fprintf(stderr, "Unknown compression - %d\n", compression);
  142. exit(1);
  143. }
  144. if (csize>size) {
  145. //Compressing enbiggened this file. Revert to uncompressed store.
  146. compression=COMPRESS_NONE;
  147. csize=size;
  148. cdat=fdat;
  149. flags=0;
  150. }
  151. //Fill header data
  152. h.magic=('E'<<0)+('S'<<8)+('f'<<16)+('s'<<24);
  153. h.flags=flags;
  154. h.compression=compression;
  155. h.nameLen=nameLen=strlen(name)+1;
  156. if (h.nameLen&3) h.nameLen+=4-(h.nameLen&3); //Round to next 32bit boundary
  157. h.nameLen=htoxs(h.nameLen);
  158. h.fileLenComp=htoxl(csize);
  159. h.fileLenDecomp=htoxl(size);
  160. write(1, &h, sizeof(EspFsHeader));
  161. write(1, name, nameLen);
  162. while (nameLen&3) {
  163. write(1, "\000", 1);
  164. nameLen++;
  165. }
  166. write(1, cdat, csize);
  167. //Pad out to 32bit boundary
  168. while (csize&3) {
  169. write(1, "\000", 1);
  170. csize++;
  171. }
  172. munmap(fdat, size);
  173. if (compName != NULL) {
  174. if (h.compression==COMPRESS_NONE) {
  175. if (h.flags & FLAG_GZIP) {
  176. *compName = "gzip";
  177. } else {
  178. *compName = "none";
  179. }
  180. } else {
  181. *compName = "unknown";
  182. }
  183. }
  184. *csizePtr = csize;
  185. return (csize*100)/size;
  186. }
  187. //Write final dummy header with FLAG_LASTFILE set.
  188. void finishArchive() {
  189. EspFsHeader h;
  190. h.magic=('E'<<0)+('S'<<8)+('f'<<16)+('s'<<24);
  191. h.flags=FLAG_LASTFILE;
  192. h.compression=COMPRESS_NONE;
  193. h.nameLen=htoxs(0);
  194. h.fileLenComp=htoxl(0);
  195. h.fileLenDecomp=htoxl(0);
  196. write(1, &h, sizeof(EspFsHeader));
  197. }
  198. int main(int argc, char **argv) {
  199. int f, x;
  200. char fileName[1024];
  201. char *realName;
  202. struct stat statBuf;
  203. int serr;
  204. int rate;
  205. int err=0;
  206. int compType; //default compression type - heatshrink
  207. int compLvl=-1;
  208. compType = COMPRESS_NONE;
  209. for (x=1; x<argc; x++) {
  210. if (strcmp(argv[x], "-c")==0 && argc>=x-2) {
  211. compType=atoi(argv[x+1]);
  212. x++;
  213. } else if (strcmp(argv[x], "-l")==0 && argc>=x-2) {
  214. compLvl=atoi(argv[x+1]);
  215. if (compLvl<1 || compLvl>9) err=1;
  216. x++;
  217. #ifdef ESPFS_GZIP
  218. } else if (strcmp(argv[x], "-g")==0 && argc>=x-2) {
  219. if (!parseGzipExtensions(argv[x+1])) err=1;
  220. x++;
  221. #endif
  222. } else {
  223. err=1;
  224. }
  225. }
  226. #ifdef ESPFS_GZIP
  227. if (gzipExtensions == NULL) {
  228. parseGzipExtensions(strdup("html,css,js,ico"));
  229. }
  230. #endif
  231. if (err) {
  232. fprintf(stderr, "%s - Program to create espfs images\n", argv[0]);
  233. fprintf(stderr, "Usage: \nfind | %s [-c compressor] [-l compression_level] ", argv[0]);
  234. #ifdef ESPFS_GZIP
  235. fprintf(stderr, "[-g gzipped_extensions] ");
  236. #endif
  237. fprintf(stderr, "> out.espfs\n");
  238. fprintf(stderr, "Compressors:\n");
  239. fprintf(stderr, "0 - None(default)\n");
  240. 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");
  241. #ifdef ESPFS_GZIP
  242. fprintf(stderr, "\nGzipped extensions: list of comma separated, case sensitive file extensions \nthat will be gzipped. Defaults to 'html,css,js'\n");
  243. #endif
  244. exit(0);
  245. }
  246. #ifdef __WIN32__
  247. setmode(fileno(stdout), _O_BINARY);
  248. #endif
  249. while(fgets(fileName, sizeof(fileName), stdin)) {
  250. //Kill off '\n' at the end
  251. fileName[strlen(fileName)-1]=0;
  252. //Only include files
  253. serr=stat(fileName, &statBuf);
  254. if ((serr==0) && S_ISREG(statBuf.st_mode)) {
  255. //Strip off './' or '/' madness.
  256. realName=fileName;
  257. if (fileName[0]=='.') realName++;
  258. if (realName[0]=='/') realName++;
  259. f=open(fileName, O_RDONLY);
  260. if (f>0) {
  261. char *compName = "unknown";
  262. off_t csize;
  263. rate=handleFile(f, realName, compType, compLvl, &compName, &csize);
  264. fprintf(stderr, "%-16s (%3d%%, %s, %4u bytes)\n", realName, rate, compName, (uint32_t)csize);
  265. close(f);
  266. } else {
  267. perror(fileName);
  268. }
  269. } else {
  270. if (serr!=0) {
  271. perror(fileName);
  272. }
  273. }
  274. }
  275. finishArchive();
  276. return 0;
  277. }