Menu ▾ ▴

Configuration File

TravisAlan
2011-08-03
2024-11-20
  • TravisAlan

    TravisAlan - 2011-08-03

    I noticed that one of the most in demand features for this project is the
    presence of a configuration file.
    So I took the code and here is a simple example I wrote out (added to 0.8
    code):

    import ConfigParser

    Config file here we come

    config = ConfigParser.RawConfigParser()
    pycmdConfig = expand_env_vars('%USERPROFILE%\pycmd.cfg')

    if os.path.isfile(pycmdConfig):

    Don't wanna overwrite their prefs

    config.read(pycmdConfig)
    else:
    config.add_section("CD(no args) - 'bash' or 'dos'")
    config.set('prefs', 'cdPref', 'bash')
    with open(pycmdConfig, 'wb') as configfile:
    config.write(configfile)

    cdPreference = config.get('prefs', 'cdPref')

    Only one setting in my example

    Then I implemented this when 'CD' is actually called

    def internal_cd(args):
    """The internal CD command"""
    try:
    if len(args) == 0 and cdPreference == 'bash':
    os.chdir(expand_env_vars('~'))
    elif len(args) == 0 and cdPreference == 'dos':
    print os.getcwd()
    else:
    target = args
    if target != '\' and target != ':\':
    target = target.rstrip('\')
    target = expand_env_vars(target.strip('"').strip(' '))
    os.chdir(target)
    except OSError, error:
    sys.stdout.write('\n' + str(error))
    os.environ = os.getcwd()

    There are a lot more settings I would like to implement through this and then
    make commands for changing the values in PyCmd instead of always directly
    editing the file, but I wanted to know what everyone else thought.

     
  • TravisAlan

    TravisAlan - 2011-08-03

    EDIT: In the couple places it says 'prefs' in here, it should actually have
    the section name which I changed for clarity to "CD(no args) - 'bash' or
    'dos'"

     
  • TravisAlan

    TravisAlan - 2011-08-08

    This is the code for myPyCmd-Set.py (uses mySettings.py as a configuration
    file)


    import sys, os, msvcrt, tempfile, signal, time, traceback
    import win32console, win32gui, win32con
    import mySettings

    from common import parse_line, unescape, sep_tokens, sep_chars
    from common import split_nocase, abbrev_path
    from common import expand_tilde, expand_env_vars
    from common import associated_application, full_executable_path,
    is_gui_application
    from completion import complete_file, complete_wildcard, complete_env_var,
    find_common_prefix, has_wildcards, wildcard_to_regex
    from InputState import ActionCode, InputState
    from DirHistory import DirHistory
    from console import get_text_attributes, set_text_attributes, get_buffer_size,
    set_console_title
    from console import move_cursor, get_cursor, cursor_backward
    from console import read_input, write_input
    from console import is_ctrl_pressed, is_alt_pressed, is_shift_pressed,
    is_control_only
    from console import scroll_buffer, get_viewport
    from console import FOREGROUND_WHITE, FOREGROUND_BRIGHT, FOREGROUND_RED
    from console import BACKGROUND_WHITE, BACKGROUND_BLUE, BACKGROUND_GREEN,
    BACKGROUND_RED

    def main():

    %APPDATA% is not always defined (e.g. when using runas.exe)

    if 'APPDATA' in os.environ.keys():
    APPDATA = '%APPDATA%'
    else:
    APPDATA = '%USERPROFILE%\Application Data'
    global pycmd_data_dir
    pycmd_data_dir = expand_env_vars(APPDATA + '\PyCmd')

    Create app data directory structure if not present

    if not os.path.isdir(pycmd_data_dir):
    os.mkdir(pycmd_data_dir)
    if not os.path.isdir(pycmd_data_dir + '\tmp'):
    os.mkdir(pycmd_data_dir + '\tmp')

    Current state of the input (prompt, entered chars, history)

    global state
    state = InputState()

    Read/initialize command history

    state.history = read_history(pycmd_data_dir + '\history')
    state.history_index = len(state.history)

    Read/initialize directory history

    global dir_hist
    dir_hist = DirHistory()
    dir_hist.locations = read_history(pycmd_data_dir + '\dir_history')
    dir_hist.index = len(dir_hist.locations) - 1
    dir_hist.visit_cwd()

    Create temporary file

    global tmpfile
    (handle, tmpfile) = tempfile.mkstemp(dir = pycmd_data_dir + '\tmp')
    os.close(handle)

    Catch SIGINT to emulate Ctrl-C key combo

    signal.signal(signal.SIGINT, signal_handler)

    Default options

    title_prefix = ""
    global quiet_mode
    quiet_mode = False

    Parse arguments

    arg = 1
    while arg < len(sys.argv):
    switch = sys.argv.upper()
    rest = ['"' + t + '"' if os.path.exists(t) else t
    for t in sys.argv]

    if switch in :

    Run the specified command and continue

    if rest != :
    run_command(rest)
    dir_hist.visit_cwd()
    break
    elif switch in :

    Run the specified command end exit

    if rest != :
    run_command(rest)
    internal_exit()
    elif switch in :

    Show usage information and exit

    print_usage()
    internal_exit()
    elif switch in :
    if arg == len(sys.argv) - 1:
    sys.stderr.write('PyCmd: no title specified to /T switch\n')
    print_usage()
    internal_exit()
    title_prefix = sys.argv + ' - '
    arg += 1
    elif switch in :

    Quiet mode: suppress messages

    quiet_mode = True
    else:

    Invalid command line switch

    sys.stderr.write('PyCmd: unrecognized option `' + sys.argv + '\'\n')
    print_usage()
    internal_exit()
    arg += 1

    if not quiet_mode:

    Print some splash text

    try:
    from buildinfo import build_info
    except ImportError, ie:
    build_info = '<no build="" info="">' </no>

    print
    print 'Welcome to PyCmd %s!' % build_info
    print

    Run an empty command to initialize environment

    run_command()

    Main loop

    while True:

    Prepare buffer for reading one line

    state.reset_line(prompt())
    scrolling = False
    auto_select = False
    force_repaint = True
    dir_hist.shown = False
    print

    while True:

    Update console title and environment

    curdir = os.getcwd()
    curdir = curdir.upper() + curdir
    set_console_title(title_prefix + curdir + ' - PyCmd')
    os.environ = curdir

    Save default (original) text attributes

    orig_attr = get_text_attributes()

    if state.changed() or force_repaint:
    prev_total_len = len(state.prev_prompt + state.prev_before_cursor +
    state.prev_after_cursor)
    cursor_backward(len(state.prev_prompt + state.prev_before_cursor))
    sys.stdout.write('\r')

    Update the offset of the directory history in case of overflow

    Note that if the history display is marked as 'dirty'

    (dir_hist.shown == False) the result of this action can be

    ignored

    dir_hist.check_overflow(state.prompt)

    Write current line

    set_text_attributes(orig_attr ^ FOREGROUND_BRIGHT) # Revert brightness bit for
    prompt
    sys.stdout.write('\r' + state.prompt)
    if state.history_filter == '':
    line = state.before_cursor + state.after_cursor
    cursor = len(state.before_cursor)
    sel_start, sel_end = state.get_selection_range()
    set_text_attributes(orig_attr)
    sys.stdout.write(line)
    set_text_attributes(orig_attr ^ FOREGROUND_WHITE ^ BACKGROUND_WHITE)
    sys.stdout.write(line)
    set_text_attributes(orig_attr)
    sys.stdout.write(line)
    else:

    print '\n\n', (before_cursor + after_cursor).split(history_filter), '\n\n'

    (chunks, seps) = split_nocase(state.before_cursor + state.after_cursor,
    state.history_filter)

    print '\n\n', chunks, seps, '\n\n'

    for i in range(len(chunks)):
    set_text_attributes(orig_attr)
    sys.stdout.write(chunks_)
    if i < len(chunks) - 1:
    set_text_attributes(orig_attr ^ BACKGROUND_BLUE ^ BACKGROUND_RED ^
    FOREGROUND_BRIGHT)
    sys.stdout.write(seps_)
    set_text_attributes(orig_attr)

    Erase remaining chars from old line

    to_erase = prev_total_len - len(state.prompt + state.before_cursor +
    state.after_cursor)
    if to_erase > 0:
    for i in range(to_erase):
    sys.stdout.write(' ')
    cursor_backward(to_erase)
    cursor_backward(len(state.after_cursor))

    Prepare new input state

    state.step_line()

    Read and process a keyboard event

    rec = read_input()
    select = auto_select or is_shift_pressed(rec)

    Will be overriden if Shift-PgUp/Dn is pressed

    force_repaint = not is_control_only(rec)

    print '\n\n', rec.keyDown, rec.char, rec.virtualKeyCode, rec.controlKeyState,

    '\n\n'
    if is_ctrl_pressed(rec) and not is_alt_pressed(rec): # Ctrl-Something
    if rec.Char == chr(4): # Ctrl-D
    if state.before_cursor + state.after_cursor == '':
    internal_exit('\r\nBye!')
    else:
    state.handle(ActionCode.ACTION_DELETE)
    elif rec.Char == chr(31): # Ctrl-_
    state.handle(ActionCode.ACTION_UNDO_EMACS)
    auto_select = False
    elif rec.VirtualKeyCode == 75: # Ctrl-K
    state.handle(ActionCode.ACTION_KILL_EOL)
    elif rec.VirtualKeyCode == 32: # Ctrl-Space
    auto_select = True
    state.reset_selection()
    elif rec.VirtualKeyCode == 71: # Ctrl-G
    if scrolling:
    scrolling = False
    else:
    state.handle(ActionCode.ACTION_ESCAPE)
    save_history(state.history,
    pycmd_data_dir + '\history',
    1000)
    elif rec.VirtualKeyCode == 65: # Ctrl-A
    state.handle(ActionCode.ACTION_HOME, select)
    elif rec.VirtualKeyCode == 69: # Ctrl-E
    state.handle(ActionCode.ACTION_END, select)
    elif rec.VirtualKeyCode == 66: # Ctrl-B
    state.handle(ActionCode.ACTION_LEFT, select)
    elif rec.VirtualKeyCode == 70: # Ctrl-F
    state.handle(ActionCode.ACTION_RIGHT, select)
    elif rec.VirtualKeyCode == 80: # Ctrl-P
    state.handle(ActionCode.ACTION_PREV)
    elif rec.VirtualKeyCode == 78: # Ctrl-N
    state.handle(ActionCode.ACTION_NEXT)
    elif rec.VirtualKeyCode == 37: # Ctrl-Left
    state.handle(ActionCode.ACTION_LEFT_WORD, select)
    elif rec.VirtualKeyCode == 39: # Ctrl-Right
    state.handle(ActionCode.ACTION_RIGHT_WORD, select)
    elif rec.VirtualKeyCode == 46: # Ctrl-Delete
    state.handle(ActionCode.ACTION_DELETE_WORD)
    elif rec.VirtualKeyCode == 67: # Ctrl-C

    The Ctrl-C signal is caught by our custom handler, and a

    synthetic keyboard event is created so that we can catch

    it here

    if state.get_selection() != '':
    state.handle(ActionCode.ACTION_COPY)
    else:
    state.handle(ActionCode.ACTION_ESCAPE)
    auto_select = False
    elif rec.VirtualKeyCode == 88: # Ctrl-X
    state.handle(ActionCode.ACTION_CUT)
    auto_select = False
    elif rec.VirtualKeyCode == 87: # Ctrl-W
    state.handle(ActionCode.ACTION_CUT)
    auto_select = False
    elif rec.VirtualKeyCode == 86: # Ctrl-V
    state.handle(ActionCode.ACTION_PASTE)
    auto_select = False
    elif rec.VirtualKeyCode == 89: # Ctrl-Y
    state.handle(ActionCode.ACTION_PASTE)
    auto_select = False
    elif rec.VirtualKeyCode == 8: # Ctrl-Backspace
    state.handle(ActionCode.ACTION_BACKSPACE_WORD)
    elif rec.VirtualKeyCode == 90:
    if not is_shift_pressed(rec): # Ctrl-Z
    state.handle(ActionCode.ACTION_UNDO)
    else: # Ctrl-Shift-Z
    state.handle(ActionCode.ACTION_REDO)
    auto_select = False
    elif is_alt_pressed(rec) and not is_ctrl_pressed(rec): # Alt-Something
    if rec.VirtualKeyCode in + range(49, 59): # Dir history
    if state.before_cursor + state.after_cursor == '':
    state.reset_prev_line()
    if rec.VirtualKeyCode == 37: # Alt-Left
    changed = dir_hist.go_left()
    elif rec.VirtualKeyCode == 39: # Alt-Right
    changed = dir_hist.go_right()
    else: # Alt-1..Alt-9
    changed = dir_hist.jump(rec.VirtualKeyCode - 48)
    if changed:
    state.prev_prompt = state.prompt
    state.prompt = prompt()
    save_history(dir_hist.locations,
    pycmd_data_dir + '\dir_history',
    dir_hist.max_len)
    if dir_hist.shown:
    dir_hist.display()
    sys.stdout.write(state.prev_prompt)
    else:
    if rec.VirtualKeyCode == 37: # Alt-Left
    state.handle(ActionCode.ACTION_LEFT_WORD, select)
    elif rec.VirtualKeyCode == 39: # Alt-Right
    state.handle(ActionCode.ACTION_RIGHT_WORD, select)
    elif rec.VirtualKeyCode == 66: # Alt-B
    state.handle(ActionCode.ACTION_LEFT_WORD, select)
    elif rec.VirtualKeyCode == 70: # Alt-F
    state.handle(ActionCode.ACTION_RIGHT_WORD, select)
    elif rec.VirtualKeyCode == 80: # Alt-P
    state.handle(ActionCode.ACTION_PREV)
    elif rec.VirtualKeyCode == 78: # Alt-N
    state.handle(ActionCode.ACTION_NEXT)
    elif rec.VirtualKeyCode == 68: # Alt-D
    if state.before_cursor + state.after_cursor == '':
    dir_hist.display()
    dir_hist.check_overflow(state.prev_prompt)
    sys.stdout.write(state.prev_prompt)
    else:
    state.handle(ActionCode.ACTION_DELETE_WORD)
    elif rec.VirtualKeyCode == 87: # Alt-W
    state.handle(ActionCode.ACTION_COPY)
    state.reset_selection()
    auto_select = False
    elif rec.VirtualKeyCode == 46: # Alt-Delete
    state.handle(ActionCode.ACTION_DELETE_WORD)
    elif rec.VirtualKeyCode == 8: # Alt-Backspace
    state.handle(ActionCode.ACTION_BACKSPACE_WORD)
    elif rec.VirtualKeyCode == 191:
    state.handle(ActionCode.ACTION_EXPAND)
    elif is_shift_pressed(rec) and rec.VirtualKeyCode == 33: # Shift-PgUp
    (, t, , b) = get_viewport()
    scroll_buffer(t - b + 2)
    scrolling = True
    force_repaint = False
    elif is_shift_pressed(rec) and rec.VirtualKeyCode == 34: # Shift-PgDn
    (, t, , b) = get_viewport()
    scroll_buffer(b - t - 2)
    scrolling = True
    force_repaint = False
    else: # Clean key (no modifiers)
    if rec.Char == chr(0): # Special key (arrows and such)
    if rec.VirtualKeyCode == 37: # Left arrow
    state.handle(ActionCode.ACTION_LEFT, select)
    elif rec.VirtualKeyCode == 39: # Right arrow
    state.handle(ActionCode.ACTION_RIGHT, select)
    elif rec.VirtualKeyCode == 36: # Home
    state.handle(ActionCode.ACTION_HOME, select)
    elif rec.VirtualKeyCode == 35: # End
    state.handle(ActionCode.ACTION_END, select)
    elif rec.VirtualKeyCode == 38: # Up arrow
    state.handle(ActionCode.ACTION_PREV)
    elif rec.VirtualKeyCode == 40: # Down arrow
    state.handle(ActionCode.ACTION_NEXT)
    elif rec.VirtualKeyCode == 46: # Delete
    state.handle(ActionCode.ACTION_DELETE)
    elif rec.Char == chr(13): # Enter
    state.reset_history()
    break
    elif rec.Char == chr(27): # Esc
    if scrolling:
    scrolling = False
    else:
    state.handle(ActionCode.ACTION_ESCAPE)
    save_history(state.history,
    pycmd_data_dir + '\history',
    1000)
    elif rec.Char == '\t': # Tab
    sys.stdout.write(state.after_cursor) # Move cursor to the end

    tokens = parse_line(state.before_cursor)
    if tokens == or state.before_cursor in sep_chars:
    tokens.append('') # This saves some checks later on
    if tokens.strip('"').count('%') % 2 == 1 or tokens.strip('"').endswith('%'):
    (completed, suggestions) = complete_env_var(state.before_cursor)
    elif has_wildcards(tokens):
    (completed, suggestions) = complete_wildcard(state.before_cursor)
    else:
    (completed, suggestions) = complete_file(state.before_cursor)

    Show multiple completions if available

    if len(suggestions) > 1:
    dir_hist.shown = False # The displayed dirhist is no longer valid
    column_width = max() + 10
    if column_width > get_buffer_size() - 1:
    column_width = get_buffer_size() - 1
    if len(suggestions) > (get_viewport() - get_viewport()) / 4:

    We print multiple columns to save space

    num_columns = (get_buffer_size() - 1) / column_width
    else:

    We print a single column for clarity

    num_columns = 1
    num_lines = len(suggestions) / num_columns
    if len(suggestions) % num_columns != 0:
    num_lines += 1

    num_screens = 1.0 * num_lines / (get_viewport() - get_viewport())
    if num_screens >= 0.9:

    We ask for confirmation before displaying many completions

    (c_x, c_y) = get_cursor()
    offset_from_bottom = get_buffer_size() - c_y
    message = ' Scroll ' + str(int(round(num_screens))) + ' screens? '
    sys.stdout.write('\n' + message)
    rec = read_input()
    move_cursor(c_x, get_buffer_size() - offset_from_bottom)
    sys.stdout.write('\n' + ' ' * len(message))
    move_cursor(c_x, get_buffer_size() - offset_from_bottom)
    if rec.Char != '\t':
    continue

    sys.stdout.write('\n')
    for line in range(0, num_lines):

    Print one line

    sys.stdout.write('\r')
    for column in range(0, num_columns):
    if line + column * num_lines < len(suggestions):
    s = suggestions
    if has_wildcards(tokens):

    Print wildcard matches in a different color

    tokens = parse_line(completed.rstrip('\'))
    token = tokens.replace('"', '')
    (, , prefix) = token.rpartition('\')
    match = wildcard_to_regex(prefix + '*').match(s)
    current_index = 0
    for i in range(1, match.lastindex + 1):
    set_text_attributes(orig_attr ^ FOREGROUND_RED)
    sys.stdout.write(s)
    set_text_attributes(orig_attr)
    sys.stdout.write(s)
    current_index = match.end(i)
    sys.stdout.write(' ' * (column_width - len(s)))
    else:

    Print the common part in a different color

    common_prefix_len = len(find_common_prefix(state.before_cursor, suggestions))
    set_text_attributes(orig_attr ^ FOREGROUND_RED)
    sys.stdout.write(s)
    set_text_attributes(orig_attr)
    sys.stdout.write(s)
    sys.stdout.write(' ' * (column_width - len(s)))
    sys.stdout.write('\n')
    line += 1
    state.reset_prev_line()
    state.handle(ActionCode.ACTION_COMPLETE, completed)
    elif rec.Char == chr(8): # Backspace
    state.handle(ActionCode.ACTION_BACKSPACE)
    else: # Regular character
    state.handle(ActionCode.ACTION_INSERT, rec.Char)

    Done reading line, now execute

    sys.stdout.write(state.after_cursor) # Move cursor to the end
    line = (state.before_cursor + state.after_cursor).strip()
    tokens = parse_line(line)
    if tokens == or tokens == '':
    continue
    else:
    print
    run_command(tokens)

    Add to history

    state.add_to_history(line)
    save_history(state.history,
    pycmd_data_dir + '\history',
    1000)

    Add to dir history

    dir_hist.visit_cwd()
    save_history(dir_hist.locations,
    pycmd_data_dir + '\dir_history',
    dir_hist.max_len)

    def internal_cd(args):
    """The internal CD command"""
    try:
    if len(args) == 0 and mySettings.bashCD == True:
    os.chdir(expand_env_vars('~'))
    elif len(args) == 0:
    print os.getcwd()
    else:
    target = args
    if target != '\' and target != ':\':
    target = target.rstrip('\')
    target = expand_env_vars(target.strip('"').strip(' '))
    os.chdir(target)
    except OSError, error:
    sys.stdout.write('\n' + str(error))
    os.environ = os.getcwd()

    def internal_exit(message = ''):
    """The EXIT command, with an optional goodbye message"""
    global quiet_mode
    if ((not quiet_mode) and message != ''):
    print message
    os.remove(tmpfile)
    sys.exit()

    def run_command(tokens):
    """Execute a command line (treat internal and external appropriately"""
    if tokens == 'exit':
    internal_exit('Bye!')
    elif tokens.lower() == 'cd' and == :

    This is a single CD command -- use our custom, more handy CD

    internal_cd([unescape(t) for t in tokens])
    else:
    if set(sep_tokens).intersection(tokens) == set():

    This is a simple (non-compound) command

    Crude hack so that we return to the prompt when starting GUI

    applications: if we think that the first token on the given command

    line is an executable, check its PE header to decide whether it's

    GUI application. If it is, spawn the process and then get on with

    life.

    cmd = expand_env_vars(tokens.strip('"'))
    dir, name = os.path.split(cmd)
    ext = os.path.splitext(name)

    if ext in :

    Executable given

    app = cmd
    else:

    Not an executable -- search for the associated application

    if os.path.isfile(cmd):
    app = associated_application(ext)
    else:

    No application will be spawned if the file doesn't exist

    app = None

    if app:
    executable = full_executable_path(app)
    if executable and os.path.splitext(executable).lower() == '.exe':

    This is an exe file, try to figure out whether it's a GUI

    or console application

    if is_gui_application(executable):
    import subprocess
    subprocess.Popen(' '.join(), shell=True)
    return

    Regular (external) command

    start_time = time.time()
    run_in_cmd(tokens)
    console_window = win32console.GetConsoleWindow()
    if win32gui.GetForegroundWindow() != console_window and time.time() -
    start_time > 15:

    If the window is inactive, flash after long tasks

    win32gui.FlashWindowEx(console_window, win32con.FLASHW_ALL, 3, 750)

    def run_in_cmd(tokens):
    pseudo_vars =

    line_sanitized = ''
    for token in tokens:
    token_sane = expand_tilde(token)
    if token_sane != '\' and token_sane != ':\':
    token_sane = token_sane.rstrip('\')
    if token_sane.count('"') % 2 == 1:
    token_sane += '"'
    line_sanitized += token_sane + ' '
    line_sanitized = line_sanitized
    if line_sanitized.endswith('&') and not line_sanitized.endswith('^&'):

    We remove a redundant & to avoid getting an 'Unexpected &' error when

    we append a new one below; the ending & it would be ignored by cmd.exe

    anyway...

    line_sanitized = line_sanitized
    elif line_sanitized.endswith('|') and not line_sanitized.endswith('^|') \
    or line_sanitized.endswith('&&') and not line_sanitized.endswith('^&&'):

    The syntax of the command is incorrect, cmd would refuse to execute it

    altogether; in order to we replicate the error message, we run a simple

    invalid command and return

    print
    os.system('echo |')
    return

    Cleanup environment

    for var in pseudo_vars:
    if var in os.environ.keys():
    del os.environ

    Run command

    if line_sanitized != '':
    command = '"'
    command += line_sanitized
    command += ' &set > "' + tmpfile + '"'
    for var in pseudo_vars:
    command += ' & echo ' + var + '="%' + var + '%" >> "' + tmpfile + '"'
    command += '& <nul (set="" p="" xxx="CD=)">>"' + tmpfile + '" & cd >>"' + tmpfile +
    '"'
    command += '"'
    os.system(command) </nul>

    Update environment and state

    new_environ = {}
    env_file = open(tmpfile, 'r')
    for l in env_file.readlines():
    = l.split('=', 1)
    value = value.rstrip('\n ')
    if variable in pseudo_vars:
    value = value.strip('"')
    new_environ = value
    env_file.close()
    if new_environ != {}:
    for variable in os.environ.keys():
    if not variable in new_environ.keys() \
    and sorted(new_environ.keys()) != sorted(pseudo_vars):
    del os.environ
    for variable in new_environ:
    os.environ = new_environ
    os.chdir(os.environ)

    def prompt():
    """Return a custom prompt"""
    if not mySettings.prompt:
    curdir = os.getcwd()
    curdir = curdir.upper() + curdir
    return abbrev_path(curdir) + '> '
    else:
    return mySettings.prompt

    def signal_handler(signum, frame):
    """
    Signal handler that catches SIGINT and emulates the Ctrl-C
    keyboard combo
    """
    if signum == signal.SIGINT:

    Emulate a Ctrl-C press

    write_input(67, 0x0008)

    def save_history(lines, filename, length):
    """
    Save a list of unique lines into a history file and truncate the
    result to the given maximum number of lines
    """
    if os.path.isfile(filename):

    Read previously saved history and merge with current

    history_file = open(filename, 'r')
    history_to_save =
    history_file.close()
    for line in lines:
    if line in history_to_save:
    history_to_save.remove(line)
    history_to_save.append(line)
    else:

    No previous history, save current

    history_to_save = lines

    if len(history_to_save) > length:
    history_to_save = history_to_save # Limit history file

    Write merged history to history file

    history_file = open(filename, 'w')
    history_file.writelines()
    history_file.close()

    def read_history(filename):
    """
    Read and return a list of lines from a history file
    """
    if os.path.isfile(filename):
    history_file = open(filename, 'r')
    history =
    history_file.close()
    else:
    print 'Warning: Can\'t open ' + os.path.basename(filename) + '!'
    history =
    return history

    def print_usage():
    """Print usage information"""
    print 'Usage:'
    print '\t PyCmd ( | | )'
    print
    print '\t\t-c command \tRun command, then exit'
    print '\t\t-k command \tRun command, then continue to the prompt'
    print '\t\t-t title \tShow title in window caption'
    print '\t\t-q\t\tQuiet (suppress messages)'
    print '\t\t-h \t\tShow this help'
    print
    print 'Note that you can use \'/\' instead of \'-\', uppercase instead of '
    print 'lowercase and \'/?\' instead of \'-h\''

    Entry point

    if name == 'main':
    try:
    main()
    except Exception, e:
    global pycmd_data_dir
    report_file_name = (pycmd_data_dir

    • '\crash-'
    • time.strftime('%Y%m%d_%H%M%S')
    • '.log')
      print '\n'
      print '***'
      print 'Internal error in PyCmd!'
      report_file = open(report_file_name, 'w')
      traceback.print_exc(file=report_file)
      report_file.close()
      traceback.print_exc()
      print
      print 'Crash report written to:\n ' + report_file_name
      print 'Exiting... sorry :('
      print '
      ***'

    And this is mySettings.py

    settings.py

    PyCmd configuration

    Behavior

    If marked False then CD (no args) will print current directory

    bashCD = True

    If marked False then tab will cycle completions

    This is NOT functional, help is needed to utilize Windows completion

    bashTabs = False

    Appearance

    For a custom prompt merely insert below, empty is current directory

    It is suggested to end it with a special character then space e.g. '> '

    prompt = ''

    Please give feedback. I would also like some help getting the Windows tab
    completion working.__

     
  • Horea Haitonic

    Horea Haitonic - 2011-08-08

    Thanks for the work, Travis; do you think you could send me a diff file?
    Copying and pasting from the forum text garbles the formatting. First look at
    the code prompts a few suggestions:

    • did you consider a more elaborate scheme for organizing the options? e.g. in some sort of a hierarchy using nested objects or whatnot (think appearance.bgcolor, behavior.cd_mode etc)? I think it's pretty important to get things right here from the start
    • how about the location of the mySettings.py? shouldn't it be searched in the standard PyCmd AppData?
    • what about the case when the mySettings.py doesn't exist? we should handle this case as well
     
  • Horea Haitonic

    Horea Haitonic - 2011-08-11

    Hi Travis,

    What's the status for this issue? Are you still working on it? Can I help
    somehow? Or should I try to take it from here and build upon your work?

     
  • TravisAlan

    TravisAlan - 2011-08-23

    I still would like some help on this. Specifically links to configuration
    files set up similarly. I know nested options will be important and would like
    to know how that is commonly done (in a function?) and I definitely need help
    making the standard Windows tab work as an alternative. Someone please hit
    back on this. I am just getting back into the time to really work here so I
    would like a reply promptly (some pun intended),

     
  • Horea Haitonic

    Horea Haitonic - 2011-08-26

    A.
    My current ideas of implementing the config loading are along the following
    lines:

    1. in PyCmd.py, create the 'appearance', 'behavior'... objects with default values for the fields
    2. if present, run settings.py from the "install" location -- this would implement "global" settings
    3. if present, run settings.py from the user's AppData -- for the user settings

    Running settings.py should be probably done using exec; this would allow
    direct assignment of new values to the fields of the objects from 1,
    overriding the default values.

    Does this sound reasonable?

    B. The cycling Tab behavior is something I never gave too much thought to (I
    personally prefer the bash mode). You can try to implement it yourself if you
    want -- in this case, I'd pick up the settings task from where you left it. Of
    course, I can provide some hints on where/how to begin if needed

     
  • Horea Haitonic

    Horea Haitonic - 2011-09-22

    I began developing on this, using Travis' work as a starting point; the
    hierarchical options + execfile really seem like the right way to go.

    I'll report here when there's something available to play with.

     
  • Horea Haitonic

    Horea Haitonic - 2011-09-28

    I pushed an implementation; see the issue page for details:

    {https://sourceforge.net/tracker/?func=detail&aid=3386080&group_id=261720&atid
    =1127598}