4
4
5
5
import serial .tools .list_ports
6
6
7
- from .transport import TransportError
8
- from .transport_serial import SerialTransport , stdout_write_bytes
7
+ from .transport import TransportError , stdout_write_bytes
8
+ from .transport_serial import SerialTransport
9
9
10
10
11
11
class CommandError (Exception ):
@@ -106,61 +106,238 @@ def show_progress_bar(size, total_size, op="copying"):
106
106
)
107
107
108
108
109
+ def _remote_path_join (a , * b ):
110
+ if not a :
111
+ a = "./"
112
+ result = a .rstrip ("/" )
113
+ for x in b :
114
+ result += "/" + x .strip ("/" )
115
+ return result
116
+
117
+
118
+ def _remote_path_dirname (a ):
119
+ a = a .rsplit ("/" , 1 )
120
+ if len (a ) == 1 :
121
+ return ""
122
+ else :
123
+ return a [0 ]
124
+
125
+
126
+ def _remote_path_basename (a ):
127
+ return a .rsplit ("/" , 1 )[- 1 ]
128
+
129
+
130
+ def do_filesystem_cp (state , src , dest , multiple ):
131
+ if dest .startswith (":" ):
132
+ dest_exists = state .transport .fs_exists (dest [1 :])
133
+ dest_isdir = dest_exists and state .transport .fs_isdir (dest [1 :])
134
+ else :
135
+ dest_exists = os .path .exists (dest )
136
+ dest_isdir = dest_exists and os .path .isdir (dest )
137
+
138
+ if multiple :
139
+ if not dest_exists :
140
+ raise CommandError ("cp: destination does not exist" )
141
+ if not dest_isdir :
142
+ raise CommandError ("cp: destination is not a directory" )
143
+
144
+ # Download the contents of source.
145
+ try :
146
+ if src .startswith (":" ):
147
+ data = state .transport .fs_readfile (src [1 :], progress_callback = show_progress_bar )
148
+ filename = _remote_path_basename (src [1 :])
149
+ else :
150
+ with open (src , "rb" ) as f :
151
+ data = f .read ()
152
+ filename = os .path .basename (src )
153
+ except IsADirectoryError :
154
+ raise CommandError ("cp: -r not specified; omitting directory" )
155
+
156
+ # Write back to dest.
157
+ if dest .startswith (":" ):
158
+ # If the destination path is just the directory, then add the source filename.
159
+ if dest_isdir :
160
+ dest = ":" + _remote_path_join (dest [1 :], filename )
161
+
162
+ # Write to remote.
163
+ state .transport .fs_writefile (dest [1 :], data , progress_callback = show_progress_bar )
164
+ else :
165
+ # If the destination path is just the directory, then add the source filename.
166
+ if dest_isdir :
167
+ dest = os .path .join (dest , filename )
168
+
169
+ # Write to local file.
170
+ with open (dest , "wb" ) as f :
171
+ f .write (data )
172
+
173
+
174
+ def do_filesystem_recursive_cp (state , src , dest , multiple ):
175
+ # Ignore trailing / on both src and dest. (Unix cp ignores them too)
176
+ src = src .rstrip ("/" + os .path .sep + (os .path .altsep if os .path .altsep else "" ))
177
+ dest = dest .rstrip ("/" + os .path .sep + (os .path .altsep if os .path .altsep else "" ))
178
+
179
+ # If the destination directory exists, then we copy into it. Otherwise we
180
+ # use the destination as the target.
181
+ if dest .startswith (":" ):
182
+ dest_exists = state .transport .fs_exists (dest [1 :])
183
+ else :
184
+ dest_exists = os .path .exists (dest )
185
+
186
+ # Recursively find all files to copy from a directory.
187
+ # `dirs` will be a list of dest split paths.
188
+ # `files` will be a list of `(dest split path, src joined path)`.
189
+ dirs = []
190
+ files = []
191
+
192
+ # For example, if src=/tmp/foo, with /tmp/foo/x.py and /tmp/foo/a/b/c.py,
193
+ # and if the destination directory exists, then we will have:
194
+ # dirs = [['foo'], ['foo', 'a'], ['foo', 'a', 'b']]
195
+ # files = [(['foo', 'x.py'], '/tmp/foo/x.py'), (['foo', 'a', 'b', 'c.py'], '/tmp/foo/a/b/c.py')]
196
+ # If the destination doesn't exist, then we will have:
197
+ # dirs = [['a'], ['a', 'b']]
198
+ # files = [(['x.py'], '/tmp/foo/x.py'), (['a', 'b', 'c.py'], '/tmp/foo/a/b/c.py')]
199
+
200
+ def _list_recursive (base , src_path , dest_path , src_join_fun , src_isdir_fun , src_listdir_fun ):
201
+ src_path_joined = src_join_fun (base , * src_path )
202
+ if src_isdir_fun (src_path_joined ):
203
+ if dest_path :
204
+ dirs .append (dest_path )
205
+ for entry in src_listdir_fun (src_path_joined ):
206
+ _list_recursive (
207
+ base ,
208
+ src_path + [entry ],
209
+ dest_path + [entry ],
210
+ src_join_fun ,
211
+ src_isdir_fun ,
212
+ src_listdir_fun ,
213
+ )
214
+ else :
215
+ files .append (
216
+ (
217
+ dest_path ,
218
+ src_path_joined ,
219
+ )
220
+ )
221
+
222
+ if src .startswith (":" ):
223
+ src_dirname = [_remote_path_basename (src [1 :])]
224
+ dest_dirname = src_dirname if dest_exists else []
225
+ _list_recursive (
226
+ _remote_path_dirname (src [1 :]),
227
+ src_dirname ,
228
+ dest_dirname ,
229
+ src_join_fun = _remote_path_join ,
230
+ src_isdir_fun = state .transport .fs_isdir ,
231
+ src_listdir_fun = lambda p : [x .name for x in state .transport .fs_listdir (p )],
232
+ )
233
+ else :
234
+ src_dirname = [os .path .basename (src )]
235
+ dest_dirname = src_dirname if dest_exists else []
236
+ _list_recursive (
237
+ os .path .dirname (src ),
238
+ src_dirname ,
239
+ dest_dirname ,
240
+ src_join_fun = os .path .join ,
241
+ src_isdir_fun = os .path .isdir ,
242
+ src_listdir_fun = os .listdir ,
243
+ )
244
+
245
+ # If no directories were encountered then we must have just had a file.
246
+ if not dirs :
247
+ return do_filesystem_cp (state , src , dest , multiple )
248
+
249
+ def _mkdir (a , * b ):
250
+ try :
251
+ if a .startswith (":" ):
252
+ state .transport .fs_mkdir (_remote_path_join (a [1 :], * b ))
253
+ else :
254
+ os .mkdir (os .path .join (a , * b ))
255
+ except FileExistsError :
256
+ pass
257
+
258
+ # Create the destination if necessary.
259
+ if not dest_exists :
260
+ _mkdir (dest )
261
+
262
+ # Create all sub-directories relative to the destination.
263
+ for d in dirs :
264
+ _mkdir (dest , * d )
265
+
266
+ # Copy all files, in sorted order to help it be deterministic.
267
+ files .sort ()
268
+ for dest_path_split , src_path_joined in files :
269
+ if src .startswith (":" ):
270
+ src_path_joined = ":" + src_path_joined
271
+
272
+ if dest .startswith (":" ):
273
+ dest_path_joined = ":" + _remote_path_join (dest [1 :], * dest_path_split )
274
+ else :
275
+ dest_path_joined = os .path .join (dest , * dest_path_split )
276
+
277
+ do_filesystem_cp (state , src_path_joined , dest_path_joined , multiple = False )
278
+
279
+
109
280
def do_filesystem (state , args ):
110
281
state .ensure_raw_repl ()
111
282
state .did_action ()
112
283
113
- def _list_recursive (files , path ):
114
- if os .path .isdir (path ):
115
- for entry in os .listdir (path ):
116
- _list_recursive (files , "/" .join ((path , entry )))
117
- else :
118
- files .append (os .path .split (path ))
119
-
120
284
command = args .command [0 ]
121
285
paths = args .path
122
286
123
287
if command == "cat" :
124
- # Don't be verbose by default when using cat, so output can be
125
- # redirected to something.
288
+ # Don't do verbose output for `cat` unless explicitly requested.
126
289
verbose = args .verbose is True
127
290
else :
128
291
verbose = args .verbose is not False
129
292
130
- if command == "cp" and args .recursive :
131
- if paths [- 1 ] != ":" :
132
- raise CommandError ("'cp -r' destination must be ':'" )
133
- paths .pop ()
134
- src_files = []
135
- for path in paths :
136
- if path .startswith (":" ):
137
- raise CommandError ("'cp -r' source files must be local" )
138
- _list_recursive (src_files , path )
139
- known_dirs = {"" }
140
- state .transport .exec ("import os" )
141
- for dir , file in src_files :
142
- dir_parts = dir .split ("/" )
143
- for i in range (len (dir_parts )):
144
- d = "/" .join (dir_parts [: i + 1 ])
145
- if d not in known_dirs :
146
- state .transport .exec (
147
- "try:\n os.mkdir('%s')\n except OSError as e:\n print(e)" % d
148
- )
149
- known_dirs .add (d )
150
- state .transport .filesystem_command (
151
- ["cp" , "/" .join ((dir , file )), ":" + dir + "/" ],
152
- progress_callback = show_progress_bar ,
153
- verbose = verbose ,
154
- )
293
+ if command == "cp" :
294
+ # Note: cp requires the user to specify local/remote explicitly via
295
+ # leading ':'.
296
+
297
+ # The last argument must be the destination.
298
+ if len (paths ) <= 1 :
299
+ raise CommandError ("cp: missing destination path" )
300
+ cp_dest = paths [- 1 ]
301
+ paths = paths [:- 1 ]
155
302
else :
156
- if args .recursive :
157
- raise CommandError ("'-r' only supported for 'cp'" )
158
- try :
159
- state .transport .filesystem_command (
160
- [command ] + paths , progress_callback = show_progress_bar , verbose = verbose
161
- )
162
- except OSError as er :
163
- raise CommandError (er )
303
+ # All other commands implicitly use remote paths. Strip the
304
+ # leading ':' if the user included them.
305
+ paths = [path [1 :] if path .startswith (":" ) else path for path in paths ]
306
+
307
+ # ls implicitly lists the cwd.
308
+ if command == "ls" and not paths :
309
+ paths = ["" ]
310
+
311
+ # Handle each path sequentially.
312
+ for path in paths :
313
+ if verbose :
314
+ if command == "cp" :
315
+ print ("{} {} {}" .format (command , path , cp_dest ))
316
+ else :
317
+ print ("{} :{}" .format (command , path ))
318
+
319
+ if command == "cat" :
320
+ state .transport .fs_printfile (path )
321
+ elif command == "ls" :
322
+ for result in state .transport .fs_listdir (path ):
323
+ print (
324
+ "{:12} {}{}" .format (
325
+ result .st_size , result .name , "/" if result .st_mode & 0x4000 else ""
326
+ )
327
+ )
328
+ elif command == "mkdir" :
329
+ state .transport .fs_mkdir (path )
330
+ elif command == "rm" :
331
+ state .transport .fs_rmfile (path )
332
+ elif command == "rmdir" :
333
+ state .transport .fs_rmdir (path )
334
+ elif command == "touch" :
335
+ state .transport .fs_touchfile (path )
336
+ elif command == "cp" :
337
+ if args .recursive :
338
+ do_filesystem_recursive_cp (state , path , cp_dest , len (paths ) > 1 )
339
+ else :
340
+ do_filesystem_cp (state , path , cp_dest , len (paths ) > 1 )
164
341
165
342
166
343
def do_edit (state , args ):
@@ -174,11 +351,15 @@ def do_edit(state, args):
174
351
dest_fd , dest = tempfile .mkstemp (suffix = os .path .basename (src ))
175
352
try :
176
353
print ("edit :%s" % (src ,))
177
- os .close (dest_fd )
178
- state .transport .fs_touch (src )
179
- state .transport .fs_get (src , dest , progress_callback = show_progress_bar )
354
+ state .transport .fs_touchfile (src )
355
+ data = state .transport .fs_readfile (src , progress_callback = show_progress_bar )
356
+ with open (dest_fd , "wb" ) as f :
357
+ f .write (data )
180
358
if os .system ('%s "%s"' % (os .getenv ("EDITOR" ), dest )) == 0 :
181
- state .transport .fs_put (dest , src , progress_callback = show_progress_bar )
359
+ with open (dest , "rb" ) as f :
360
+ state .transport .fs_writefile (
361
+ src , f .read (), progress_callback = show_progress_bar
362
+ )
182
363
finally :
183
364
os .unlink (dest )
184
365
0 commit comments