1
2 """
3 Provides a command-line interface to the SONY Reader PRS-500.
4
5 For usage information run the script.
6 """
7
8 import StringIO, sys, time, os
9 from optparse import OptionParser
10
11 from libprs500 import VERSION
12 from libprs500.communicate import PRS500Device
13 from libprs500.terminfo import TerminalController
14 from libprs500.errors import ArgumentError
15
16
17 MINIMUM_COL_WIDTH = 12
18
20 """ Convert a size in bytes into a human readle form """
21 if size < 1024: divisor, suffix = 1, ""
22 elif size < 1024*1024: divisor, suffix = 1024., "K"
23 elif size < 1024*1024*1024: divisor, suffix = 1024*1024, "M"
24 elif size < 1024*1024*1024*1024: divisor, suffix = 1024*1024, "G"
25 size = str(size/divisor)
26 if size.find(".") > -1: size = size[:size.find(".")+2]
27 return size + suffix
28
39
40 @apply
49 return property(**locals())
50
51 @apply
63 return property(**locals())
64
65 @apply
70 return property(**locals())
71
72 @apply
77 return property(**locals())
78
79 @apply
84 return property(**locals())
85
92
93 -def ls(dev, path, term, recurse=False, color=False, human_readable_size=False, ll=False, cols=0):
94 def col_split(l, cols):
95 rows = len(l) / cols
96 if len(l) % cols:
97 rows += 1
98 m = []
99 for i in range(rows):
100 m.append(l[i::rows])
101 return m
102
103 def row_widths(table):
104 tcols = len(table[0])
105 rowwidths = [ 0 for i in range(tcols) ]
106 for row in table:
107 c = 0
108 for item in row:
109 rowwidths[c] = len(item) if len(item) > rowwidths[c] else rowwidths[c]
110 c += 1
111 return rowwidths
112
113 output = StringIO.StringIO()
114 if path.endswith("/"): path = path[:-1]
115 dirs = dev.list(path, recurse)
116 for dir in dirs:
117 if recurse: print >>output, dir[0] + ":"
118 lsoutput, lscoloutput = [], []
119 files = dir[1]
120 maxlen = 0
121 if ll:
122 for file in files:
123 size = len(str(file.size))
124 if human_readable_size:
125 file = FileFormatter(file, term)
126 size = len(file.human_readable_size)
127 if size > maxlen: maxlen = size
128 for file in files:
129 file = FileFormatter(file, term)
130 name = file.name
131 lsoutput.append(name)
132 if color: name = file.name_in_color
133 lscoloutput.append(name)
134 if ll:
135 size = str(file.size)
136 if human_readable_size: size = file.human_readable_size
137 print >>output, file.mode_string, ("%"+str(maxlen)+"s")%size, file.modification_time, name
138 if not ll and len(lsoutput) > 0:
139 trytable = []
140 for colwidth in range(MINIMUM_COL_WIDTH, cols):
141 trycols = int(cols/colwidth)
142 trytable = col_split(lsoutput, trycols)
143 works = True
144 for row in trytable:
145 row_break = False
146 for item in row:
147 if len(item) > colwidth - 1:
148 works, row_break = False, True
149 break
150 if row_break: break
151 if works: break
152 rowwidths = row_widths(trytable)
153 trytablecol = col_split(lscoloutput, len(trytable[0]))
154 for r in range(len(trytable)):
155 for c in range(len(trytable[r])):
156 padding = rowwidths[c] - len(trytable[r][c])
157 print >>output, trytablecol[r][c], "".ljust(padding),
158 print >>output
159 print >>output
160 listing = output.getvalue().rstrip()+ "\n"
161 output.close()
162 return listing
163
165 term = TerminalController()
166 cols = term.COLS
167
168 parser = OptionParser(usage="usage: %prog [options] command args\n\ncommand is one of: info, df, ls, cp, mkdir, touch, cat or rm\n\n"+
169 "For help on a particular command: %prog command", version="libprs500 version: " + VERSION)
170 parser.add_option("--log-packets", help="print out packet stream to stdout. "+\
171 "The numbers in the left column are byte offsets that allow the packet size to be read off easily.",
172 dest="log_packets", action="store_true", default=False)
173 parser.remove_option("-h")
174 parser.disable_interspersed_args()
175 options, args = parser.parse_args()
176
177 if len(args) < 1:
178 parser.print_help()
179 sys.exit(1)
180 command = args[0]
181 args = args[1:]
182 dev = PRS500Device(log_packets=options.log_packets)
183 dev.open()
184 try:
185 if command == "df":
186 data = dev.available_space()
187 print "Filesystem\tSize \tUsed \tAvail \tUse%"
188 for datum in data:
189 total, free, used, percent = human_readable(datum[2]), human_readable(datum[1]), human_readable(datum[2]-datum[1]), \
190 str(0 if datum[2]==0 else int(100*(datum[2]-datum[1])/(datum[2]*1.)))+"%"
191 print "%-10s\t%s\t%s\t%s\t%s"%(datum[0], total, used, free, percent)
192 elif command == "mkdir":
193 parser = OptionParser(usage="usage: %prog mkdir [options] path\n\npath must begin with /,a:/ or b:/")
194 if len(args) != 1:
195 parser.print_help()
196 sys.exit(1)
197 dev.mkdir(args[0])
198 elif command == "ls":
199 parser = OptionParser(usage="usage: %prog ls [options] path\n\npath must begin with /,a:/ or b:/")
200 parser.add_option("--color", help="show ls output in color", dest="color", action="store_true", default=False)
201 parser.add_option("-l", help="In addition to the name of each file, print the file type, permissions, and timestamp (the modification time unless other times are selected). Times are local.", dest="ll", action="store_true", default=False)
202 parser.add_option("-R", help="Recursively list subdirectories encountered. /dev and /proc are omitted", dest="recurse", action="store_true", default=False)
203 parser.remove_option("-h")
204 parser.add_option("-h", "--human-readable", help="show sizes in human readable format", dest="hrs", action="store_true", default=False)
205 options, args = parser.parse_args(args)
206 if len(args) != 1:
207 parser.print_help()
208 sys.exit(1)
209 dev.open()
210 print ls(dev, args[0], term, color=options.color, recurse=options.recurse, ll=options.ll, human_readable_size=options.hrs, cols=cols),
211 elif command == "info":
212 info(dev)
213 elif command == "cp":
214 usage="usage: %prog cp [options] source destination\n\n"+\
215 "One of source or destination must be a path on the device. Device paths have the form:\n"+\
216 "device:mountpoint/my/path\n"+\
217 "where mountpoint is one of /, a: or b:\n"+\
218 "source must point to a file for which you have read permissions\n"+\
219 "destination must point to a file or directory for which you have write permissions"
220 parser = OptionParser(usage=usage)
221 options, args = parser.parse_args(args)
222 if len(args) != 2:
223 parser.print_help()
224 sys.exit(1)
225 if args[0].startswith("device:"):
226 outfile = args[1]
227 path = args[0][7:]
228 if path.endswith("/"): path = path[:-1]
229 if os.path.isdir(outfile):
230 outfile = os.path.join(outfile, path[path.rfind("/")+1:])
231 try:
232 outfile = open(outfile, "w")
233 except IOError, e:
234 print >> sys.stderr, e
235 parser.print_help()
236 sys.exit(1)
237 dev.get_file(path, outfile)
238 outfile.close()
239 elif args[1].startswith("device:"):
240 try:
241 infile = open(args[0], "r")
242 except IOError, e:
243 print >> sys.stderr, e
244 parser.print_help()
245 sys.exit(1)
246 dev.put_file(infile, args[1][7:])
247 infile.close()
248 else:
249 parser.print_help()
250 sys.exit(1)
251 elif command == "cat":
252 outfile = sys.stdout
253 parser = OptionParser(usage="usage: %prog cat path\n\npath should point to a file on the device and must begin with /,a:/ or b:/")
254 options, args = parser.parse_args(args)
255 if len(args) != 1:
256 parser.print_help()
257 sys.exit(1)
258 if args[0].endswith("/"): path = args[0][:-1]
259 else: path = args[0]
260 outfile = sys.stdout
261 dev.get_file(path, outfile)
262 elif command == "rm":
263 parser = OptionParser(usage="usage: %prog rm path\n\npath should point to a file or empty directory on the device "+\
264 "and must begin with /,a:/ or b:/\n\n"+\
265 "rm will DELETE the file. Be very CAREFUL")
266 options, args = parser.parse_args(args)
267 if len(args) != 1:
268 parser.print_help()
269 sys.exit(1)
270 dev.rm(args[0])
271 elif command == "touch":
272 parser = OptionParser(usage="usage: %prog touch path\n\npath should point to a file on the device and must begin with /,a:/ or b:/\n\n"+
273 "Unfortunately, I cant figure out how to update file times on the device, so if path already exists, touch does nothing" )
274 options, args = parser.parse_args(args)
275 if len(args) != 1:
276 parser.print_help()
277 sys.exit(1)
278 dev.touch(args[0])
279 else:
280 parser.print_help()
281 if dev.handle: dev.close()
282 sys.exit(1)
283 except ArgumentError, e:
284 print >>sys.stderr, e
285 sys.exit(1)
286 finally:
287 if dev.handle: dev.close()
288
289 if __name__ == "__main__":
290 main()
291