Home | History | Annotate | Download | only in 7z
      1 /* 7zMain.c - Test application for 7z Decoder
      2 2010-10-28 : Igor Pavlov : Public domain */
      3 
      4 #include <stdio.h>
      5 #include <string.h>
      6 
      7 #include "../../7z.h"
      8 #include "../../7zAlloc.h"
      9 #include "../../7zCrc.h"
     10 #include "../../7zFile.h"
     11 #include "../../7zVersion.h"
     12 
     13 #ifndef USE_WINDOWS_FILE
     14 /* for mkdir */
     15 #ifdef _WIN32
     16 #include <direct.h>
     17 #else
     18 #include <sys/stat.h>
     19 #include <errno.h>
     20 #endif
     21 #endif
     22 
     23 static ISzAlloc g_Alloc = { SzAlloc, SzFree };
     24 
     25 static int Buf_EnsureSize(CBuf *dest, size_t size)
     26 {
     27   if (dest->size >= size)
     28     return 1;
     29   Buf_Free(dest, &g_Alloc);
     30   return Buf_Create(dest, size, &g_Alloc);
     31 }
     32 
     33 #ifndef _WIN32
     34 
     35 static Byte kUtf8Limits[5] = { 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
     36 
     37 static Bool Utf16_To_Utf8(Byte *dest, size_t *destLen, const UInt16 *src, size_t srcLen)
     38 {
     39   size_t destPos = 0, srcPos = 0;
     40   for (;;)
     41   {
     42     unsigned numAdds;
     43     UInt32 value;
     44     if (srcPos == srcLen)
     45     {
     46       *destLen = destPos;
     47       return True;
     48     }
     49     value = src[srcPos++];
     50     if (value < 0x80)
     51     {
     52       if (dest)
     53         dest[destPos] = (char)value;
     54       destPos++;
     55       continue;
     56     }
     57     if (value >= 0xD800 && value < 0xE000)
     58     {
     59       UInt32 c2;
     60       if (value >= 0xDC00 || srcPos == srcLen)
     61         break;
     62       c2 = src[srcPos++];
     63       if (c2 < 0xDC00 || c2 >= 0xE000)
     64         break;
     65       value = (((value - 0xD800) << 10) | (c2 - 0xDC00)) + 0x10000;
     66     }
     67     for (numAdds = 1; numAdds < 5; numAdds++)
     68       if (value < (((UInt32)1) << (numAdds * 5 + 6)))
     69         break;
     70     if (dest)
     71       dest[destPos] = (char)(kUtf8Limits[numAdds - 1] + (value >> (6 * numAdds)));
     72     destPos++;
     73     do
     74     {
     75       numAdds--;
     76       if (dest)
     77         dest[destPos] = (char)(0x80 + ((value >> (6 * numAdds)) & 0x3F));
     78       destPos++;
     79     }
     80     while (numAdds != 0);
     81   }
     82   *destLen = destPos;
     83   return False;
     84 }
     85 
     86 static SRes Utf16_To_Utf8Buf(CBuf *dest, const UInt16 *src, size_t srcLen)
     87 {
     88   size_t destLen = 0;
     89   Bool res;
     90   Utf16_To_Utf8(NULL, &destLen, src, srcLen);
     91   destLen += 1;
     92   if (!Buf_EnsureSize(dest, destLen))
     93     return SZ_ERROR_MEM;
     94   res = Utf16_To_Utf8(dest->data, &destLen, src, srcLen);
     95   dest->data[destLen] = 0;
     96   return res ? SZ_OK : SZ_ERROR_FAIL;
     97 }
     98 #endif
     99 
    100 static SRes Utf16_To_Char(CBuf *buf, const UInt16 *s, int fileMode)
    101 {
    102   int len = 0;
    103   for (len = 0; s[len] != '\0'; len++);
    104 
    105   #ifdef _WIN32
    106   {
    107     int size = len * 3 + 100;
    108     if (!Buf_EnsureSize(buf, size))
    109       return SZ_ERROR_MEM;
    110     {
    111       char defaultChar = '_';
    112       BOOL defUsed;
    113       int numChars = WideCharToMultiByte(fileMode ?
    114           (
    115           #ifdef UNDER_CE
    116           CP_ACP
    117           #else
    118           AreFileApisANSI() ? CP_ACP : CP_OEMCP
    119           #endif
    120           ) : CP_OEMCP,
    121           0, s, len, (char *)buf->data, size, &defaultChar, &defUsed);
    122       if (numChars == 0 || numChars >= size)
    123         return SZ_ERROR_FAIL;
    124       buf->data[numChars] = 0;
    125       return SZ_OK;
    126     }
    127   }
    128   #else
    129   fileMode = fileMode;
    130   return Utf16_To_Utf8Buf(buf, s, len);
    131   #endif
    132 }
    133 
    134 static WRes MyCreateDir(const UInt16 *name)
    135 {
    136   #ifdef USE_WINDOWS_FILE
    137 
    138   return CreateDirectoryW(name, NULL) ? 0 : GetLastError();
    139 
    140   #else
    141 
    142   CBuf buf;
    143   WRes res;
    144   Buf_Init(&buf);
    145   RINOK(Utf16_To_Char(&buf, name, 1));
    146 
    147   res =
    148   #ifdef _WIN32
    149   _mkdir((const char *)buf.data)
    150   #else
    151   mkdir((const char *)buf.data, 0777)
    152   #endif
    153   == 0 ? 0 : errno;
    154   Buf_Free(&buf, &g_Alloc);
    155   return res;
    156 
    157   #endif
    158 }
    159 
    160 static WRes OutFile_OpenUtf16(CSzFile *p, const UInt16 *name)
    161 {
    162   #ifdef USE_WINDOWS_FILE
    163   return OutFile_OpenW(p, name);
    164   #else
    165   CBuf buf;
    166   WRes res;
    167   Buf_Init(&buf);
    168   RINOK(Utf16_To_Char(&buf, name, 1));
    169   res = OutFile_Open(p, (const char *)buf.data);
    170   Buf_Free(&buf, &g_Alloc);
    171   return res;
    172   #endif
    173 }
    174 
    175 static SRes PrintString(const UInt16 *s)
    176 {
    177   CBuf buf;
    178   SRes res;
    179   Buf_Init(&buf);
    180   res = Utf16_To_Char(&buf, s, 0);
    181   if (res == SZ_OK)
    182     fputs((const char *)buf.data, stdout);
    183   Buf_Free(&buf, &g_Alloc);
    184   return res;
    185 }
    186 
    187 static void UInt64ToStr(UInt64 value, char *s)
    188 {
    189   char temp[32];
    190   int pos = 0;
    191   do
    192   {
    193     temp[pos++] = (char)('0' + (unsigned)(value % 10));
    194     value /= 10;
    195   }
    196   while (value != 0);
    197   do
    198     *s++ = temp[--pos];
    199   while (pos);
    200   *s = '\0';
    201 }
    202 
    203 static char *UIntToStr(char *s, unsigned value, int numDigits)
    204 {
    205   char temp[16];
    206   int pos = 0;
    207   do
    208     temp[pos++] = (char)('0' + (value % 10));
    209   while (value /= 10);
    210   for (numDigits -= pos; numDigits > 0; numDigits--)
    211     *s++ = '0';
    212   do
    213     *s++ = temp[--pos];
    214   while (pos);
    215   *s = '\0';
    216   return s;
    217 }
    218 
    219 #define PERIOD_4 (4 * 365 + 1)
    220 #define PERIOD_100 (PERIOD_4 * 25 - 1)
    221 #define PERIOD_400 (PERIOD_100 * 4 + 1)
    222 
    223 static void ConvertFileTimeToString(const CNtfsFileTime *ft, char *s)
    224 {
    225   unsigned year, mon, day, hour, min, sec;
    226   UInt64 v64 = (ft->Low | ((UInt64)ft->High << 32)) / 10000000;
    227   Byte ms[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
    228   unsigned t;
    229   UInt32 v;
    230   sec = (unsigned)(v64 % 60); v64 /= 60;
    231   min = (unsigned)(v64 % 60); v64 /= 60;
    232   hour = (unsigned)(v64 % 24); v64 /= 24;
    233 
    234   v = (UInt32)v64;
    235 
    236   year = (unsigned)(1601 + v / PERIOD_400 * 400);
    237   v %= PERIOD_400;
    238 
    239   t = v / PERIOD_100; if (t ==  4) t =  3; year += t * 100; v -= t * PERIOD_100;
    240   t = v / PERIOD_4;   if (t == 25) t = 24; year += t * 4;   v -= t * PERIOD_4;
    241   t = v / 365;        if (t ==  4) t =  3; year += t;       v -= t * 365;
    242 
    243   if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
    244     ms[1] = 29;
    245   for (mon = 1; mon <= 12; mon++)
    246   {
    247     unsigned s = ms[mon - 1];
    248     if (v < s)
    249       break;
    250     v -= s;
    251   }
    252   day = (unsigned)v + 1;
    253   s = UIntToStr(s, year, 4); *s++ = '-';
    254   s = UIntToStr(s, mon, 2);  *s++ = '-';
    255   s = UIntToStr(s, day, 2);  *s++ = ' ';
    256   s = UIntToStr(s, hour, 2); *s++ = ':';
    257   s = UIntToStr(s, min, 2);  *s++ = ':';
    258   s = UIntToStr(s, sec, 2);
    259 }
    260 
    261 void PrintError(char *sz)
    262 {
    263   printf("\nERROR: %s\n", sz);
    264 }
    265 
    266 #ifdef USE_WINDOWS_FILE
    267 #define kEmptyAttribChar '.'
    268 static void GetAttribString(UInt32 wa, Bool isDir, char *s)
    269 {
    270   s[0] = (char)(((wa & FILE_ATTRIBUTE_DIRECTORY) != 0 || isDir) ? 'D' : kEmptyAttribChar);
    271   s[1] = (char)(((wa & FILE_ATTRIBUTE_READONLY) != 0) ? 'R': kEmptyAttribChar);
    272   s[2] = (char)(((wa & FILE_ATTRIBUTE_HIDDEN) != 0) ? 'H': kEmptyAttribChar);
    273   s[3] = (char)(((wa & FILE_ATTRIBUTE_SYSTEM) != 0) ? 'S': kEmptyAttribChar);
    274   s[4] = (char)(((wa & FILE_ATTRIBUTE_ARCHIVE) != 0) ? 'A': kEmptyAttribChar);
    275   s[5] = '\0';
    276 }
    277 #else
    278 static void GetAttribString(UInt32, Bool, char *s)
    279 {
    280   s[0] = '\0';
    281 }
    282 #endif
    283 
    284 int MY_CDECL main(int numargs, char *args[])
    285 {
    286   CFileInStream archiveStream;
    287   CLookToRead lookStream;
    288   CSzArEx db;
    289   SRes res;
    290   ISzAlloc allocImp;
    291   ISzAlloc allocTempImp;
    292   UInt16 *temp = NULL;
    293   size_t tempSize = 0;
    294 
    295   printf("\n7z ANSI-C Decoder " MY_VERSION_COPYRIGHT_DATE "\n\n");
    296   if (numargs == 1)
    297   {
    298     printf(
    299       "Usage: 7zDec <command> <archive_name>\n\n"
    300       "<Commands>\n"
    301       "  e: Extract files from archive (without using directory names)\n"
    302       "  l: List contents of archive\n"
    303       "  t: Test integrity of archive\n"
    304       "  x: eXtract files with full paths\n");
    305     return 0;
    306   }
    307   if (numargs < 3)
    308   {
    309     PrintError("incorrect command");
    310     return 1;
    311   }
    312 
    313   allocImp.Alloc = SzAlloc;
    314   allocImp.Free = SzFree;
    315 
    316   allocTempImp.Alloc = SzAllocTemp;
    317   allocTempImp.Free = SzFreeTemp;
    318 
    319   if (InFile_Open(&archiveStream.file, args[2]))
    320   {
    321     PrintError("can not open input file");
    322     return 1;
    323   }
    324 
    325   FileInStream_CreateVTable(&archiveStream);
    326   LookToRead_CreateVTable(&lookStream, False);
    327 
    328   lookStream.realStream = &archiveStream.s;
    329   LookToRead_Init(&lookStream);
    330 
    331   CrcGenerateTable();
    332 
    333   SzArEx_Init(&db);
    334   res = SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp);
    335   if (res == SZ_OK)
    336   {
    337     char *command = args[1];
    338     int listCommand = 0, testCommand = 0, extractCommand = 0, fullPaths = 0;
    339     if (strcmp(command, "l") == 0) listCommand = 1;
    340     else if (strcmp(command, "t") == 0) testCommand = 1;
    341     else if (strcmp(command, "e") == 0) extractCommand = 1;
    342     else if (strcmp(command, "x") == 0) { extractCommand = 1; fullPaths = 1; }
    343     else
    344     {
    345       PrintError("incorrect command");
    346       res = SZ_ERROR_FAIL;
    347     }
    348 
    349     if (res == SZ_OK)
    350     {
    351       UInt32 i;
    352 
    353       /*
    354       if you need cache, use these 3 variables.
    355       if you use external function, you can make these variable as static.
    356       */
    357       UInt32 blockIndex = 0xFFFFFFFF; /* it can have any value before first call (if outBuffer = 0) */
    358       Byte *outBuffer = 0; /* it must be 0 before first call for each new archive. */
    359       size_t outBufferSize = 0;  /* it can have any value before first call (if outBuffer = 0) */
    360 
    361       for (i = 0; i < db.db.NumFiles; i++)
    362       {
    363         size_t offset = 0;
    364         size_t outSizeProcessed = 0;
    365         const CSzFileItem *f = db.db.Files + i;
    366         size_t len;
    367         if (listCommand == 0 && f->IsDir && !fullPaths)
    368           continue;
    369         len = SzArEx_GetFileNameUtf16(&db, i, NULL);
    370 
    371         if (len > tempSize)
    372         {
    373           SzFree(NULL, temp);
    374           tempSize = len;
    375           temp = (UInt16 *)SzAlloc(NULL, tempSize * sizeof(temp[0]));
    376           if (temp == 0)
    377           {
    378             res = SZ_ERROR_MEM;
    379             break;
    380           }
    381         }
    382 
    383         SzArEx_GetFileNameUtf16(&db, i, temp);
    384         if (listCommand)
    385         {
    386           char attr[8], s[32], t[32];
    387 
    388           GetAttribString(f->AttribDefined ? f->Attrib : 0, f->IsDir, attr);
    389 
    390           UInt64ToStr(f->Size, s);
    391           if (f->MTimeDefined)
    392             ConvertFileTimeToString(&f->MTime, t);
    393           else
    394           {
    395             size_t j;
    396             for (j = 0; j < 19; j++)
    397               t[j] = ' ';
    398             t[j] = '\0';
    399           }
    400 
    401           printf("%s %s %10s  ", t, attr, s);
    402           res = PrintString(temp);
    403           if (res != SZ_OK)
    404             break;
    405           if (f->IsDir)
    406             printf("/");
    407           printf("\n");
    408           continue;
    409         }
    410         fputs(testCommand ?
    411             "Testing    ":
    412             "Extracting ",
    413             stdout);
    414         res = PrintString(temp);
    415         if (res != SZ_OK)
    416           break;
    417         if (f->IsDir)
    418           printf("/");
    419         else
    420         {
    421           res = SzArEx_Extract(&db, &lookStream.s, i,
    422               &blockIndex, &outBuffer, &outBufferSize,
    423               &offset, &outSizeProcessed,
    424               &allocImp, &allocTempImp);
    425           if (res != SZ_OK)
    426             break;
    427         }
    428         if (!testCommand)
    429         {
    430           CSzFile outFile;
    431           size_t processedSize;
    432           size_t j;
    433           UInt16 *name = (UInt16 *)temp;
    434           const UInt16 *destPath = (const UInt16 *)name;
    435           for (j = 0; name[j] != 0; j++)
    436             if (name[j] == '/')
    437             {
    438               if (fullPaths)
    439               {
    440                 name[j] = 0;
    441                 MyCreateDir(name);
    442                 name[j] = CHAR_PATH_SEPARATOR;
    443               }
    444               else
    445                 destPath = name + j + 1;
    446             }
    447 
    448           if (f->IsDir)
    449           {
    450             MyCreateDir(destPath);
    451             printf("\n");
    452             continue;
    453           }
    454           else if (OutFile_OpenUtf16(&outFile, destPath))
    455           {
    456             PrintError("can not open output file");
    457             res = SZ_ERROR_FAIL;
    458             break;
    459           }
    460           processedSize = outSizeProcessed;
    461           if (File_Write(&outFile, outBuffer + offset, &processedSize) != 0 || processedSize != outSizeProcessed)
    462           {
    463             PrintError("can not write output file");
    464             res = SZ_ERROR_FAIL;
    465             break;
    466           }
    467           if (File_Close(&outFile))
    468           {
    469             PrintError("can not close output file");
    470             res = SZ_ERROR_FAIL;
    471             break;
    472           }
    473           #ifdef USE_WINDOWS_FILE
    474           if (f->AttribDefined)
    475             SetFileAttributesW(destPath, f->Attrib);
    476           #endif
    477         }
    478         printf("\n");
    479       }
    480       IAlloc_Free(&allocImp, outBuffer);
    481     }
    482   }
    483   SzArEx_Free(&db, &allocImp);
    484   SzFree(NULL, temp);
    485 
    486   File_Close(&archiveStream.file);
    487   if (res == SZ_OK)
    488   {
    489     printf("\nEverything is Ok\n");
    490     return 0;
    491   }
    492   if (res == SZ_ERROR_UNSUPPORTED)
    493     PrintError("decoder doesn't support this archive");
    494   else if (res == SZ_ERROR_MEM)
    495     PrintError("can not allocate memory");
    496   else if (res == SZ_ERROR_CRC)
    497     PrintError("CRC error");
    498   else
    499     printf("\nERROR #%d\n", res);
    500   return 1;
    501 }
    502