Source code for TpddTool

'''
TpddTool.py
Copyright 2016 - Gary Hammond email gfhammond@gmail.com

Python command line tool for the Tandy TPDD and TPDD2 external floppy disk
drives.

The available commands are:

* copy <source> <dest>        - copy file from <source> to <dest>
* copydir <source> <dest>     - copy directory to/from disk
* del <file>                  - delete file on disk
* dir <bank\|dirspec>         - listing of disk bank (0:\|1:) or local directory
* format                      - format disk
* help or ?                   - displays this help information
* ren <from file> <to file>   - renames file <from> to <to>
* status                      - drive status
* type <file>                 - prints the contents of a file
* q or x                      - quit/exit

Notes:

* Note 1: It is assumed that you know what you are doing. There are no warnings or prompts! Formatting a disk does precisely that without a prompt.
* Note 2: Use the prefix 0: or 1: to specify a file on the TPDD disk for disk file commands. e.g. "0:hello.do". Filenames without the prefix will be considered to be on the local file system.
* Note 3: The linux equivalent cat, cp, rm, ls, mv can be used in the place of type, copy, del, dir and ren respectively. cpd can be used in place of copydir.
* Note 4: There is no wildcard support.

:param comport: The com port that the TPDD is connected to.

'''

from Tpdd import *
import argparse
import os
import sys

###############################################################################
# Constants
###############################################################################
CMD_PROMPT                  = 'TPDD-Tool>'
MAX_CMD_TOKENS              = 3
TPDD_VERSION                = 1.0

###############################################################################
# Variables
###############################################################################
cmd_param                   = ['none', 'none', 'none']
echo_all                    = False

###############################################################################
# Functions start here...
###############################################################################

[docs]def copy_command(dd, in_filename, out_filename): ''' Copy the contents of the in file to out file or prints an error message. :param dd: The TPDD object. :param in_filename: The file to read from. :param out_filename: The file to write to. ''' _return_code_item = ['', ''] # Copy the contents of the in file to out file or print an error message if (in_filename[0:2] == '0:' or in_filename[0:2] == '1:'): if dd.file_exists(in_filename.upper()): # We have a request to view a file on the TPDD _return_code_item = dd.load_file(in_filename.upper(), echo_all) if _return_code_item[0] == Tpdd.OK: if write_file(out_filename, _return_code_item[1]): print 'Copy successful' else: print 'Error writing to the local disk.' else: print 'Error reading from the TPDD disk.' else: print 'File', in_filename.upper(), 'does not exist' elif (out_filename[0:2] == '0:' or out_filename[0:2] == '1:'): _data = read_file(in_filename) if _data != '': _return_code_item = dd.save_file(out_filename.upper(), _data, echo_all) if _return_code_item[0] == Tpdd.OK: print 'Copy successful' else: print 'Error writing file to the TPDD disk.' else: print 'Error reading the source file.' else: _data = read_file(in_filename) if _data != '': if write_file(out_filename, _data): print 'Local copy successful.' else: print 'Error writing to the local disk.' else: print 'Error reading the source file.'
[docs]def copydir_command(dd, source, destination): ''' Copy the contents of the files in the directory/disk to the out directory/disk or prints an error message. :param dd: The TPDD object. :param source: The directory/disk to read from. :param destination: The directory/disk to write to. ''' _dir_list = [''] _cwd = os.getcwd() # Check to see if the source is the TPDD if (source == '0:' or source == '1:'): if not os.path.exists(destination): os.makedirs(destination) os.chdir(destination) _dir_list = get_dir(dd, source, size = False) for _item in _dir_list: _return_code_item = dd.load_file(source + _item) if _return_code_item[0] == Tpdd.OK: if write_file(_item.replace(" ",""), _return_code_item[1]): print 'Copied', _item else: print 'Failed writing to', _item else: print 'Unable to load the file', _item else: _dir_list = get_dir(dd, source, size = False) os.chdir(source) for _item in _dir_list: _data = read_file(_item) if _data != '': _return_code_item = dd.save_file(destination + _item.upper(), _data) if _return_code_item[0] == Tpdd.OK: print 'Copied', destination + _item else: print _return_code_item print 'Failed writing to', destination + _item else: print 'Unable to load the file', _item os.chdir(_cwd) print ''
[docs]def del_command(dd, filename): ''' Deletes the file specified from the TPDD disk. This command does not work on the local filesystem. :param dd: The TPDD object. :param filename: The file to be deleted. ''' if (filename[0:2] == '0:' or filename[0:2] == '1:'): _return_code_item = dd.delete_file(filename.upper()) if _return_code_item[0] is Tpdd.OK: print 'File deleted successfully' else: print _return_code_item[1] else: print 'Not a TPDD file'
[docs]def dir_command(dd, dirspec = '.'): ''' Prints a listing of the host directory or the TPDD bank if specified. :param dd: The TPDD object. :param dirspec: (optional) The directory to list. The default is the local host directory. ''' _dir_list = [''] if (dirspec != '0:') and (dirspec != '1:'): print 'Directory listing for "' + dirspec + '"' print '---------------------' _dir_list = get_dir(dd, dirspec) for _item in _dir_list: print _item print ''
[docs]def format_command(dd): ''' Formats the disk bank in the TPDD. Prints error message on failure. :param dd: The TPDD object. ''' _return_code = dd.get_status() if _return_code[0] is Tpdd.OK: print 'Formatting disk...' _return_code = dd.format_disk() if _return_code[0] is Tpdd.OK: print 'Format complete' else: print _return_code[1] else: print _return_code[1]
[docs]def get_dir(dd, dirspec, size = True): ''' gets a list of the files of the host directory or the TPDD bank if specified. :param dd: The TPDD object. :param dirspec: The directory to list. The default is the local host directory. :param size: (Optional) Used to include file sizes and bytes free in listing. Default is True. :returns: A list of directory entries. The TPDD list includes the bytes free entry as the last entry. ''' _return_code_item = ['', ''] if (dirspec == '0:') or (dirspec == '1:'): if dirspec == '0:': dd.set_bank('0') else: dd.set_bank('1') # Prints the directory of the disk bank in the TPDD. Print error message on failure _return_code_item = dd.get_status() if _return_code_item[0] is Tpdd.OK: _temp_list = dd.get_dir(size)[1] _first = True for _item in _temp_list: if _first: _dir_list = [_item] _first = False else: _dir_list.append(_item) else: return [_return_code_item[1]] else: try: _first = True for _item in os.listdir(dirspec): if _first: _dir_list = [_item] _first = False else: _dir_list.append(_item) except: return ['Incorrect directory specification'] return _dir_list
[docs]def help_command(): ''' Prints the hlep information for TpddTool. ''' print 'TPDD Python Tool -', TPDD_VERSION print 'TPDD API -', Tpdd.API_VERSION print '' print 'Type command followed by <enter>' print '' print 'copy <source> <destination> - copy file from <source> to <destination>' print 'copydir <source> <dest> - copy directory to/from disk' print 'del <file> - delete file on disk' print 'dir <bank|dirspec> - listing of disk bank (0:|1:) or local directory' print 'format - format disk' print 'help or ? - displays this help information' print 'ren <from file> <to file> - renames file <from> to <to>' print 'status - drive status' print 'type <file> - prints the contents of a file' print 'q or x - quit/exit' print '' print 'Note 1: It is assumed that you know what you are doing. There are no warnings' print ' or prompts! Formatting a disk does precisely that without a prompt.' print 'Note 2: Use the prefix 0: or 1: to specify a file on the TPDD disk for' print ' disk file commands. e.g. "0:hello.do". Filenames without the prefix' print ' will be considered to be on the local file system.' print 'Note 3: The linux equivalent cat, cp, rm, ls, mv can be used in the place of' print ' type, copy, del, dir and ren respectively. cpd can be used in place' print ' of copydir.' print 'Note 4: There is no wildcard support.' print ''
def print_bytes(_item): print(':'.join(x.encode('hex') for x in _item))
[docs]def process_command(dd, cmd_param, token_count): ''' Process commands typed in at the 'Tpdd-Tool>' prompt. :param dd: The TPDD object. :param cmd_param: List containing command and options (if any). :param token_count: Number of items in the 'cmd_param' list. ''' _command_found = True # No command typed if cmd_param[0] == '': _command_found = False # Exit script... elif cmd_param[0] == 'q' or cmd_param[0] == 'x': print 'Exiting script' # copy file elif (cmd_param[0] == 'copy' or cmd_param[0] == 'cp') and token_count == 3: copy_command(dd, cmd_param[1], cmd_param[2]) # copy directory elif (cmd_param[0] == 'copydir' or cmd_param[0] == 'cpd') and token_count == 3: copydir_command(dd, cmd_param[1], cmd_param[2]) # delete file elif (cmd_param[0] == 'del' or cmd_param[0] == 'rm') and token_count == 2: del_command(dd, cmd_param[1]) # dir listing elif (cmd_param[0] == 'dir' or cmd_param[0] == 'ls') and token_count <= 2: if token_count == 1: dir_command(dd) else: dir_command(dd, dirspec = cmd_param[1]) # format disk elif cmd_param[0] == 'format' and token_count == 1: format_command(dd) # help elif (cmd_param[0] == 'help' or cmd_param[0] == '?') and token_count == 1: help_command() # rename file elif (cmd_param[0] == 'ren' or cmd_param[0] == 'mv') and token_count == 3: ren_command(dd, cmd_param[1], cmd_param[2]) # disk status elif cmd_param[0] == 'status' and token_count == 1: status_command(dd) # type file elif (cmd_param[0] == 'type' or cmd_param[0] == 'cat') and token_count == 2: type_command(dd, cmd_param[1]) # command not recognised else: print 'Command not found or incorrect number of parameters' _command_found = False # Reset the command parameters cmd_param = ['none', 'none', 'none'] return _command_found
[docs]def read_file(filename): ''' Reads the contents of a local file. :param filename: The name of the file to read from. :returns: The data as read from the file if successful, otherwise an empty string. ''' # Check to see if the file can be opened for reading. try: file = open(filename, 'rb') # Read the data from the file try: _data = file.read() # Close the file try: file.close() except: print 'Error closing the file.' return '' except: print 'Unable to data from the file.' return '' except: print 'Error opening the file for reading.' return '' return _data
[docs]def ren_command(dd, old_filename, new_filename): ''' Function not implemented. ToDo. ''' _return_code_item = dd.rename_file(old_filename, new_filename) if _return_code_item[0] == Tpdd.OK: print 'Rename successful' else: print 'Rename failed -', _return_code_item[1]
[docs]def status_command(dd): ''' Returns the status of the TPDD as a text message. :param dd: The TPDD object. ''' print dd.get_status()[1]
[docs]def type_command(dd, filename): ''' Prints the contents of the file specified to stdout or prints an error message. :param dd: The TPDD object. :param filename: The file on the disk to read. ''' if (filename[0:2] == '0:' or filename[0:2] == '1:'): if dd.file_exists(filename.upper()): # We have a request to view a file on the TPDD _return_code = dd.load_file(filename.upper(), echo = True) print '' if _return_code[0] != Tpdd.OK: # Something went wrong during the printing of the file. print 'There was an error printing the file. The error was', _return_code[1] else: print 'File', filename.upper(), 'does not exist' else: # We have a request to view a file on the local filesystem try: file = open(filename, 'r') print file.read() except: print 'Error opening the file.'
[docs]def write_file(filename, data): ''' Writes data to a local file. :param filename: The name of the file to write to. :param data: The data to write to the file. :returns: True if successful, otherwise False. ''' # Check to see if the file can be opened for writing. try: file = open(filename, 'wb') # Write the data to the file try: file.write(data) # Close the file try: file.close() except: print 'Error closing the file.' return False except: print 'Unable to write the data to the file.' return False except: print 'Error opening the file for writing.' return False return True
############################################################################### # Main script starts here... ############################################################################### if __name__ == "__main__": parser = argparse.ArgumentParser(description="Command line tool for the Tandy TPDD and TPDD 2") parser.add_argument("com_port", help="The com port that the TPDD is connected to") args = parser.parse_args() disk_drive = Tpdd(args.com_port) ## Process commands try: while cmd_param[0] != 'q' and cmd_param[0] != 'x': sys.stdout.write(CMD_PROMPT) user_input = sys.stdin.readline() token_count = 0 if user_input != '': for token in user_input.split(' '): if token_count == MAX_CMD_TOKENS: break; cmd_param[token_count] = token.strip('\n\r') token_count += 1 else: cmd_param[0] = 'none' process_command(disk_drive, cmd_param, token_count) except KeyboardInterrupt: print 'Aborting script <ctrl-c>'