├── README.md ├── dxArRead.c ├── dxArchiveRead.c ├── dxCpioRead.c └── dxTarRead.c /README.md: -------------------------------------------------------------------------------- 1 | # dx Achive readers 2 | A minimalistic non compressed archive file readers written in ANSI C. Supported formats: [GNU tar](http://www.gnu.org/software/tar/manual/html_node/Standard.html) (tape archive) and [PAX](http://pubs.opengroup.org/onlinepubs/009695299/utilities/pax.html) in tar-compatibility mode, [GNU ar](https://en.wikipedia.org/wiki/Ar_(Unix)), [Cpio](https://en.wikipedia.org/wiki/Cpio) (binary for little- and big-endian machines, old an new ACSII). 3 | 4 | * public domain 5 | * single file with one function for each format ([tar](dxTarRead.c), [ar](dxArRead.c), binary [cpio](dxCpioRead.c)) 6 | * less than 50 lines of code, including comments, license and blanks 7 | * or one file for [all formats](dxArchiveRead.c) - more complicated, but more powerful 8 | * easy to use, just returns a pointer to your file inside archive 9 | * no dependencies (even stdlibc not used), embedded friendly 10 | * requires no memory/malloc or copying, uses only what is passed in 11 | * optimized for high speed routing of small archive files, stops parsing upon match 12 | * designed to work in pair with [tinfl.c](https://github.com/richgel999/miniz/blob/master/tinfl.c) from [miniz](https://github.com/richgel999/miniz) 13 | 14 | ## Functions: 15 | 16 | ````c 17 | const char* dxTarRead(const void* tarData, const long tarSize, 18 | const char* fileName, long* fileSize) 19 | ```` 20 | 21 | Function returns pointer to `tarData`, where file data stored. If your file size can be divided evenly by 512, the data will be not NULL terminated! 22 | `fileSize` will be `0`, if there is no such file in the archive. `fileSize` will be `0` too if the archive is broken or unsupported. 23 | 24 | For usage example see the [dxTarRead.c](dxTarRead.c). 25 | 26 | ````c 27 | const char* dxArRead(const void* arData, const long arSize, 28 | const char* fileName, long* fileSize) 29 | ```` 30 | The same. Data will *never* be NULL-terminated. 31 | 32 | ````c 33 | const char* dxCpioRead(const void* cpioData, const long cpioSize, 34 | const char* fileName, long* fileSize) 35 | ```` 36 | The same. Binary format only. If you need an ASCII-ones - use [dxArchiveRead](dxArchiveRead.c). Data will be NULL terminated only if `fileSize` is odd. 37 | 38 | ````c 39 | const char* dxArchiveIterate(long* pp, const void* archiveData, 40 | const long archiveSize, long* fileSize, long* nameLength, long* dataDelta) 41 | ```` 42 | Available only in [dxArchiveRead.c](dxArchiveRead.c). Per-file iterator. `pp` - is the iterator itself, points to new file in archive. Just init it to `0` before using. `fileSize`, `nameLength` and `dataDelta` are function outputs, all optional, can be replaced by `NULL`. `nameLength` is a name string length. Equals to `strlen(name)` in general case. `dataDelta` - pointer to current file content from the start of archive in bytes. See [dxArchiveRead.c](dxArchiveRead.c) for usage example. 43 | 44 | ## Building example: 45 | Just define `DXTARREAD_EXAMPLE` or `DXARREAD_EXAMPLE` or `DXCPIOREAD_EXAMPLE` and compile: 46 | ```bash 47 | gcc -DDXTARREAD_EXAMPLE -Wall dxTarRead.c -o dxTarReadExample 48 | ``` 49 | 50 | ## File naming and folders: 51 | The `fileName` string must be the same, as archieving-time arguments. Example: 52 | ````bash 53 | tar -cvf myarchive.tar file1.txt dir/subfile.txt ./file2.txt ./dir2/* 54 | ar -cq myarchive.ar file1.txt dir/subfile.txt ./file2.txt ./dir2/file9.txt 55 | find . -depth -print | cpio -o > /path/myarchive.cpio 56 | ```` 57 | The `dxTarRead` will find `file1.txt` correctly, but not `./file1.txt`. The `dir/subfile.txt` is correct too. To find a file inside of `dir2`: `./dir2/file`. 58 | 59 | You can get only files in directories, but not directory listing in TAR. The function will find `dir/`, but it's `fileSize` will be `0`. CPIO works the same. 60 | 61 | The `dxArRead` will find `file1.txt`, `file2.txt` and `file9.txt` correctly. But *not* `./file2.txt`, `dir2/.file9.txt` or `./dir2/file9.txt`. 62 | 63 | There is no directories in AR. 64 | 65 | ## Supported versions 66 | Only [GNU tar](http://www.gnu.org/software/tar/manual/html_node/Standard.html), GNU/System5 ar and GNU Cpio (binary, odc, newc). It's most modern formats, used by default in Linux (checked in Ubuntu and Arch Linux). Ar and Cpio may work on other format specifications, but not tested. 67 | 68 | ## Supported compilers 69 | The code is not compiler dependent. The `sizeof(char)` must be `1` on your compiler. Theoretically, you can use it with any compiler and operation system (OpenCL for example). 70 | 71 | Tested compilers: GCC, Clang, TCC, Visual Studio. 72 | 73 | ## Credits: 74 | Developed by Dmitry Hrabrov a.k.a. DeXPeriX and every direct or indirect contributor to the GitHub. 75 | 76 | ## License: 77 | This software is dual-licensed to the public domain and under the following license: you are granted a perpetual, irrevocable license to copy, modify, publish and distribute this file as you see fit 78 | -------------------------------------------------------------------------------- /dxArRead.c: -------------------------------------------------------------------------------- 1 | /* 2 | dxArRead - 0.4 - public domain 3 | no warrenty implied; use at your own risk. 4 | authored from 2017 by Dmitry Hrabrov a.k.a. DeXPeriX 5 | http://dexperix.net 6 | 7 | LICENSE: 8 | This software is dual-licensed to the public domain and under the following 9 | license: you are granted a perpetual, irrevocable license to copy, modify, 10 | publish and distribute this file as you see fit. 11 | */ 12 | const char* dxArRead(const void* arData, const long arSize, 13 | const char* fileName, long* fileSize) 14 | { 15 | const int NAME_OFFSET = 0, SIZE_OFFSET = 48, NL_OFFSET = 59; 16 | const int BLOCK_SIZE = 60, NAME_SIZE = 16, SZ_SIZE = 10, MAGIC_SIZE = 8; 17 | const char MAGIC[] = "!\n"; /* ar's magic const */ 18 | const char* ar = (const char*) arData; /* From "void*" to "char*" */ 19 | long fsize, mul, i, p = 0, found = 0, newOffset = 0, sizeEnd; 20 | 21 | *fileSize = 0; /* will be zero if AR wrong or there is no such file */ 22 | for(p=0; p=0; mul*=10, i--) 34 | if( (sz[i]>='1') && (sz[i] <= '9') ) fsize += (sz[i] - '0') * mul; 35 | 36 | /* Offset size in bytes. Depends on file size and AR's block size */ 37 | newOffset = (fsize % 2)? (fsize + BLOCK_SIZE +1): (fsize + BLOCK_SIZE); 38 | 39 | i = 0; /* strncmp - compare file's name with that a user wants */ 40 | while((i 0) && (fileName[i] == 0) ) found = 1; 42 | } while( !found && (p + newOffset + BLOCK_SIZE <= arSize) ); 43 | if( found ){ 44 | *fileSize = fsize; 45 | return ar + p + BLOCK_SIZE; /* skip header, point to data */ 46 | } else return 0; /* No file found in AR - return NULL */ 47 | } 48 | 49 | 50 | #if defined(DXARREAD_EXAMPLE) 51 | #include 52 | #include 53 | 54 | int main(int argc, char **argv){ 55 | long fsize, mysize, bytes_read; 56 | FILE* f; 57 | char* data; 58 | const char* myfile; 59 | 60 | if(argc < 2){ 61 | printf( 62 | "dxArRead.c usage example. Public domain.\n" 63 | "Usage: dxArReadExample arFile inArFile\n" 64 | ); 65 | return 0; 66 | } 67 | 68 | f = fopen(argv[1], "rb"); 69 | fseek(f, 0, SEEK_END); 70 | fsize = ftell(f); 71 | fseek(f, 0, SEEK_SET); 72 | 73 | data = (char*) malloc(fsize + 1); 74 | bytes_read = fread(data, 1, fsize, f); 75 | fclose(f); 76 | if( bytes_read != fsize ){ 77 | puts("File was not read correctly!"); 78 | free(data); 79 | return -1; 80 | } 81 | data[fsize] = 0; 82 | 83 | myfile = dxArRead(data, fsize, argv[2], &mysize); 84 | if( myfile ){ 85 | printf("File content: '"); 86 | fwrite(myfile, 1, mysize, stdout); 87 | printf("'\nFile size: %ld\n", mysize); 88 | }else printf("No file '%s' in AR archive or archive incorrect\n",argv[2]); 89 | free(data); /* free data after you finished using ar reader */ 90 | return 0; 91 | } 92 | #endif /* DXARREAD_EXAMPLE */ 93 | -------------------------------------------------------------------------------- /dxArchiveRead.c: -------------------------------------------------------------------------------- 1 | /* 2 | dxArchiveRead - 0.8 - public domain 3 | no warrenty implied; use at your own risk. 4 | authored from 2017 by Dmitry Hrabrov a.k.a. DeXPeriX, http://dexperix.net 5 | 6 | LICENSE: This software is dual-licensed to the public domain and under the 7 | following license: you are granted a perpetual, irrevocable license to 8 | copy, modify, publish and distribute this file as you see fit. 9 | */ 10 | const char* dxArchiveIterate(long* pp, const void* archiveData, 11 | const long archiveSize, long* fileSize, long* nameLength, long* dataDelta) 12 | { 13 | const int FILE_SZ = 0, NAME_SZ = 1, BIN_CPIO = 1, NEWC_CPIO = 3, GNUAR = 5; 14 | static const char bm[][2] = { {0xC7, 0x71}, {0x71, 0xC7}, {0x60, 0x0A} }; 15 | enum { MAGS=0,MAGO,MUL,BSIZE,FNAME,SZOF,SZSIZE,NLOF,NLSIZE,DIVTO,BIT,IDC}; 16 | static const int inf[6][IDC]={ /* Format constants. 0-3 - CPIO variants */ 17 | /* bin,le */{2, 0, 10, 26, 26, 22, 0, 20, 0, 2, 0}, 18 | /* bin,be */{2, 0, 10, 26, 26, 22, 0, 20, 0, 2, 1}, 19 | /* odc */{6, 0, 8, 76, 76, 65, 11, 59, 6, 0, 1}, 20 | /* newc */{6, 0, 16, 110, 110, 54, 8, 94, 8, 4, 0}, 21 | /* tar/pax*/{5, 257, 8, 512, 0, 124, 11, 0, 0, 512, 0}, 22 | /* GNU ar */{2, 58, 10, 60, 0, 48, 10, 0, 0, 2, 0}}; 23 | const char* MAGIC[6] = { bm[0], bm[1], "070707", "070701", "ustar", bm[2]}; 24 | const unsigned char* u = (const unsigned char*) archiveData; 25 | long fmt = -1, p = *pp, i, j, mul, v[2]; 26 | 27 | if( (p==0) && (u[0]=='!') && (u[1]=='<') && (u[2]=='a') ){ p = 8; *pp = 8;} 28 | for(j=0; j<6; j++){ /* Loop on formats */ 29 | for(i=0, mul=1; i= 0 ){ /* Magic was correct. Let's work with archive! */ 36 | const int* cf = inf[fmt]; /* Pointer to format's offsets etc */ 37 | const char* name = (const char*)u + cf[FNAME] + p; /* file name */ 38 | const unsigned char* s[2] = { u + p + cf[SZOF], u + p + cf[NLOF] }; 39 | const long strSizes[2] = { cf[SZSIZE], cf[NLSIZE] }; 40 | long newOffset = 0; 41 | if( name == 0 ) return 0; 42 | 43 | /* Convert file and name size from string */ 44 | v[NAME_SZ] = 0; v[FILE_SZ] = 0; /* integer sizes of name and file*/ 45 | for(j=0; j<2; j++){ /* get it from strings, stored in s */ 46 | long k = strSizes[j]-1; while( s[j][k] == ' ' ) k--; 47 | for(i=k, mul=1; i>=0; mul *= cf[MUL], i--){ /* str to int */ 48 | if((s[j][i]>='1') && (s[j][i]<='9')) v[j]+=(s[j][i]-'0') * mul; 49 | if((s[j][i]>='a') && (s[j][i]<='f')) v[j]+=(s[j][i]-'a'+10)*mul; 50 | if((s[j][i]>='A') && (s[j][i]<='F')) v[j]+=(s[j][i]-'A'+10)*mul; 51 | } 52 | } 53 | /* Calculating offset size */ 54 | if( fmt <= BIN_CPIO ){ /* Binary CPIO's specific stuff */ 55 | long b = 1 - cf[BIT], s = !b, k = p + cf[SZOF]; 56 | v[NAME_SZ] = (u[p+cf[NLOF] + b] << 8) | (u[p+cf[NLOF] + s]); 57 | if( (v[NAME_SZ] % 2) != 0 ) v[NAME_SZ]++; 58 | v[FILE_SZ] = (u[k+b]<<24)|(u[k+s]<<16)|(u[k+2+b]<<8)|(u[k+2+s]); 59 | } else if( fmt == NEWC_CPIO ){ /* New ascii CPIO = newc */ 60 | i = (cf[BSIZE] + v[NAME_SZ]) % 4; 61 | if( i > 0 ) v[NAME_SZ] += 4 - i; 62 | } 63 | j = cf[DIVTO]; /* file size additional offset */ 64 | if( (j>1) && (v[FILE_SZ]%j)>0 ) newOffset += j - v[FILE_SZ]%j; 65 | newOffset += v[FILE_SZ] + v[NAME_SZ] + cf[BSIZE]; 66 | 67 | j = v[NAME_SZ]; /* Calculating name length if not stored in archive */ 68 | mul = (fmt == GNUAR)? '/': 0; /* file name in GNU ar ends with '/' */ 69 | while( (name[j] != 0) && (name[j] != (char)mul) ) j++; 70 | 71 | if( fileSize ) *fileSize = v[FILE_SZ]; 72 | if( dataDelta ) *dataDelta = p + inf[fmt][BSIZE] + v[NAME_SZ]; 73 | if( nameLength ) *nameLength = j; /* correct pointer - assign value */ 74 | *pp += newOffset; /* Main iteration: increment p to offset */ 75 | return name; 76 | } else return 0; /* Format is not known */ 77 | } 78 | 79 | const char* dxArchiveRead(const void* archiveData, const long archiveSize, 80 | const char* fileName, long* fileSize) 81 | { 82 | const char* u = (const char*)archiveData; 83 | long p = 0, found = 0, curFileSize = 0, dataDelta; 84 | 85 | *fileSize = 0; /* will be zero if archive wrong or there is no such file */ 86 | do{ 87 | const char* name = dxArchiveIterate(&p, archiveData, archiveSize, 88 | &curFileSize, 0, &dataDelta); 89 | long i = 0; /* strcmp */ 90 | if( !name ) break; 91 | while( (fileName[i]!=0) && (name[i]==fileName[i]) ) i++; 92 | if( (i > 0) && ((name[i]==0) || (name[i]=='/')) && 93 | (fileName[i] == 0) ) found = 1; 94 | } while( !found && (p < archiveSize) ); 95 | if( found ){ 96 | *fileSize = curFileSize; 97 | return u + dataDelta; 98 | } else return 0; /* No file found in archive - return NULL */ 99 | } 100 | 101 | 102 | #if defined(DXARCHIVEREAD_EXAMPLE) 103 | #include 104 | #include 105 | 106 | int main(int argc, char **argv){ 107 | long fsize, bytes_read; 108 | FILE* f; 109 | char* data; 110 | 111 | if(argc < 2){ 112 | printf( 113 | "dxArchiveRead.c usage example. Public domain. \n" 114 | "Usage: \n" 115 | " Get archive's file list: \n" 116 | " dxArchiveReadExample archiveName \n" 117 | " Get the file's content: \n" 118 | " dxArchiveReadExample archiveName inArchiveFile \n" 119 | ); 120 | return 0; 121 | } 122 | 123 | f = fopen(argv[1], "rb"); 124 | fseek(f, 0, SEEK_END); 125 | fsize = ftell(f); 126 | fseek(f, 0, SEEK_SET); 127 | 128 | data = (char*) malloc(fsize + 1); 129 | bytes_read = fread(data, 1, fsize, f); 130 | fclose(f); 131 | if( bytes_read != fsize ){ 132 | printf("File was not read correctly!\n"); 133 | free(data); 134 | return -1; 135 | } 136 | data[fsize] = 0; 137 | 138 | if(argc < 3){ 139 | /* File list */ 140 | const char* name = 0; 141 | long p = 0, curSize, nameLen; 142 | printf("Archive content: \n"); 143 | do { 144 | name = dxArchiveIterate(&p, data, fsize, &curSize, &nameLen, NULL); 145 | if(name) printf("%.*s (%ld bytes)\n", (int)nameLen, name, curSize); 146 | } while( name != NULL ); 147 | } else { 148 | /* Concrete file content */ 149 | long mysize; 150 | const char* myfile = dxArchiveRead(data, fsize, argv[2], &mysize); 151 | if( myfile ){ 152 | printf("File content: \n"); 153 | fwrite(myfile, mysize, 1, stdout); 154 | printf("\nFile size: %ld \n", mysize); 155 | } else printf("No file '%s' in archive. Or bad archive! \n", argv[2]); 156 | } 157 | free(data); /* free data after you finished using archive reader */ 158 | return 0; 159 | } 160 | #endif /* DXARCHIVEREAD_EXAMPLE */ 161 | -------------------------------------------------------------------------------- /dxCpioRead.c: -------------------------------------------------------------------------------- 1 | /* 2 | dxCpioRead - 0.5 - public domain 3 | no warrenty implied; use at your own risk. 4 | authored from 2017 by Dmitry Hrabrov a.k.a. DeXPeriX 5 | http://dexperix.net 6 | 7 | LICENSE: 8 | This software is dual-licensed to the public domain and under the following 9 | license: you are granted a perpetual, irrevocable license to copy, modify, 10 | publish and distribute this file as you see fit. 11 | */ 12 | const char* dxCpioRead(const void* cpioData, const long cpioSize, 13 | const char* fileName, long* fileSize) 14 | { 15 | const int BLOCK_SIZE = 26, NAME_LEN_OFFSET = 20, SIZE_OFFSET = 22; 16 | const unsigned char MAGIC[] = {0xC7, 0x71}; /* =070707oct, little endian */ 17 | const unsigned char* u = (const unsigned char*) cpioData; 18 | long p = 0, found = 0, newOffset = 0, nameLen = 0, fsize = 0; 19 | 20 | *fileSize = 0; /* will be zero if CPIO wrong or there is no such file */ 21 | do { /* "Load" data from CPIO - just point to passed memory*/ 22 | const unsigned char* name = u + BLOCK_SIZE + p + newOffset; 23 | long b = 1, s = 0, i; /* big and small byte offset */ 24 | p += newOffset; /* pointer to current file's data in CPIO */ 25 | 26 | if( !( (u[p+0] == MAGIC[0]) && (u[p+1] == MAGIC[1]) ) ){ 27 | if((u[p+1] == MAGIC[0]) && (u[p+0] == MAGIC[0])){ 28 | b = 0; s = 1; /* big endian */ 29 | } else return 0; /* = return NULL; magic constant not found */ 30 | } 31 | 32 | nameLen = (u[p+NAME_LEN_OFFSET+b] << 8) | (u[p+NAME_LEN_OFFSET+s]); 33 | i = p + SIZE_OFFSET; 34 | fsize = (u[i+b] << 24) | (u[i+s] << 16) | (u[i+2+b] << 8) | (u[i+2+s]); 35 | 36 | /* Offset size in bytes. Depends on file size and CPIO's block size */ 37 | if( (nameLen % 2) != 0 ) nameLen++; /* odd name = +1 NULL byte */ 38 | newOffset = nameLen + fsize + BLOCK_SIZE; 39 | if( (fsize % 2) != 0 ) newOffset++; /* odd size = +1 NULL byte */ 40 | 41 | i = 0; /* strncmp - compare file's name with that a user wants */ 42 | while((i 0) && (name[i] == 0) && (fileName[i] == 0) ) found = 1; 44 | } while( !found && (p + newOffset + BLOCK_SIZE <= cpioSize) ); 45 | if( found ){ 46 | *fileSize = fsize; 47 | return (const char*)u + p + BLOCK_SIZE + nameLen; /* skip header */ 48 | } else return 0; /* No file found in CPIO - return NULL */ 49 | } 50 | 51 | 52 | #if defined(DXCPIOREAD_EXAMPLE) 53 | #include 54 | #include 55 | 56 | int main(int argc, char **argv){ 57 | long fsize, mysize, bytes_read; 58 | FILE* f; 59 | char* data; 60 | const char* myfile; 61 | 62 | if(argc < 2){ 63 | printf( 64 | "dxCpioRead.c usage example. Public domain.\n" 65 | "Usage: dxCpioReadExample cpioFile inCpioFile\n" 66 | ); 67 | return 0; 68 | } 69 | 70 | f = fopen(argv[1], "rb"); 71 | fseek(f, 0, SEEK_END); 72 | fsize = ftell(f); 73 | fseek(f, 0, SEEK_SET); 74 | 75 | data = (char*) malloc(fsize + 1); 76 | bytes_read = fread(data, 1, fsize, f); 77 | fclose(f); 78 | if( bytes_read != fsize ){ 79 | printf("File was not read correctly!\n"); 80 | free(data); 81 | return -1; 82 | } 83 | data[fsize] = 0; 84 | 85 | myfile = dxCpioRead(data, fsize, argv[2], &mysize); 86 | if( myfile ){ 87 | printf("File content:\n"); 88 | fwrite(myfile, mysize, 1, stdout); 89 | printf("\nFile size: %ld\n", mysize); 90 | }else printf("No file '%s' in archive. Or incorrect archive\n",argv[2]); 91 | free(data); /* free data after you finished using CPIO reader */ 92 | return 0; 93 | } 94 | #endif /* DXCPIOREAD_EXAMPLE */ 95 | -------------------------------------------------------------------------------- /dxTarRead.c: -------------------------------------------------------------------------------- 1 | /* 2 | dxTarRead - 0.91 - public domain 3 | no warrenty implied; use at your own risk. 4 | authored from 2017 by Dmitry Hrabrov a.k.a. DeXPeriX 5 | http://dexperix.net 6 | 7 | LICENSE: 8 | This software is dual-licensed to the public domain and under the following 9 | license: you are granted a perpetual, irrevocable license to copy, modify, 10 | publish and distribute this file as you see fit. 11 | */ 12 | const char* dxTarRead(const void* tarData, const long tarSize, 13 | const char* fileName, long* fileSize) 14 | { 15 | const int NAME_OFFSET = 0, SIZE_OFFSET = 124, MAGIC_OFFSET = 257; 16 | const int BLOCK_SIZE = 512, NAME_SIZE = 100, SZ_SIZE = 12, MAGIC_SIZE = 5; 17 | const char MAGIC[] = "ustar"; /* Modern GNU tar's magic const */ 18 | const char* tar = (const char*) tarData; /* From "void*" to "char*" */ 19 | long size, mul, i, p = 0, found = 0, newOffset = 0; 20 | 21 | *fileSize = 0; /* will be zero if TAR wrong or there is no such file */ 22 | do { /* "Load" data from tar - just point to passed memory*/ 23 | const char* name = tar + NAME_OFFSET + p + newOffset; 24 | const char* sz = tar + SIZE_OFFSET + p + newOffset; /* size string */ 25 | p += newOffset; /* pointer to current file's data in TAR */ 26 | 27 | for(i=0; i=0; mul*=8, i--) /* Octal str to int */ 32 | if( (sz[i]>='0') && (sz[i] <= '9') ) size += (sz[i] - '0') * mul; 33 | 34 | /* Offset size in bytes. Depends on file size and TAR's block size */ 35 | newOffset = (1 + size/BLOCK_SIZE) * BLOCK_SIZE; /* trim by block */ 36 | if( (size % BLOCK_SIZE) > 0 ) newOffset += BLOCK_SIZE; 37 | 38 | i = 0; /* strncmp - compare file's name with that a user wants */ 39 | while((i 0) && (name[i] == 0) && (fileName[i] == 0) ) found = 1; 41 | } while( !found && (p + newOffset + BLOCK_SIZE <= tarSize) ); 42 | if( found ){ 43 | *fileSize = size; 44 | return tar + p + BLOCK_SIZE; /* skip header, point to data */ 45 | } else return 0; /* No file found in TAR - return NULL */ 46 | } 47 | 48 | 49 | #if defined(DXTARREAD_EXAMPLE) 50 | #include 51 | #include 52 | 53 | int main(int argc, char **argv){ 54 | long fsize, mysize, bytes_read; 55 | FILE* f; 56 | char* data; 57 | const char* myfile; 58 | 59 | if(argc < 2){ 60 | printf( 61 | "dxTarRead.c usage example. Public domain.\n" 62 | "Usage: dxTarReadExample tarFile inTarFile\n" 63 | ); 64 | return 0; 65 | } 66 | 67 | f = fopen(argv[1], "rb"); 68 | fseek(f, 0, SEEK_END); 69 | fsize = ftell(f); 70 | fseek(f, 0, SEEK_SET); 71 | 72 | data = (char*) malloc(fsize + 1); 73 | bytes_read = fread(data, 1, fsize, f); 74 | fclose(f); 75 | if( bytes_read != fsize ){ 76 | puts("File was not read correctly!"); 77 | free(data); 78 | return -1; 79 | } 80 | data[fsize] = 0; 81 | 82 | myfile = dxTarRead(data, fsize, argv[2], &mysize); 83 | if( myfile ){ 84 | puts("File content:"); 85 | fwrite(myfile, 1, mysize, stdout); 86 | printf("\nFile size: %ld\n", mysize); 87 | }else printf("No file '%s' in TAR archive or archive incorrect\n",argv[2]); 88 | free(data); /* free data after you finished using tar reader */ 89 | return 0; 90 | } 91 | #endif /* DXTARREAD_EXAMPLE */ 92 | --------------------------------------------------------------------------------