unar.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. import errno
  2. from subprocess import Popen, PIPE, STDOUT
  3. import re
  4. class Error(Exception):
  5. """Something did go wrong"""
  6. class UnknownOption(Error):
  7. """Unknown option to unar given."""
  8. class ArchiveNotFound(Error):
  9. """Archive not found."""
  10. class StreamError(Error):
  11. """When extraction failed (cannot write, duplicate, ..?)"""
  12. class PasswordError(Error):
  13. """Password required."""
  14. class Unar:
  15. # grep -rnw -e "XADException describeXADError" ./unarchiver
  16. EXCEPTION_PATTERNS = {
  17. "Unknown option" : UnknownOption,
  18. "Couldn't open archive." : ArchiveNotFound,
  19. "Couldn't recognize the archive format" : StreamError,
  20. "Extraction to current directory failed!" : StreamError,
  21. "Archive parsing failed" : StreamError,
  22. "Extraction to directory \"\" failed" : StreamError, # TODO
  23. "This archive requires a password to unpack" : PasswordError
  24. }
  25. def __init__(self):
  26. self._path = '/usr/bin'
  27. self._command = 'unar'
  28. self._args = [] # unar command arguments
  29. self._files = [] # list of files to extract (alternative is all or use indexes with -i)
  30. self._file = ''
  31. # Options
  32. self._output = ""
  33. self._forceOverwrite = False
  34. self._forceRename = False
  35. self._forceSkip = False
  36. self._forceDirectory = False
  37. self._noDirectory = False
  38. self._password = ""
  39. self._encoding = None # TODO
  40. self._passwordEncoding = None # TODO
  41. self._indexes = []
  42. self._noRecursion = False
  43. self._copyTime = False
  44. self._forks = False
  45. self._quiet = False
  46. """
  47. TODO:
  48. List all supported encodings: unar -e list
  49. grep -rnw 'TODO' unar.py
  50. """
  51. def run(self, cb=None):
  52. p = Popen(self._compileCmd(), stdout=PIPE, stderr=PIPE)
  53. log = ""
  54. while p.poll() is None:
  55. o = p.communicate()
  56. m = o[0].decode('UTF-8')
  57. log += m
  58. print("m: {}".format(m))
  59. print(o[1])
  60. exitCode = p.returncode
  61. print("Return code: {0}".format(exitCode))
  62. if exitCode != 0:
  63. for pattern, exception in self.EXCEPTION_PATTERNS.items():
  64. for line in log.split('\n'):
  65. if pattern in line:
  66. raise exception(line)
  67. if cb: cb(exitCode)
  68. def _compileCmd(self):
  69. # full command path + command
  70. args = [self.fullCommandPath]
  71. # options
  72. if self.output:
  73. args.append('-o')
  74. args.append(self.output)
  75. if self.forceOverwrite: args.append("-f")
  76. if self.forceRename: args.append("-r")
  77. if self.forceSkip: args.append("-s")
  78. if self.forceDirectory: args.append("-d")
  79. if self.noDirectory: args.append("-D")
  80. if self.password:
  81. args.append('-p')
  82. args.append(self.password)
  83. if self.encoding:
  84. args.append('-e')
  85. args.append(self.encoding)
  86. if self.passwordEncoding:
  87. args.append('-E')
  88. args.append(self.passwordEncoding)
  89. if self.indexes:
  90. args.append('-i')
  91. args.append("".join(i for i in self.indexes))#TODO does this work?
  92. if self.noRecursion: args.append("-nr")
  93. if self.copyTime: args.append("-t")
  94. if self.forks:
  95. args.append('-k')
  96. args.append("")# TODO <visible|hidden|skip>
  97. # archive path
  98. if self.file: args.append(self.file)
  99. # optional files to extract TODO
  100. #if self._files: args.append(",".join())
  101. return args
  102. ########################
  103. @property
  104. def path(self): return self._path
  105. @path.setter
  106. def path(self, path): self._path = path
  107. @property
  108. def command(self): return self._command
  109. @command.setter
  110. def command(self, cmd): self._command = cmd
  111. @property
  112. def fullCommandPath(self):
  113. return "{0}/{1}".format(self._path, self._command)
  114. @fullCommandPath.setter
  115. def fullCommandPath(self, tupleValue):
  116. self._path = tupleValue[0]
  117. self._command = tupleValue[1]
  118. @property
  119. def file(self): return self._file
  120. @file.setter
  121. def file(self, path): self._file = path
  122. """ Options
  123. """
  124. # -output-directory (-o) <string>
  125. @property
  126. def output(self): return self._output
  127. @output.setter
  128. def output(self, path): self._output = path
  129. # -force-overwrite (-f)
  130. @property
  131. def forceOverwrite(self): return self._forceOverwrite
  132. @forceOverwrite.setter
  133. def forceOverwrite(self, state): self._forceOverwrite = state
  134. # -force-rename (-r)
  135. @property
  136. def forceRename(self): return self._forceRename
  137. @forceRename.setter
  138. def forceRename(self, state): self._forceRename = state
  139. # -force-skip (-s)
  140. @property
  141. def forceSkip(self): return self._forceSkip
  142. @forceSkip.setter
  143. def forceSkip(self, state): self._forceSkip = state
  144. # -force-directory (-d)
  145. @property
  146. def forceDirectory(self): return self._forceDirectory
  147. @forceDirectory.setter
  148. def forceDirectory(self, state): self._forceDirectory = state
  149. # -no-directory (-D)
  150. @property
  151. def noDirectory(self): return self._noDirectory
  152. @noDirectory.setter
  153. def noDirectory(self, state): self._noDirectory = state
  154. # -password (-p) <string>
  155. @property
  156. def password(self): return self._password
  157. @password.setter
  158. def password(self, password): self._password = password
  159. # -encoding (-e) <encoding name>
  160. @property
  161. def encoding(self): return self._encoding
  162. @encoding.setter
  163. def encoding(self, encoding): self._encoding = encoding
  164. # -password-encoding (-E) <name>
  165. @property
  166. def passwordEncoding(self): return self._passwordEncoding
  167. @passwordEncoding.setter
  168. def passwordEncoding(self, encoding): self._passwordEncoding = encoding
  169. # -indexes (-i)
  170. @property
  171. def indexes(self): return self._indexes
  172. @indexes.setter
  173. def indexes(self, indexes): self._indexes = indexes
  174. # -no-recursion (-nr)
  175. @property
  176. def noRecursion(self): return self._noRecursion
  177. @noRecursion.setter
  178. def noRecursion(self, state): self._noRecursion = state
  179. # -copy-time (-t)
  180. @property
  181. def copyTime(self): return self._copyTime
  182. @copyTime.setter
  183. def copyTime(self, state): self._copyTime = state
  184. # -forks (-k) <visible|hidden|skip>
  185. @property
  186. def forks(self): return self._forks
  187. @forks.setter
  188. def forks(self, option): self._forks = option
  189. # -version (-v)
  190. @property
  191. def version(self): return self._version