diff options
author | 2012-05-08 17:10:38 -0700 | |
---|---|---|
committer | 2012-05-08 17:10:38 -0700 | |
commit | 4bd63020ca897b4b65d74469a0036451a2e23fa4 (patch) | |
tree | 2b2e7e9bf6906d53e78776daa5b9fec2cf742b96 /share/tools/web_config/webconfig.py | |
parent | 4885842ae827cba51441c8a44351d12e75d9c618 (diff) |
Fix to add a little explanatory text to colors in the web config interface
Diffstat (limited to 'share/tools/web_config/webconfig.py')
-rwxr-xr-x | share/tools/web_config/webconfig.py | 522 |
1 files changed, 274 insertions, 248 deletions
diff --git a/share/tools/web_config/webconfig.py b/share/tools/web_config/webconfig.py index 20e5514f..1acf64a9 100755 --- a/share/tools/web_config/webconfig.py +++ b/share/tools/web_config/webconfig.py @@ -7,248 +7,274 @@ import subprocess import re, json, socket, os, sys, cgi, select def run_fish_cmd(text): - from subprocess import PIPE - p = subprocess.Popen(["fish"], stdin=PIPE, stdout=PIPE, stderr=PIPE) - out, err = p.communicate(text) - return out, err + from subprocess import PIPE + p = subprocess.Popen(["fish"], stdin=PIPE, stdout=PIPE, stderr=PIPE) + out, err = p.communicate(text) + return out, err named_colors = { - 'black' : '000000', - 'red' : 'FF0000', - 'green' : '00FF00', - 'brown' : '725000', - 'yellow' : 'FFFF00', - 'blue' : '0000FF', - 'magenta' : 'FF00FF', - 'purple' : 'FF00FF', - 'cyan' : '00FFFF', - 'white' : 'FFFFFF' + 'black' : '000000', + 'red' : 'FF0000', + 'green' : '00FF00', + 'brown' : '725000', + 'yellow' : 'FFFF00', + 'blue' : '0000FF', + 'magenta' : 'FF00FF', + 'purple' : 'FF00FF', + 'cyan' : '00FFFF', + 'white' : 'FFFFFF' } def parse_one_color(comp): - """ A basic function to parse a single color value like 'FFA000' """ - if comp in named_colors: - # Named color - return named_colors[comp] - elif re.match(r"[0-9a-fA-F]{3}", comp) is not None or re.match(r"[0-9a-fA-F]{6}", comp) is not None: - # Hex color - return comp - else: - # Unknown - return '' + """ A basic function to parse a single color value like 'FFA000' """ + if comp in named_colors: + # Named color + return named_colors[comp] + elif re.match(r"[0-9a-fA-F]{3}", comp) is not None or re.match(r"[0-9a-fA-F]{6}", comp) is not None: + # Hex color + return comp + else: + # Unknown + return '' def parse_color(color_str): - """ A basic function to parse a color string, for example, 'red' '--bold' """ - comps = color_str.split(' ') - color = 'normal' - background_color = '' - bold, underline = False, False - for comp in comps: - # Remove quotes - comp = comp.strip("'\" ") - if comp == '--bold': - bold = True - elif comp == '--underline': - underline = True - elif comp.startswith('--background='): - # Background color - background_color = parse_one_color(comp[len('--background='):]) - else: - # Regular color - maybe_color = parse_one_color(comp) - if maybe_color: color = maybe_color - - return [color, background_color, bold, underline] - - + """ A basic function to parse a color string, for example, 'red' '--bold' """ + comps = color_str.split(' ') + color = 'normal' + background_color = '' + bold, underline = False, False + for comp in comps: + # Remove quotes + comp = comp.strip("'\" ") + if comp == '--bold': + bold = True + elif comp == '--underline': + underline = True + elif comp.startswith('--background='): + # Background color + background_color = parse_one_color(comp[len('--background='):]) + else: + # Regular color + maybe_color = parse_one_color(comp) + if maybe_color: color = maybe_color + + return [color, background_color, bold, underline] + + def parse_bool(val): - val = val.lower() - if val.startswith('f') or val.startswith('0'): return False - if val.startswith('t') or val.startswith('1'): return True - return bool(val) + val = val.lower() + if val.startswith('f') or val.startswith('0'): return False + if val.startswith('t') or val.startswith('1'): return True + return bool(val) class FishVar: - """ A class that represents a variable """ - def __init__(self, name, value): - self.name = name - self.value = value - self.universal = False - self.exported = False - - def get_json_obj(self): - # Return an array(3): name, value, flags - flags = [] - if self.universal: flags.append('universal') - if self.exported: flags.append('exported') - return [self.name, self.value, ', '.join(flags)] + """ A class that represents a variable """ + def __init__(self, name, value): + self.name = name + self.value = value + self.universal = False + self.exported = False + + def get_json_obj(self): + # Return an array(3): name, value, flags + flags = [] + if self.universal: flags.append('universal') + if self.exported: flags.append('exported') + return [self.name, self.value, ', '.join(flags)] class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): - - def do_get_colors(self): - "Look for fish_color_*" - result = [] - remaining = set(['normal', - 'error', - 'command', - 'end', - 'param', - 'comment', - 'match', - 'search_match', - 'operator', - 'escape', - 'quote', - 'redirection', - 'valid_path', - 'autosuggestion' - ]) - out, err = run_fish_cmd('set -L') - for line in out.split('\n'): - for match in re.finditer(r"^fish_color_(\S+) ?(.*)", line): - color_name, color_value = [x.strip() for x in match.group(1, 2)] - result.append([color_name, parse_color(color_value)]) - remaining.discard(color_name) - - # Ensure that we have all the color names we know about, so that if the - # user deletes one he can still set it again via the web interface - for x in remaining: - result.append([x, parse_color('')]) - - # Sort our result (by their keys) - result.sort() - - return result - - def do_get_functions(self): - out, err = run_fish_cmd('functions') - out = out.strip() - - # Not sure why fish sometimes returns this with newlines - if "\n" in out: - return out.split('\n') - else: - return out.strip().split(', ') - - def do_get_variable_names(self, cmd): - " Given a command like 'set -U' return all the variable names " - out, err = run_fish_cmd(cmd) - return out.split('\n') - - def do_get_variables(self): - out, err = run_fish_cmd('set -L') - - # Put all the variables into a dictionary - vars = {} - for line in out.split('\n'): - comps = line.split(' ', 1) - if len(comps) < 2: continue - fish_var = FishVar(comps[0], comps[1]) - vars[fish_var.name] = fish_var - - # Mark universal variables. L means don't abbreviate. - for name in self.do_get_variable_names('set -nUL'): - if name in vars: vars[name].universal = True - # Mark exported variables. L means don't abbreviate. - for name in self.do_get_variable_names('set -nxL'): - if name in vars: vars[name].exported = True - - return [vars[key].get_json_obj() for key in sorted(vars.keys(), key=str.lower)] - - def do_get_history(self): - # Use \x1e ("record separator") to distinguish between history items. The first - # backslash is so Python passes one backslash to fish - out, err = run_fish_cmd('for val in $history; echo -n $val \\x1e; end') - result = out.split('\x1e') - if result: result.pop() - return result - + def do_get_colors(self): + # Looks for fish_color_*. + # Returns an array of lists [color_name, color_description, color_value] + result = [] + + # Make sure we return at least these + remaining = set(['normal', + 'error', + 'command', + 'end', + 'param', + 'comment', + 'match', + 'search_match', + 'operator', + 'escape', + 'quote', + 'redirection', + 'valid_path', + 'autosuggestion' + ]) + + # Here are our color descriptions + descriptions = { + 'normal': 'Default text', + 'command': 'Ordinary commands', + 'quote': 'Text within quotes', + 'redirection': 'Like | and >', + 'end': 'Like ; and &', + 'error': 'Potential errors', + 'param': 'Command parameters', + 'comment': 'Comments start with #', + 'match': 'Matching parenthesis', + 'search_match': 'History searching', + 'history_current': 'Directory history', + 'operator': 'Like * and ~', + 'escape': 'Escapes like \\n', + 'cwd': 'Current directory', + 'cwd_root': 'cwd for root user', + 'valid_path': 'Valid paths', + 'autosuggestion': 'Suggested completion' + } + + out, err = run_fish_cmd('set -L') + for line in out.split('\n'): + for match in re.finditer(r"^fish_color_(\S+) ?(.*)", line): + color_name, color_value = [x.strip() for x in match.group(1, 2)] + color_desc = descriptions.get(color_name, '') + result.append([color_name, color_desc, parse_color(color_value)]) + remaining.discard(color_name) + + # Ensure that we have all the color names we know about, so that if the + # user deletes one he can still set it again via the web interface + for color_name in remaining: + color_desc = descriptions.get(color_name, '') + result.append([color_name, color_desc, parse_color('')]) + + # Sort our result (by their keys) + result.sort() + + return result + + def do_get_functions(self): + out, err = run_fish_cmd('functions') + out = out.strip() + + # Not sure why fish sometimes returns this with newlines + if "\n" in out: + return out.split('\n') + else: + return out.strip().split(', ') + + def do_get_variable_names(self, cmd): + " Given a command like 'set -U' return all the variable names " + out, err = run_fish_cmd(cmd) + return out.split('\n') + + def do_get_variables(self): + out, err = run_fish_cmd('set -L') + + # Put all the variables into a dictionary + vars = {} + for line in out.split('\n'): + comps = line.split(' ', 1) + if len(comps) < 2: continue + fish_var = FishVar(comps[0], comps[1]) + vars[fish_var.name] = fish_var + + # Mark universal variables. L means don't abbreviate. + for name in self.do_get_variable_names('set -nUL'): + if name in vars: vars[name].universal = True + # Mark exported variables. L means don't abbreviate. + for name in self.do_get_variable_names('set -nxL'): + if name in vars: vars[name].exported = True + + return [vars[key].get_json_obj() for key in sorted(vars.keys(), key=str.lower)] + + def do_get_history(self): + # Use \x1e ("record separator") to distinguish between history items. The first + # backslash is so Python passes one backslash to fish + out, err = run_fish_cmd('for val in $history; echo -n $val \\x1e; end') + result = out.split('\x1e') + if result: result.pop() + return result + - def do_get_color_for_variable(self, name): - "Return the color with the given name, or the empty string if there is none" - out, err = run_fish_cmd("echo -n $" + name) - return out - - def do_set_color_for_variable(self, name, color, background_color, bold, underline): - if not color: color = 'normal' - "Sets a color for a fish color name, like 'autosuggestion'" - command = 'set -U fish_color_' + name - if color: command += ' ' + color - if background_color: command += ' --background=' + background_color - if bold: command += ' --bold' - if underline: command += ' --underline' - - out, err = run_fish_cmd(command) - return out - - def do_get_function(self, func_name): - out, err = run_fish_cmd('functions ' + func_name) - return out - - def do_GET(self): - p = self.path - if p == '/colors/': - output = self.do_get_colors() - elif p == '/functions/': - output = self.do_get_functions() - elif p == '/variables/': - output = self.do_get_variables() - elif p == '/history/': - output = self.do_get_history() - elif re.match(r"/color/(\w+)/", p): - name = re.match(r"/color/(\w+)/", p).group(1) - output = self.do_get_color_for_variable(name) - else: - return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self) - - # Return valid output - self.send_response(200) - self.send_header('Content-type','text/html') - self.wfile.write('\n') - - # Output JSON - json.dump(output, self.wfile) - - def do_POST(self): - p = self.path - ctype, pdict = cgi.parse_header(self.headers.getheader('content-type')) - if ctype == 'multipart/form-data': - postvars = cgi.parse_multipart(self.rfile, pdict) - elif ctype == 'application/x-www-form-urlencoded': - length = int(self.headers.getheader('content-length')) - postvars = cgi.parse_qs(self.rfile.read(length), keep_blank_values=1) - else: - postvars = {} - - if p == '/set_color/': - what = postvars.get('what') - color = postvars.get('color') - background_color = postvars.get('background_color') - bold = postvars.get('bold') - underline = postvars.get('underline') - if what: - # Not sure why we get lists here? - output = self.do_set_color_for_variable(what[0], color[0], background_color[0], parse_bool(bold[0]), parse_bool(underline[0])) - else: - output = 'Bad request' - elif p == '/get_function/': - what = postvars.get('what') - output = [self.do_get_function(what[0])] - else: - return SimpleHTTPServer.SimpleHTTPRequestHandler.do_POST(self) - - # Return valid output - self.send_response(200) - self.send_header('Content-type','text/html') - self.wfile.write('\n') - - # Output JSON - json.dump(output, self.wfile) + def do_get_color_for_variable(self, name): + "Return the color with the given name, or the empty string if there is none" + out, err = run_fish_cmd("echo -n $" + name) + return out + + def do_set_color_for_variable(self, name, color, background_color, bold, underline): + if not color: color = 'normal' + "Sets a color for a fish color name, like 'autosuggestion'" + command = 'set -U fish_color_' + name + if color: command += ' ' + color + if background_color: command += ' --background=' + background_color + if bold: command += ' --bold' + if underline: command += ' --underline' + + out, err = run_fish_cmd(command) + return out + + def do_get_function(self, func_name): + out, err = run_fish_cmd('functions ' + func_name) + return out + + def do_GET(self): + p = self.path + if p == '/colors/': + output = self.do_get_colors() + elif p == '/functions/': + output = self.do_get_functions() + elif p == '/variables/': + output = self.do_get_variables() + elif p == '/history/': + output = self.do_get_history() + elif re.match(r"/color/(\w+)/", p): + name = re.match(r"/color/(\w+)/", p).group(1) + output = self.do_get_color_for_variable(name) + else: + return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self) + + # Return valid output + self.send_response(200) + self.send_header('Content-type','text/html') + self.wfile.write('\n') + + # Output JSON + json.dump(output, self.wfile) + + def do_POST(self): + p = self.path + ctype, pdict = cgi.parse_header(self.headers.getheader('content-type')) + if ctype == 'multipart/form-data': + postvars = cgi.parse_multipart(self.rfile, pdict) + elif ctype == 'application/x-www-form-urlencoded': + length = int(self.headers.getheader('content-length')) + postvars = cgi.parse_qs(self.rfile.read(length), keep_blank_values=1) + else: + postvars = {} + + if p == '/set_color/': + what = postvars.get('what') + color = postvars.get('color') + background_color = postvars.get('background_color') + bold = postvars.get('bold') + underline = postvars.get('underline') + if what: + # Not sure why we get lists here? + output = self.do_set_color_for_variable(what[0], color[0], background_color[0], parse_bool(bold[0]), parse_bool(underline[0])) + else: + output = 'Bad request' + elif p == '/get_function/': + what = postvars.get('what') + output = [self.do_get_function(what[0])] + else: + return SimpleHTTPServer.SimpleHTTPRequestHandler.do_POST(self) + + # Return valid output + self.send_response(200) + self.send_header('Content-type','text/html') + self.wfile.write('\n') + + # Output JSON + json.dump(output, self.wfile) - def log_request(self, code='-', size='-'): - """ Disable request logging """ - pass + def log_request(self, code='-', size='-'): + """ Disable request logging """ + pass # Make sure that the working directory is the one that contains the script server file, # because the document root is the working directory @@ -258,23 +284,23 @@ os.chdir(where) # Try to find a suitable port PORT = 8000 while PORT <= 9000: - try: - Handler = FishConfigHTTPRequestHandler - httpd = SocketServer.TCPServer(("", PORT), Handler) - # Success - break; - except socket.error: - type, value = sys.exc_info()[:2] - if 'Address already in use' not in value: - break - PORT += 1 + try: + Handler = FishConfigHTTPRequestHandler + httpd = SocketServer.TCPServer(("", PORT), Handler) + # Success + break; + except socket.error: + type, value = sys.exc_info()[:2] + if 'Address already in use' not in value: + break + PORT += 1 if PORT > 9000: - # Nobody say it - print "Unable to find an open port between 8000 and 9000" - sys.exit(-1) + # Nobody say it + print "Unable to find an open port between 8000 and 9000" + sys.exit(-1) - + url = 'http://localhost:%d' % PORT print "Web config started at '%s'. Hit enter to stop." % url @@ -283,11 +309,11 @@ webbrowser.open(url) # Select on stdin and httpd stdin_no = sys.stdin.fileno() while True: - ready_read, _, _ = select.select([sys.stdin.fileno(), httpd.fileno()], [], []) - if stdin_no in ready_read: - print "Shutting down." - # Consume the newline so it doesn't get printed by the caller - sys.stdin.readline() - break - else: - httpd.handle_request() + ready_read, _, _ = select.select([sys.stdin.fileno(), httpd.fileno()], [], []) + if stdin_no in ready_read: + print "Shutting down." + # Consume the newline so it doesn't get printed by the caller + sys.stdin.readline() + break + else: + httpd.handle_request() |