home *** CD-ROM | disk | FTP | other *** search
/ DOS/V Power Report 2001 December (DVD) / VPR0112A.ISO / OLS / UNRAR32008 / unrar32008.lzh / src.lzh / src / rar.cxx < prev    next >
C/C++ Source or Header  |  2001-08-12  |  20KB  |  866 lines

  1. /*
  2.  *   Copyright (c) 1998-2001 T. Kamei (kamei@jsdlab.co.jp)
  3.  *
  4.  *   Permission to use, copy, modify, and distribute this software
  5.  * and its documentation for any purpose is hereby granted provided
  6.  * that the above copyright notice and this permission notice appear
  7.  * in all copies of the software and related documentation.
  8.  *
  9.  *                          NO WARRANTY
  10.  *
  11.  *   THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY WARRANTIES;
  12.  * WITHOUT EVEN THE IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS
  13.  * FOR A PARTICULAR PURPOSE.
  14.  */
  15.  
  16. #include <windows.h>
  17. #include <mbstring.h>
  18. #include <stdio.h>
  19. #include "comm-arc.h"
  20. #include "util.h"
  21. #include "unrarapi.h"
  22. #include "rar.h"
  23. #include "dialog.h"
  24.  
  25. int
  26. UnRAR::open_err (int e) const
  27. {
  28.   switch (e)
  29.     {
  30.     case ERAR_NO_MEMORY:
  31.       format (IDS_NOT_ENOUGH_MEMORY);
  32.       return ERROR_ENOUGH_MEMORY;
  33.  
  34.     case ERAR_BAD_DATA:
  35.       format (IDS_ARCHIVE_HEADER_BROKEN, u_path);
  36.       return ERROR_HEADER_BROKEN;
  37.  
  38.     case ERAR_BAD_ARCHIVE:
  39.       format (IDS_NOT_VALID_RAR_ARCHIVE, u_path);
  40.       return ERROR_FILE_STYLE;
  41.  
  42.     case ERAR_EOPEN:
  43.       format (IDS_FILE_OPEN_ERROR, u_path);
  44.       return ERROR_NOT_FIND_ARC_FILE;
  45.  
  46.     default:
  47.       format (IDS_UNDOCUMENTED, e, u_path);
  48.       return ERROR_UNEXPECTED;
  49.     }
  50. }
  51.  
  52. int
  53. UnRAR::process_err (int e, const char *path) const
  54. {
  55.   switch (e)
  56.     {
  57.     case ERAR_BAD_DATA:
  58.       format (IDS_CRC_ERROR, path);
  59.       return ERROR_FILE_CRC;
  60.  
  61.     case ERAR_BAD_ARCHIVE:
  62.       format (IDS_NOT_VALID_RAR_VOLUME, path);
  63.       return ERROR_FILE_STYLE;
  64.  
  65.     case ERAR_UNKNOWN_FORMAT:
  66.       format (IDS_UNKNOWN_ARCHIVE_FORMAT, path);
  67.       return ERROR_FILE_STYLE;
  68.  
  69.     case ERAR_EOPEN:
  70.       format (IDS_VOLUME_OPEN, path);
  71.       return ERROR_ARC_FILE_OPEN;
  72.  
  73.     case ERAR_ECREATE:
  74.       format (IDS_FILE_CREATE_ERROR, path);
  75.       return ERROR_FILE_OPEN;
  76.  
  77.     case ERAR_ECLOSE:
  78.       format (IDS_FILE_CLOSE_ERROR, path);
  79.       return ERROR_CANNOT_WRITE;
  80.  
  81.     case ERAR_EREAD:
  82.       format (IDS_READ_ERROR, path);
  83.       return ERROR_CANNOT_READ;
  84.  
  85.     case ERAR_EWRITE:
  86.       format (IDS_WRITE_ERROR, path);
  87.       return ERROR_CANNOT_WRITE;
  88.  
  89.     default:
  90.       format (IDS_UNDOCUMENTED, e, path);
  91.       return ERROR_UNEXPECTED;
  92.     }
  93. }
  94.  
  95. int
  96. UnRAR::header_err (int e) const
  97. {
  98.   switch (e)
  99.     {
  100.     case ERAR_END_ARCHIVE:
  101.       return 0;
  102.  
  103.     case ERAR_BAD_DATA:
  104.       format (IDS_FILE_HEADER_BROKEN, u_path);
  105.       return ERROR_HEADER_BROKEN;
  106.  
  107.     default:
  108.       format (IDS_UNDOCUMENTED, e, u_path);
  109.       return ERROR_UNEXPECTED;
  110.     }
  111. }
  112.  
  113. int
  114. UnRAR::format (const char *fmt, ...) const
  115. {
  116.   va_list ap;
  117.   va_start (ap, fmt);
  118.   int x = u_ostr.formatv (fmt, ap);
  119.   va_end (ap);
  120.   return x;
  121. }
  122.  
  123. int
  124. UnRAR::format (int id, ...) const
  125. {
  126.   char fmt[1024];
  127.   if (!LoadString (lstate.hinst, id, fmt, sizeof fmt))
  128.     return format ("Unable to load message string: %d\n", id);
  129.   else
  130.     {
  131.       va_list ap;
  132.       va_start (ap, id);
  133.       int x = u_ostr.formatv (fmt, ap);
  134.       va_end (ap);
  135.       return x;
  136.     }
  137. }
  138.  
  139. int
  140. UnRAR::parse_opt (int ac, char **av)
  141. {
  142.   u_cmd = C_NOTDEF;
  143.   u_opt = 0;
  144.   u_type = UT_ASK;
  145.   u_dest = "";
  146.   u_passwd = 0;
  147.   u_path = 0;
  148.  
  149.   if (!ac)
  150.     {
  151.       format (IDS_NO_ARCHIVE_FILE);
  152.       return ERROR_NOT_ARC_FILE;
  153.     }
  154.  
  155.   int c = av[0][0] == '-' ? av[0][1] : av[0][0];
  156.   switch (c)
  157.     {
  158.     case 'x':
  159.       u_cmd = C_EXTRACT;
  160.       break;
  161.  
  162.     case 'e':
  163.       u_cmd = C_EXTRACT_NODIR;
  164.       break;
  165.  
  166.     case 't':
  167.       u_cmd = C_TEST;
  168.       break;
  169.  
  170.     case 'p':
  171.       u_cmd = C_PRINT;
  172.       break;
  173.  
  174.     case 'l':
  175.       u_cmd = C_LIST;
  176.       break;
  177.  
  178.     case 'v':
  179.       u_cmd = C_VLIST;
  180.       break;
  181.  
  182.     case 'c':
  183.       u_cmd = C_COMMENT;
  184.       break;
  185.  
  186.     default:
  187.       format (IDS_UNRECOGNIZED_OPTION, c);
  188.       return ERROR_COMMAND_NAME;
  189.     }
  190.  
  191.   for (int i = 1; i < ac && av[i][0] == '-'; i++)
  192.     switch (av[i][1])
  193.       {
  194.       case 'r':
  195.         u_opt |= O_RECURSIVE;
  196.         break;
  197.  
  198.       case 'y':
  199.         u_opt |= O_YES;
  200.         break;
  201.  
  202.       case 'o':
  203.         u_type = av[i][2] == '-' ? UT_SKIP : UT_OVWRT;
  204.         break;
  205.  
  206.       case 'f':
  207.         u_type = UT_EXISTING;
  208.         break;
  209.  
  210.       case 'u':
  211.         u_type = UT_NEWER;
  212.         break;
  213.  
  214.       case 's':
  215.         u_opt |= O_STRICT;
  216.         break;
  217.  
  218.       case 'p':
  219.         if (av[i][2])
  220.           u_passwd = &av[i][2];
  221.         else if (++i < ac)
  222.           u_passwd = av[i];
  223.         else
  224.           {
  225.             format (IDS_OPTION_P_REQ_ARGS);
  226.             return ERROR_COMMAND_NAME;
  227.           }
  228.         break;
  229.  
  230.       case '-':
  231.         i++;
  232.         goto optend;
  233.  
  234.       default:
  235.         format (IDS_UNRECOGNIZED_OPTION, av[i][1]);
  236.         return ERROR_COMMAND_NAME;
  237.       }
  238. optend:
  239.   if (i >= ac)
  240.     {
  241.       format (IDS_NO_ARCHIVE_FILE);
  242.       return ERROR_NOT_ARC_FILE;
  243.     }
  244.   u_path = av[i++];
  245.  
  246.   if (i < ac)
  247.     {
  248.       char *sl = find_last_slash (av[i]);
  249.       if (sl && !sl[1])
  250.         {
  251.           u_dest = av[i++];
  252.           if (strlen (u_dest) >= FNAME_MAX32 - MAX_PATH)
  253.             {
  254.               format (IDS_FILE_NAME_TOO_LONG, u_dest);
  255.               return ERROR_LONG_FILE_NAME;
  256.             }
  257.         }
  258.     }
  259.  
  260.   u_glob.set_pattern (ac - i, av + i);
  261.  
  262.   return 0;
  263. }
  264.  
  265. class dyn_handle
  266. {
  267. private:
  268.   void operator = (const dyn_handle &);
  269.   dyn_handle (const dyn_handle &);
  270. protected:
  271.   HANDLE h;
  272. public:
  273.   dyn_handle () : h (INVALID_HANDLE_VALUE) {}
  274.   dyn_handle (HANDLE h_) : h (h_) {}
  275.   ~dyn_handle () {if (valid ()) CloseHandle (h);}
  276.   int valid () const {return h != INVALID_HANDLE_VALUE;}
  277.   operator HANDLE () const {return h;}
  278.   void fix (HANDLE h_) {h = h_;}
  279.   void close ()
  280.     {
  281.       if (valid ())
  282.         {
  283.           CloseHandle (h);
  284.           h = INVALID_HANDLE_VALUE;
  285.         }
  286.     }
  287. };
  288.  
  289. class write_handle: public dyn_handle
  290. {
  291.   int w_complete;
  292.   int w_delete_if_fail;
  293.   const char *w_path;
  294. public:
  295.   write_handle (const char *);
  296.   ~write_handle ();
  297.   void complete () {w_complete = 1;}
  298.   int open ();
  299.   int ensure_room (LONG);
  300. };
  301.  
  302. write_handle::write_handle (const char *path)
  303.      : w_complete (0), w_delete_if_fail (0), w_path (path)
  304. {
  305. }
  306.  
  307. write_handle::~write_handle ()
  308. {
  309.   if (!w_complete && valid () && w_delete_if_fail)
  310.     {
  311.       close ();
  312.       DeleteFile (w_path);
  313.     }
  314. }
  315.  
  316. int
  317. write_handle::ensure_room (LONG size)
  318. {
  319.   if (SetFilePointer (*this, size, 0, FILE_BEGIN) == -1
  320.       || !SetEndOfFile (*this))
  321.     return 0;
  322.  
  323.   w_delete_if_fail = 1;
  324.  
  325.   if (SetFilePointer (*this, 0, 0, FILE_BEGIN) == -1)
  326.     return 0;
  327.   return 1;
  328. }
  329.  
  330. int
  331. write_handle::open ()
  332. {
  333.   fix (CreateFile (w_path, GENERIC_WRITE, 0, 0, OPEN_ALWAYS,
  334.                    FILE_ATTRIBUTE_ARCHIVE | FILE_FLAG_SEQUENTIAL_SCAN, 0));
  335.   if (!valid ())
  336.     return 0;
  337.   w_delete_if_fail = GetLastError () == NO_ERROR;
  338.   return 1;
  339. }
  340.  
  341. int
  342. UnRAR::check_timestamp (const char *path, const RARHeaderData &hd)
  343. {
  344.   WIN32_FIND_DATA fd;
  345.   HANDLE h = FindFirstFile (path, &fd);
  346.   if (h != INVALID_HANDLE_VALUE)
  347.     FindClose (h);
  348.  
  349.   switch (u_type)
  350.     {
  351.     case UT_ASK:
  352.       if (h != INVALID_HANDLE_VALUE && !(u_opt & O_YES))
  353.         {
  354.           replace_param r;
  355.           r.old_name = path;
  356.           FILETIME ft;
  357.           FileTimeToLocalFileTime (&fd.ftLastWriteTime, &ft);
  358.           WORD d, t;
  359.           FileTimeToDosDateTime (&ft, &d, &t);
  360.           r.old_date = (DWORD (d) << 16) + t;
  361.           r.old_size = fd.nFileSizeLow;
  362.           r.new_name = hd.FileName;
  363.           r.new_date = hd.FileTime;
  364.           r.new_size = hd.UnpSize;
  365.           switch (replace_dialog (u_hwnd, r))
  366.             {
  367.             case IDYES:
  368.               return 1;
  369.             case IDNO:
  370.               return 0;
  371.             case IDC_ALL:
  372.               u_opt |= O_YES;
  373.               return 1;
  374.             default:
  375.               return -1;
  376.             }
  377.         }
  378.       break;
  379.  
  380.     case UT_OVWRT:
  381.       return 1;
  382.  
  383.     case UT_SKIP:
  384.       return h == INVALID_HANDLE_VALUE;
  385.  
  386.     case UT_EXISTING:
  387.       if (h == INVALID_HANDLE_VALUE)
  388.         return 0;
  389.       /* fall thru... */
  390.     case UT_NEWER:
  391.       if (h != INVALID_HANDLE_VALUE)
  392.         {
  393.           FILETIME ft;
  394.           FileTimeToLocalFileTime (&fd.ftLastWriteTime, &ft);
  395.           WORD d, t;
  396.           FileTimeToDosDateTime (&ft, &d, &t);
  397.           if ((DWORD (d) << 16) + t >= hd.FileTime)
  398.             return 0;
  399.         }
  400.       break;
  401.     }
  402.   return 1;
  403. }
  404.  
  405. struct extract_info
  406. {
  407.   const progress_dlg *progress;
  408.   HWND hwnd_owner;
  409.   HANDLE h;
  410.   const RARHeaderData *hd;
  411.   const char *path;
  412.   int canceled;
  413.   int error;
  414.   long nbytes;
  415.   EXTRACTINGINFOEX *xex;
  416. };
  417.  
  418. static extract_info *xtract_info;
  419. static const UINT UWM_ARCEXTRACT = RegisterWindowMessage (WM_ARCEXTRACT);
  420.  
  421. static int
  422. run_callback (int mode, EXTRACTINGINFOEX &ex)
  423. {
  424.   return (lstate.has_callback
  425.           ? (lstate.callback
  426.              ? !lstate.callback (lstate.hwnd_owner, UWM_ARCEXTRACT,
  427.                                  mode, &ex)
  428.              : SendMessage (lstate.hwnd_owner, UWM_ARCEXTRACT,
  429.                             mode, LPARAM (&ex)))
  430.           : 0);
  431. }
  432.  
  433. static int __cdecl
  434. extract_helper (void *, u_char *data, int nbytes)
  435. {
  436.   if (!xtract_info)
  437.     return 1;
  438.  
  439.   while (nbytes > 0)
  440.     {
  441.       int size = min (nbytes, 65536);
  442.  
  443.       DWORD nwrite;
  444.       if (!WriteFile (xtract_info->h, data, size, &nwrite, 0)
  445.           || nwrite != DWORD (size))
  446.         {
  447.           xtract_info->error = 1;
  448.           xtract_info = 0;
  449.           return 0;
  450.         }
  451.  
  452.       nbytes -= size;
  453.       data += size;
  454.  
  455.       xtract_info->nbytes += size;
  456.       if (xtract_info->progress
  457.           && !xtract_info->progress->update (xtract_info->nbytes))
  458.         {
  459.           xtract_info->canceled = 1;
  460.           xtract_info = 0;
  461.           return 0;
  462.         }
  463.       if (lstate.has_callback)
  464.         {
  465.           xtract_info->xex->exinfo.dwWriteSize = xtract_info->nbytes;
  466.           if (run_callback (ARCEXTRACT_INPROCESS, *xtract_info->xex))
  467.             {
  468.               xtract_info->canceled = 1;
  469.               xtract_info = 0;
  470.               return 0;
  471.             }
  472.         }
  473.     }
  474.  
  475.   return 1;
  476. }
  477.  
  478. static int __cdecl
  479. change_volume (void *, char *path, int mode)
  480. {
  481.   if (mode != RAR_VOL_ASK)
  482.     return 1;
  483.   return change_vol_dialog (xtract_info ? xtract_info->hwnd_owner : 0, path);
  484. }
  485.  
  486. #define DEFINE_STUB(STUB_NAME, REAL_NAME, ARGSIZE) \
  487.   static int __declspec (naked) \
  488.   STUB_NAME () \
  489.     { \
  490.       __asm  mov eax, dword ptr [esp] \
  491.       __asm  mov ax, word ptr [eax] \
  492.       __asm  cmp ax, 0xc483          /* add esp,NN   -> 83 c4 NN */ \
  493.       __asm  jz cdecl_ \
  494.       __asm  call REAL_NAME \
  495.       __asm  ret ARGSIZE \
  496.       __asm cdecl_: \
  497.       __asm  call REAL_NAME \
  498.       __asm  ret 0 \
  499.     }
  500.  
  501. DEFINE_STUB (extract_helper_stub, extract_helper, 8);
  502. DEFINE_STUB (change_volume_stub, change_volume, 8);
  503.  
  504. static void
  505. init_exinfo (EXTRACTINGINFOEX &ex, const RARHeaderData &hd,
  506.              const char *path)
  507. {
  508.   ex.exinfo.dwFileSize = hd.UnpSize;
  509.   ex.exinfo.dwWriteSize = 0;
  510.   strcpy (ex.exinfo.szSourceFileName, hd.FileName);
  511.   strcpy (ex.exinfo.szDestFileName, path);
  512.   ex.dwCompressedSize = hd.PackSize;
  513.   ex.dwCRC = hd.FileCRC;
  514.   ex.uOSType = os_type (hd.HostOS);
  515.   ex.wRatio = calc_ratio (hd.PackSize, hd.UnpSize);
  516.   ex.wDate = HIWORD (hd.FileTime);
  517.   ex.wTime = LOWORD (hd.FileTime);
  518.   strcpy (ex.szAttribute, attr_string (hd.FileAttr));
  519.   strcpy (ex.szMode, method_string (hd.Method));
  520. }
  521.  
  522. int
  523. UnRAR::canceled () const
  524. {
  525.   format (IDS_CANCELED);
  526.   return ERROR_USER_CANCEL;
  527. }
  528.  
  529. int
  530. UnRAR::skip (rarData &rd, const char *path) const
  531. {
  532.   format (IDS_SKIPPING, path);
  533.   int e = rd.skip ();
  534.   return e ? process_err (e, path) : -1;
  535. }
  536.  
  537. int
  538. UnRAR::extract (rarData &rd, const char *path, const RARHeaderData &hd,
  539.                 progress_dlg &progress)
  540. {
  541.   if (progress.hwnd)
  542.     progress.init (path, hd.UnpSize);
  543.  
  544.   int e = check_timestamp (path, hd);
  545.   if (e < 0)
  546.     return canceled ();
  547.   if (!e)
  548.     return skip (rd, path);
  549.  
  550.   write_handle w (path);
  551.   if (!w.open ())
  552.     {
  553.       format (IDS_CANNOT_CREATE, path);
  554.       return skip (rd, path);
  555.     }
  556.   if (!w.ensure_room (hd.UnpSize))
  557.     {
  558.       format (IDS_DISK_FULL);
  559.       return skip (rd, path);
  560.     }
  561.  
  562.   extract_info xinfo;
  563.   xinfo.progress = progress.hwnd ? &progress : 0;
  564.   xinfo.hwnd_owner = u_hwnd;
  565.   xinfo.h = w;
  566.   xinfo.hd = &hd;
  567.   xinfo.path = path;
  568.   xinfo.canceled = 0;
  569.   xinfo.error = 0;
  570.   xinfo.nbytes = 0;
  571.   xinfo.xex = &u_ex;
  572.   xtract_info = &xinfo;
  573.   format (IDS_EXTRACTING, path);
  574.  
  575.   if (lstate.has_callback)
  576.     {
  577.       init_exinfo (*xinfo.xex, hd, path);
  578.       if (run_callback (ARCEXTRACT_BEGIN, *xinfo.xex))
  579.         return canceled ();
  580.     }
  581.  
  582.   e = rd.test ();
  583.   xtract_info = 0;
  584.   if (xinfo.canceled)
  585.     return canceled ();
  586.   if (e)
  587.     return process_err (e, path);
  588.   if (xinfo.error)
  589.     {
  590.       format (IDS_WRITE_ERROR, path);
  591.       return -1;
  592.     }
  593.   if (!SetEndOfFile (w))
  594.     {
  595.       format (IDS_CANNOT_SET_EOF);
  596.       return -1;
  597.     }
  598.  
  599.   w.complete ();
  600.  
  601.   FILETIME lo, ft;
  602.   DosDateTimeToFileTime (hd.FileTime >> 16, hd.FileTime, &lo);
  603.   LocalFileTimeToFileTime (&lo, &ft);
  604.   SetFileTime (w, 0, 0, &ft);
  605.   SetFileAttributes (path, hd.FileAttr);
  606.  
  607.   return 0;
  608. }
  609.  
  610. int
  611. UnRAR::mkdirhier (const char *path)
  612. {
  613.   if (CreateDirectory (path, 0))
  614.     {
  615.       format (IDS_CREATING, path);
  616.       return 1;
  617.     }
  618.   DWORD a = GetFileAttributes (path);
  619.   if (a != -1 && a & FILE_ATTRIBUTE_DIRECTORY)
  620.     return 1;
  621.   char buf[FNAME_MAX32 + 1];
  622.   strcpy (buf, path);
  623.   for (char *p = buf;
  624.        p = (char *)_mbspbrk ((u_char *)p, (u_char *)"/\\");
  625.        *p++ = '\\')
  626.     {
  627.       *p = 0;
  628.       if (CreateDirectory (buf, 0))
  629.         format (IDS_CREATING, buf);
  630.     }
  631.   if (CreateDirectory (path, 0))
  632.     {
  633.       format (IDS_CREATING, path);
  634.       return 1;
  635.     }
  636.   a = GetFileAttributes (path);
  637.   if (a != -1 && a & FILE_ATTRIBUTE_DIRECTORY)
  638.     return 1;
  639.   format (IDS_CANNOT_CREATE, path);
  640.   return 0;
  641. }
  642.  
  643. int
  644. UnRAR::extract1 ()
  645. {
  646.   format (IDS_EXTRACTING_FROM, u_path);
  647.  
  648.   rarData rd;
  649.   if (!rd.open (u_path, RAR_OM_EXTRACT))
  650.     return open_err (rd.oad.OpenResult);
  651.  
  652.   rarSetProcessDataProc (rd.h, (int (__cdecl *)(u_char *, int)) extract_helper_stub);
  653.   rarSetChangeVolProc (rd.h, (int (__cdecl *)(char *,int))change_volume_stub);
  654.  
  655.   progress_dlg progress;
  656.   if (!lstate.has_callback)
  657.     progress.create (u_hwnd);
  658.  
  659.   char dest[FNAME_MAX32 + 1];
  660.   char *de = stpcpy (dest, u_dest);
  661.  
  662.   int nerrors = 0;
  663.   int e;
  664.   while (1)
  665.     {
  666.       e = rd.read_header ();
  667.       if (e)
  668.         {
  669.           e = header_err (e);
  670.           return e ? e : nerrors;
  671.         }
  672.  
  673.       if (rd.hd.Flags & FRAR_ENCRYPTED)
  674.         {
  675.           if (!u_passwd)
  676.             {
  677.               u_passwd = askpass_dialog (u_hwnd);
  678.               if (!u_passwd)
  679.                 return ERROR_USER_CANCEL;
  680.             }
  681.           rarSetPassword (rd.h, u_passwd);
  682.         }
  683.  
  684.       if (!u_glob.match (rd.hd.FileName, u_opt & O_STRICT, u_opt & O_RECURSIVE))
  685.         {
  686.           e = rd.skip ();
  687.           if (e)
  688.             return process_err (e, rd.hd.FileName);
  689.         }
  690.       else
  691.         {
  692.           const char *name = trim_root (rd.hd.FileName);
  693.           if (!*name)
  694.             name = rd.hd.FileName;
  695.           if (u_cmd == C_EXTRACT)
  696.             strcpy (de, name);
  697.           else
  698.             {
  699.               char *sl = find_last_slash (name);
  700.               strcpy (de, sl ? sl + 1 : name);
  701.             }
  702.  
  703.           if (rd.hd.FileAttr & FILE_ATTRIBUTE_DIRECTORY)
  704.             {
  705.               if (u_cmd == C_EXTRACT && !mkdirhier (dest))
  706.                 return ERROR_DIRECTORY;
  707.               e = rd.skip ();
  708.               if (e)
  709.                 return process_err (e, dest);
  710.             }
  711.           else
  712.             {
  713.               char *p = find_last_slash (dest);
  714.               if (p)
  715.                 {
  716.                   *p = 0;
  717.                   if (!mkdirhier (dest))
  718.                     return ERROR_DIRECTORY;
  719.                   *p = '\\';
  720.                 }
  721.               e = extract (rd, dest, rd.hd, progress);
  722.               if (e)
  723.                 {
  724.                   if (e > 0)
  725.                     return e;
  726.                   nerrors++;
  727.                 }
  728.             }
  729.         }
  730.     }
  731. }
  732.  
  733. int
  734. UnRAR::extract ()
  735. {
  736.   if (lstate.has_callback)
  737.     {
  738.       memset (&u_ex, 0, sizeof u_ex);
  739.       strcpy (u_ex.exinfo.szSourceFileName, u_path);
  740.       if (run_callback (ARCEXTRACT_OPEN, u_ex))
  741.         return canceled ();
  742.     }
  743.  
  744.   int e = extract1 ();
  745.   if (lstate.has_callback)
  746.     run_callback (ARCEXTRACT_END, u_ex);
  747.  
  748.   return e;
  749. }
  750.  
  751. int
  752. UnRAR::test ()
  753. {
  754.   format (IDS_TEST_NOT_IMPL);
  755.   return ERROR_NOT_SUPPORT;
  756. }
  757.  
  758. int
  759. UnRAR::list ()
  760. {
  761.   rarData rd;
  762.   if (!rd.open (u_path, RAR_OM_LIST))
  763.     return open_err (rd.oad.OpenResult);
  764.  
  765.   format ("  Name         Original   Packed  Ratio   Date     Time   Attr Method  CRC\n");
  766.   format ("-------------- -------- -------- ------ -------- -------- ---- ------- --------\n");
  767.   int nfiles = 0;
  768.   int org_sz = 0, comp_sz = 0;
  769.   int e;
  770.   while (1)
  771.     {
  772.       e = rd.read_header ();
  773.       if (e)
  774.         {
  775.           if (e == ERAR_END_ARCHIVE)
  776.             break;
  777.           return header_err (e);
  778.         }
  779.       if (u_glob.match (rd.hd.FileName, u_opt & O_STRICT, u_opt & O_RECURSIVE))
  780.         {
  781.           nfiles++;
  782.           org_sz += rd.hd.UnpSize;
  783.           comp_sz += rd.hd.PackSize;
  784.           if (u_cmd == C_VLIST)
  785.             format ("%s\n%15c", rd.hd.FileName, ' ');
  786.           else
  787.             {
  788.               char *p = find_last_slash (rd.hd.FileName);
  789.               format ("%-14s ", p ? p + 1 : rd.hd.FileName);
  790.             }
  791.           int ratio = calc_ratio (rd.hd.PackSize, rd.hd.UnpSize);
  792.           format ("%8d %8d%c%3d.%d%%%c%02d-%02d-%02d %02d:%02d:%02d %s %-7s %08x\n",
  793.                   rd.hd.UnpSize, rd.hd.PackSize,
  794.                   rd.hd.Flags & FRAR_PREVVOL ? '<' : ' ',
  795.                   ratio / 10, ratio % 10,
  796.                   rd.hd.Flags & FRAR_NEXTVOL ? '>' : ' ',
  797.                   ((rd.hd.FileTime >> 25) + 80) % 100,
  798.                   (rd.hd.FileTime >> 21) & 15,
  799.                   (rd.hd.FileTime >> 16) & 31,
  800.                   (rd.hd.FileTime >> 11) & 31,
  801.                   (rd.hd.FileTime >> 5) & 63,
  802.                   (rd.hd.FileTime & 31) * 2,
  803.                   attr_string (rd.hd.FileAttr),
  804.                   method_string (rd.hd.Method),
  805.                   rd.hd.FileCRC);
  806.         }
  807.       e = rd.skip ();
  808.       if (e)
  809.         return process_err (e, rd.hd.FileName);
  810.     }
  811.  
  812.   if (nfiles)
  813.     {
  814.       format ("-------------- -------- -------- ------\n");
  815.       char b[32];
  816.       sprintf (b, "%d File%s", nfiles, nfiles == 1 ? "" : "s");
  817.       int ratio = calc_ratio (comp_sz, org_sz);
  818.       format ("%12s   %8d %8d %3d.%d%%\n",
  819.               b, org_sz, comp_sz, ratio / 10, ratio % 10);
  820.     }
  821.   return 0;
  822. }
  823.  
  824. int
  825. UnRAR::print ()
  826. {
  827.   format (IDS_PRINT_NOT_IMPL);
  828.   return ERROR_NOT_SUPPORT;
  829. }
  830.  
  831. int
  832. UnRAR::comment ()
  833. {
  834.   format (IDS_COMMENT_NOT_IMPL);
  835.   return ERROR_NOT_SUPPORT;
  836. }
  837.  
  838. int
  839. UnRAR::xmain (int ac, char **av)
  840. {
  841.   int e = parse_opt (ac, av);
  842.   if (e)
  843.     return e;
  844.  
  845.   switch (u_cmd)
  846.     {
  847.     case C_EXTRACT:
  848.     case C_EXTRACT_NODIR:
  849.       return extract ();
  850.  
  851.     case C_PRINT:
  852.       return print ();
  853.  
  854.     case C_LIST:
  855.     case C_VLIST:
  856.       return list ();
  857.  
  858.     case C_TEST:
  859.       return test ();
  860.  
  861.     case C_COMMENT:
  862.       return comment ();
  863.     }
  864.   return 0;
  865. }
  866.