home *** CD-ROM | disk | FTP | other *** search
/ DOS/V Power Report 1999 October / VPR9910A.BIN / OLS / unrar32005 / unrar32005.lzh / src / rar.cxx < prev    next >
C/C++ Source or Header  |  1998-04-03  |  20KB  |  847 lines

  1. /*
  2.  *   Copyright (c) 1998 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 'v':
  215.         u_opt |= O_VOLUME;
  216.         break;
  217.  
  218.       case 's':
  219.         u_opt |= O_STRICT;
  220.         break;
  221.  
  222.       case 'p':
  223.         if (av[i][2])
  224.           u_passwd = &av[i][2];
  225.         else if (++i < ac)
  226.           u_passwd = av[i];
  227.         else
  228.           {
  229.             format (IDS_OPTION_P_REQ_ARGS);
  230.             return ERROR_COMMAND_NAME;
  231.           }
  232.         break;
  233.  
  234.       case '-':
  235.         i++;
  236.         goto optend;
  237.  
  238.       default:
  239.         format (IDS_UNRECOGNIZED_OPTION, av[i][1]);
  240.         return ERROR_COMMAND_NAME;
  241.       }
  242. optend:
  243.   if (i >= ac)
  244.     {
  245.       format (IDS_NO_ARCHIVE_FILE);
  246.       return ERROR_NOT_ARC_FILE;
  247.     }
  248.   u_path = av[i++];
  249.  
  250.   if (i < ac)
  251.     {
  252.       char *sl = find_last_slash (av[i]);
  253.       if (sl && !sl[1])
  254.         {
  255.           u_dest = av[i++];
  256.           if (strlen (u_dest) >= FNAME_MAX32 - MAX_PATH)
  257.             {
  258.               format (IDS_FILE_NAME_TOO_LONG, u_dest);
  259.               return ERROR_LONG_FILE_NAME;
  260.             }
  261.         }
  262.     }
  263.  
  264.   u_glob.set_pattern (ac - i, av + i);
  265.  
  266.   return 0;
  267. }
  268.  
  269. class dyn_handle
  270. {
  271. private:
  272.   void operator = (const dyn_handle &);
  273.   dyn_handle (const dyn_handle &);
  274. protected:
  275.   HANDLE h;
  276. public:
  277.   dyn_handle () : h (INVALID_HANDLE_VALUE) {}
  278.   dyn_handle (HANDLE h_) : h (h_) {}
  279.   ~dyn_handle () {if (valid ()) CloseHandle (h);}
  280.   int valid () const {return h != INVALID_HANDLE_VALUE;}
  281.   operator HANDLE () const {return h;}
  282.   void fix (HANDLE h_) {h = h_;}
  283.   void close ()
  284.     {
  285.       if (valid ())
  286.         {
  287.           CloseHandle (h);
  288.           h = INVALID_HANDLE_VALUE;
  289.         }
  290.     }
  291. };
  292.  
  293. class write_handle: public dyn_handle
  294. {
  295.   int w_complete;
  296.   int w_delete_if_fail;
  297.   const char *w_path;
  298. public:
  299.   write_handle (const char *);
  300.   ~write_handle ();
  301.   void complete () {w_complete = 1;}
  302.   int open ();
  303.   int ensure_room (LONG);
  304. };
  305.  
  306. write_handle::write_handle (const char *path)
  307.      : w_complete (0), w_delete_if_fail (0), w_path (path)
  308. {
  309. }
  310.  
  311. write_handle::~write_handle ()
  312. {
  313.   if (!w_complete && valid () && w_delete_if_fail)
  314.     {
  315.       close ();
  316.       DeleteFile (w_path);
  317.     }
  318. }
  319.  
  320. int
  321. write_handle::ensure_room (LONG size)
  322. {
  323.   if (SetFilePointer (*this, size, 0, FILE_BEGIN) == -1
  324.       || !SetEndOfFile (*this))
  325.     return 0;
  326.  
  327.   w_delete_if_fail = 1;
  328.  
  329.   if (SetFilePointer (*this, 0, 0, FILE_BEGIN) == -1)
  330.     return 0;
  331.   return 1;
  332. }
  333.  
  334. int
  335. write_handle::open ()
  336. {
  337.   fix (CreateFile (w_path, GENERIC_WRITE, 0, 0, OPEN_ALWAYS,
  338.                    FILE_ATTRIBUTE_ARCHIVE | FILE_FLAG_SEQUENTIAL_SCAN, 0));
  339.   if (!valid ())
  340.     return 0;
  341.   w_delete_if_fail = GetLastError () == NO_ERROR;
  342.   return 1;
  343. }
  344.  
  345. int
  346. UnRAR::check_timestamp (const char *path, const RARHeaderData &hd)
  347. {
  348.   WIN32_FIND_DATA fd;
  349.   HANDLE h = FindFirstFile (path, &fd);
  350.   if (h != INVALID_HANDLE_VALUE)
  351.     FindClose (h);
  352.  
  353.   switch (u_type)
  354.     {
  355.     case UT_ASK:
  356.       if (h != INVALID_HANDLE_VALUE && !(u_opt & O_YES))
  357.         {
  358.           replace_param r;
  359.           r.old_name = path;
  360.           FILETIME ft;
  361.           FileTimeToLocalFileTime (&fd.ftLastWriteTime, &ft);
  362.           WORD d, t;
  363.           FileTimeToDosDateTime (&ft, &d, &t);
  364.           r.old_date = (DWORD (d) << 16) + t;
  365.           r.old_size = fd.nFileSizeLow;
  366.           r.new_name = hd.FileName;
  367.           r.new_date = hd.FileTime;
  368.           r.new_size = hd.UnpSize;
  369.           switch (replace_dialog (u_hwnd, r))
  370.             {
  371.             case IDYES:
  372.               return 1;
  373.             case IDNO:
  374.               return 0;
  375.             case IDC_ALL:
  376.               u_opt |= O_YES;
  377.               return 1;
  378.             default:
  379.               return -1;
  380.             }
  381.         }
  382.       break;
  383.  
  384.     case UT_OVWRT:
  385.       return 1;
  386.  
  387.     case UT_SKIP:
  388.       return h == INVALID_HANDLE_VALUE;
  389.  
  390.     case UT_EXISTING:
  391.       if (h == INVALID_HANDLE_VALUE)
  392.         return 0;
  393.       /* fall thru... */
  394.     case UT_NEWER:
  395.       if (h != INVALID_HANDLE_VALUE)
  396.         {
  397.           FILETIME ft;
  398.           FileTimeToLocalFileTime (&fd.ftLastWriteTime, &ft);
  399.           WORD d, t;
  400.           FileTimeToDosDateTime (&ft, &d, &t);
  401.           if ((DWORD (d) << 16) + t >= hd.FileTime)
  402.             return 0;
  403.         }
  404.       break;
  405.     }
  406.   return 1;
  407. }
  408.  
  409. struct extract_info
  410. {
  411.   const progress_dlg *progress;
  412.   HWND hwnd_owner;
  413.   HANDLE h;
  414.   const RARHeaderData *hd;
  415.   const char *path;
  416.   int canceled;
  417.   int error;
  418.   long nbytes;
  419.   EXTRACTINGINFOEX ex;
  420. };
  421.  
  422. static extract_info *xtract_info;
  423. static const UINT UWM_ARCEXTRACT = RegisterWindowMessage (WM_ARCEXTRACT);
  424.  
  425. static int __cdecl
  426. extract_helper (u_char *data, int size)
  427. {
  428.   if (!xtract_info)
  429.     return 1;
  430.   DWORD nwrite;
  431.   if (!WriteFile (xtract_info->h, data, size, &nwrite, 0)
  432.       || nwrite != DWORD (size))
  433.     {
  434.       xtract_info->error = 1;
  435.       xtract_info = 0;
  436.       return 1;
  437.     }
  438.   xtract_info->nbytes += size;
  439.   if (xtract_info->progress
  440.       && !xtract_info->progress->update (xtract_info->nbytes))
  441.     {
  442.       xtract_info->canceled = 1;
  443.       xtract_info = 0;
  444.     }
  445.   if (lstate.hwnd_owner)
  446.     {
  447.       xtract_info->ex.exinfo.dwWriteSize = xtract_info->nbytes;
  448.       if (lstate.callback
  449.           ? !lstate.callback (lstate.hwnd_owner, UWM_ARCEXTRACT,
  450.                               ARCEXTRACT_INPROCESS, &xtract_info->ex)
  451.           : SendMessage (lstate.hwnd_owner, UWM_ARCEXTRACT,
  452.                          ARCEXTRACT_INPROCESS, LPARAM (&xtract_info->ex)))
  453.         {
  454.           xtract_info->canceled = 1;
  455.           xtract_info = 0;
  456.         }
  457.     }
  458.   return 1;
  459. }
  460.  
  461. static int __cdecl
  462. change_volume (char *path, int mode)
  463. {
  464.   if (mode != RAR_VOL_ASK)
  465.     return 1;
  466.   char msg[1024], buf[1024];
  467.   if (LoadString (lstate.hinst, IDS_INSERT_DISK_WITH, buf, sizeof buf))
  468.     sprintf (msg, buf, path);
  469.   else
  470.     strcpy (msg, path);
  471.   if (!LoadString (lstate.hinst, IDS_CHANGE_VOLUME, buf, sizeof buf))
  472.     *buf = 0;
  473.   return MessageBox (xtract_info ? xtract_info->hwnd_owner : 0,
  474.                      msg, buf,
  475.                      MB_OKCANCEL | MB_ICONEXCLAMATION) != IDCANCEL;
  476. }
  477.  
  478. static void
  479. init_exinfo (EXTRACTINGINFOEX &ex, const RARHeaderData &hd,
  480.              const char *path)
  481. {
  482.   ex.exinfo.dwFileSize = hd.UnpSize;
  483.   ex.exinfo.dwWriteSize = 0;
  484.   strcpy (ex.exinfo.szSourceFileName, hd.FileName);
  485.   strcpy (ex.exinfo.szDestFileName, path);
  486.   ex.dwCompressedSize = hd.PackSize;
  487.   ex.dwCRC = hd.FileCRC;
  488.   ex.uOSType = os_type (hd.HostOS);
  489.   ex.wRatio = calc_ratio (hd.PackSize, hd.UnpSize);
  490.   ex.wDate = HIWORD (hd.FileTime);
  491.   ex.wTime = LOWORD (hd.FileTime);
  492.   strcpy (ex.szAttribute, attr_string (hd.FileAttr));
  493.   strcpy (ex.szMode, method_string (hd.Method));
  494. }
  495.  
  496. int
  497. UnRAR::extract (rarData &rd, const char *path, const RARHeaderData &hd,
  498.                 const progress_dlg &progress)
  499. {
  500.   if (progress.hwnd)
  501.     progress.init (path, hd.UnpSize);
  502.  
  503.   int e = check_timestamp (path, hd);
  504.   if (e < 0)
  505.     {
  506.       format (IDS_CANCELED);
  507.       return -1;
  508.     }
  509.   if (!e)
  510.     {
  511.       format (IDS_SKIPPING, path);
  512.       e = rd.skip ();
  513.       if (e)
  514.         process_err (e, path);
  515.       return 0;
  516.     }
  517.  
  518.   write_handle w (path);
  519.   if (!w.open ())
  520.     {
  521.       format (IDS_CANNOT_CREATE, path);
  522.       format (IDS_SKIPPING, path);
  523.       e = rd.skip ();
  524.       if (e)
  525.         process_err (e, path);
  526.       return 0;
  527.     }
  528.   if (!w.ensure_room (hd.UnpSize))
  529.     {
  530.       format (IDS_DISK_FULL);
  531.       format (IDS_SKIPPING, path);
  532.       e = rd.skip ();
  533.       if (e)
  534.         process_err (e, path);
  535.       return 0;
  536.     }
  537.  
  538.   extract_info xinfo;
  539.   xinfo.progress = progress.hwnd ? &progress : 0;
  540.   xinfo.hwnd_owner = u_hwnd;
  541.   xinfo.h = w;
  542.   xinfo.hd = &hd;
  543.   xinfo.path = path;
  544.   xinfo.canceled = 0;
  545.   xinfo.error = 0;
  546.   xinfo.nbytes = 0;
  547.   xtract_info = &xinfo;
  548.   format (IDS_EXTRACTING, path);
  549.  
  550.   if (lstate.hwnd_owner)
  551.     {
  552.       init_exinfo (xinfo.ex, hd, path);
  553.       if (lstate.callback
  554.           ? !lstate.callback (lstate.hwnd_owner, UWM_ARCEXTRACT,
  555.                               ARCEXTRACT_BEGIN, &xinfo.ex)
  556.           : SendMessage (lstate.hwnd_owner, UWM_ARCEXTRACT,
  557.                          ARCEXTRACT_BEGIN, LPARAM (&xinfo.ex)))
  558.         {
  559.           format (IDS_CANCELED);
  560.           return -1;
  561.         }
  562.     }
  563.  
  564.   e = rd.test ();
  565.   xtract_info = 0;
  566.   if (xinfo.canceled)
  567.     {
  568.       format (IDS_CANCELED);
  569.       return -1;
  570.     }
  571.   if (xinfo.error)
  572.     {
  573.       format (IDS_WRITE_ERROR, path);
  574.       return 0;
  575.     }
  576.   if (e)
  577.     {
  578.       process_err (e, path);
  579.       return e == ERAR_EOPEN ? -1 : 0;
  580.     }
  581.   if (!SetEndOfFile (w))
  582.     {
  583.       format (IDS_CANNOT_SET_EOF);
  584.       return 0;
  585.     }
  586.  
  587.   w.complete ();
  588.  
  589.   FILETIME lo, ft;
  590.   DosDateTimeToFileTime (hd.FileTime >> 16, hd.FileTime, &lo);
  591.   LocalFileTimeToFileTime (&lo, &ft);
  592.   SetFileTime (w, 0, 0, &ft);
  593.   SetFileAttributes (path, hd.FileAttr);
  594.  
  595.   if (lstate.hwnd_owner
  596.       && (lstate.callback
  597.           ? !lstate.callback (lstate.hwnd_owner, UWM_ARCEXTRACT,
  598.                               ARCEXTRACT_END, &xinfo.ex)
  599.           : SendMessage (lstate.hwnd_owner, UWM_ARCEXTRACT,
  600.                          ARCEXTRACT_END, LPARAM (&xinfo.ex))))
  601.     {
  602.       format (IDS_CANCELED);
  603.       return -1;
  604.     }
  605.  
  606.   return 1;
  607. }
  608.  
  609. int
  610. UnRAR::mkdirhier (const char *path)
  611. {
  612.   if (CreateDirectory (path, 0))
  613.     {
  614.       format (IDS_CREATING, path);
  615.       return 1;
  616.     }
  617.   DWORD a = GetFileAttributes (path);
  618.   if (a != -1 && a & FILE_ATTRIBUTE_DIRECTORY)
  619.     return 1;
  620.   char buf[FNAME_MAX32 + 1];
  621.   strcpy (buf, path);
  622.   for (char *p = buf;
  623.        p = (char *)_mbspbrk ((u_char *)p, (u_char *)"/\\");
  624.        *p++ = '\\')
  625.     {
  626.       *p = 0;
  627.       if (CreateDirectory (buf, 0))
  628.         format (IDS_CREATING, buf);
  629.     }
  630.   if (CreateDirectory (path, 0))
  631.     {
  632.       format (IDS_CREATING, path);
  633.       return 1;
  634.     }
  635.   a = GetFileAttributes (path);
  636.   if (a != -1 && a & FILE_ATTRIBUTE_DIRECTORY)
  637.     return 1;
  638.   format (IDS_CANNOT_CREATE, path);
  639.   return 0;
  640. }
  641.  
  642. int
  643. UnRAR::extract ()
  644. {
  645.   format (IDS_EXTRACTING_FROM, u_path);
  646.  
  647.   rarData rd;
  648.   if (!rd.open (u_path, RAR_OM_EXTRACT))
  649.     return open_err (rd.oad.OpenResult);
  650.  
  651.   rarSetProcessDataProc (rd.h, extract_helper);
  652.   rarSetChangeVolProc (rd.h, change_volume);
  653.  
  654.   progress_dlg progress;
  655.   if (!lstate.hwnd_owner)
  656.     progress.create (u_hwnd);
  657.  
  658.   char dest[FNAME_MAX32 + 1];
  659.   char *de = stpcpy (dest, u_dest);
  660.  
  661.   int nerrors = 0;
  662.   int e;
  663.   while (1)
  664.     {
  665.       e = rd.read_header ();
  666.       if (e)
  667.         {
  668.           e = header_err (e);
  669.           return e ? e : nerrors;
  670.         }
  671.  
  672.       if (rd.hd.Flags & FRAR_ENCRYPTED)
  673.         {
  674.           if (!u_passwd)
  675.             {
  676.               u_passwd = askpass_dialog (u_hwnd);
  677.               if (!u_passwd)
  678.                 return ERROR_USER_CANCEL;
  679.             }
  680.           rarSetPassword (rd.h, u_passwd);
  681.         }
  682.  
  683.       if (!u_glob.match (rd.hd.FileName, u_opt & O_STRICT))
  684.         {
  685.           e = rd.skip ();
  686.           if (e)
  687.             process_err (e, rd.hd.FileName);
  688.         }
  689.       else
  690.         {
  691.           const char *name = trim_root (rd.hd.FileName);
  692.           if (!*name)
  693.             name = rd.hd.FileName;
  694.           if (u_cmd == C_EXTRACT)
  695.             strcpy (de, name);
  696.           else
  697.             {
  698.               char *sl = find_last_slash (name);
  699.               strcpy (de, sl ? sl + 1 : name);
  700.             }
  701.  
  702.           if (rd.hd.FileAttr & FILE_ATTRIBUTE_DIRECTORY)
  703.             {
  704.               if (u_cmd == C_EXTRACT && !mkdirhier (dest))
  705.                 return ERROR_DIRECTORY;
  706.               e = rd.skip ();
  707.               if (e)
  708.                 process_err (e, dest);
  709.             }
  710.           else
  711.             {
  712.               char *p = find_last_slash (dest);
  713.               if (p)
  714.                 {
  715.                   *p = 0;
  716.                   if (!mkdirhier (dest))
  717.                     return ERROR_DIRECTORY;
  718.                   *p = '\\';
  719.                 }
  720.               e = extract (rd, dest, rd.hd, progress);
  721.               if (e < 0)
  722.                 return ERROR_USER_CANCEL;
  723.               if (!e)
  724.                 nerrors++;
  725.             }
  726.         }
  727.     }
  728. }
  729.  
  730. int
  731. UnRAR::test ()
  732. {
  733.   format (IDS_TEST_NOT_IMPL);
  734.   return ERROR_NOT_SUPPORT;
  735. }
  736.  
  737. int
  738. UnRAR::list ()
  739. {
  740.   rarData rd;
  741.   if (!rd.open (u_path, RAR_OM_LIST))
  742.     return open_err (rd.oad.OpenResult);
  743.  
  744.   format ("  Name         Original   Packed  Ratio   Date     Time   Attr Method  CRC\n");
  745.   format ("-------------- -------- -------- ------ -------- -------- ---- ------- --------\n");
  746.   int nfiles = 0;
  747.   int org_sz = 0, comp_sz = 0;
  748.   int e;
  749.   while (1)
  750.     {
  751.       e = rd.read_header ();
  752.       if (e)
  753.         break;
  754.       if (u_glob.match (rd.hd.FileName, u_opt & O_STRICT))
  755.         {
  756.           nfiles++;
  757.           org_sz += rd.hd.UnpSize;
  758.           comp_sz += rd.hd.PackSize;
  759.           if (u_cmd == C_VLIST)
  760.             format ("%s\n%15c", rd.hd.FileName, ' ');
  761.           else
  762.             {
  763.               char *p = find_last_slash (rd.hd.FileName);
  764.               format ("%-14s ", p ? p + 1 : rd.hd.FileName);
  765.             }
  766.           int ratio = calc_ratio (rd.hd.PackSize, rd.hd.UnpSize);
  767.           format ("%8d %8d%c%3d.%d%%%c%02d-%02d-%02d %02d:%02d:%02d %s %-7s %08x\n",
  768.                   rd.hd.UnpSize, rd.hd.PackSize,
  769.                   rd.hd.Flags & FRAR_PREVVOL ? '<' : ' ',
  770.                   ratio / 10, ratio % 10,
  771.                   rd.hd.Flags & FRAR_NEXTVOL ? '>' : ' ',
  772.                   ((rd.hd.FileTime >> 25) + 80) % 100,
  773.                   (rd.hd.FileTime >> 21) & 15,
  774.                   (rd.hd.FileTime >> 16) & 31,
  775.                   (rd.hd.FileTime >> 11) & 31,
  776.                   (rd.hd.FileTime >> 5) & 63,
  777.                   (rd.hd.FileTime & 31) * 2,
  778.                   attr_string (rd.hd.FileAttr),
  779.                   method_string (rd.hd.Method),
  780.                   rd.hd.FileCRC);
  781.         }
  782.       if (rd.hd.Flags & FRAR_NEXTVOL)
  783.         {
  784.           e = ERAR_END_ARCHIVE;
  785.           break;
  786.         }
  787.       e = rd.skip ();
  788.       if (e)
  789.         process_err (e, rd.hd.FileName);
  790.     }
  791.   if (e != ERAR_END_ARCHIVE)
  792.     return header_err (e);
  793.   if (nfiles)
  794.     {
  795.       format ("-------------- -------- -------- ------\n");
  796.       char b[32];
  797.       sprintf (b, "%d File%s", nfiles, nfiles == 1 ? "" : "s");
  798.       int ratio = calc_ratio (comp_sz, org_sz);
  799.       format ("%12s   %8d %8d %3d.%d%%\n",
  800.               b, org_sz, comp_sz, ratio / 10, ratio % 10);
  801.     }
  802.   return 0;
  803. }
  804.  
  805. int
  806. UnRAR::print ()
  807. {
  808.   format (IDS_PRINT_NOT_IMPL);
  809.   return ERROR_NOT_SUPPORT;
  810. }
  811.  
  812. int
  813. UnRAR::comment ()
  814. {
  815.   format (IDS_COMMENT_NOT_IMPL);
  816.   return ERROR_NOT_SUPPORT;
  817. }
  818.  
  819. int
  820. UnRAR::xmain (int ac, char **av)
  821. {
  822.   int e = parse_opt (ac, av);
  823.   if (e)
  824.     return e;
  825.  
  826.   switch (u_cmd)
  827.     {
  828.     case C_EXTRACT:
  829.     case C_EXTRACT_NODIR:
  830.       return extract ();
  831.  
  832.     case C_PRINT:
  833.       return print ();
  834.  
  835.     case C_LIST:
  836.     case C_VLIST:
  837.       return list ();
  838.  
  839.     case C_TEST:
  840.       return test ();
  841.  
  842.     case C_COMMENT:
  843.       return comment ();
  844.     }
  845.   return 0;
  846. }
  847.