From 6b8d699ea34cf2132629e89459ae4fe67d4816f9 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Sun, 25 Oct 1992 19:18:11 +0000 Subject: [PATCH] Initial revision --- Demo/sockets/gopher.py | 347 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 347 insertions(+) create mode 100755 Demo/sockets/gopher.py diff --git a/Demo/sockets/gopher.py b/Demo/sockets/gopher.py new file mode 100755 index 00000000000..d06c78a831b --- /dev/null +++ b/Demo/sockets/gopher.py @@ -0,0 +1,347 @@ +#! /usr/local/bin/python + +# A simple gopher client. +# +# Usage: gopher [ [selector] host [port] ] + +import string +import sys +import os +import socket + +# Default selector, host and port +DEF_SELECTOR = '' +DEF_HOST = 'gopher.micro.umn.edu' +DEF_PORT = 70 + +# Recognized file types +T_TEXTFILE = '0' +T_MENU = '1' +T_CSO = '2' +T_ERROR = '3' +T_BINHEX = '4' +T_DOS = '5' +T_UUENCODE = '6' +T_SEARCH = '7' +T_TELNET = '8' +T_BINARY = '9' +T_REDUNDANT = '+' +T_SOUND = 's' + +# Dictionary mapping types to strings +typename = {'0': '', '1': '', '2': '', '3': '', \ + '4': '', '5': '', '6': '', '7': '', \ + '8': '', '9': '', '+': '', 's': ''} + +# Oft-used characters and strings +CRLF = '\r\n' +TAB = '\t' + +# Open a TCP connection to a given host and port +def open_socket(host, port): + if type(port) == type(''): + port = string.atoi(port) + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect((host, port)) + return s + +# Send a selector to a given host and port, return a file with the reply +def send_request(selector, host, port): + s = open_socket(host, port) + s.send(selector + CRLF) + s.shutdown(1) + return s.makefile('r') + +# Get a menu in the form of a list of entries +def get_menu(selector, host, port): + f = send_request(selector, host, port) + list = [] + while 1: + line = f.readline() + if not line: + print '(Unexpected EOF from server)' + break + if line[-2:] == CRLF: + line = line[:-2] + elif line[-1:] in CRLF: + line = line[:-1] + if line == '.': + break + if not line: + print '(Empty line from server)' + continue + typechar = line[0] + parts = string.splitfields(line[1:], TAB) + if len(parts) < 4: + print '(Bad line from server:', `line`, ')' + continue + if len(parts) > 4: + print '(Extra info from server:', parts[4:], ')' + parts.insert(0, typechar) + list.append(parts) + f.close() + return list + +# Get a text file as a list of lines, with trailing CRLF stripped +def get_textfile(selector, host, port): + list = [] + get_alt_textfile(selector, host, port, list.append) + return list + +# Get a text file and pass each line to a function, with trailing CRLF stripped +def get_alt_textfile(selector, host, port, func): + f = send_request(selector, host, port) + while 1: + line = f.readline() + if not line: + print '(Unexpected EOF from server)' + break + if line[-2:] == CRLF: + line = line[:-2] + elif line[-1:] in CRLF: + line = line[:-1] + if line == '.': + break + if line[:2] == '..': + line = line[1:] + func(line) + f.close() + +# Get a binary file as one solid data block +def get_binary(selector, host, port): + f = send_request(selector, host, port) + data = f.read() + f.close() + return data + +# Get a binary file and pass each block to a function +def get_alt_binary(selector, host, port, func, blocksize): + f = send_request(selector, host, port) + while 1: + data = f.read(blocksize) + if not data: + break + func(data) + +# A *very* simple interactive browser + +# Browser main command, has default arguments +def browser(*args): + selector = DEF_SELECTOR + host = DEF_HOST + port = DEF_PORT + n = len(args) + if n > 0 and args[0]: + selector = args[0] + if n > 1 and args[1]: + host = args[1] + if n > 2 and args[2]: + port = args[2] + if n > 3: + raise RuntimeError, 'too many args' + try: + browse_menu(selector, host, port) + except socket.error, msg: + print 'Socket error:', msg + sys.exit(1) + except KeyboardInterrupt: + print '\n[Goodbye]' + +# Browse a menu +def browse_menu(selector, host, port): + list = get_menu(selector, host, port) + while 1: + print '----- MENU -----' + print 'Selector:', `selector` + print 'Host:', host, ' Port:', port + print + for i in range(len(list)): + item = list[i] + typechar, description = item[0], item[1] + print string.rjust(`i+1`, 3) + ':', description, + if typename.has_key(typechar): + print typename[typechar] + else: + print '' + print + while 1: + try: + str = raw_input('Choice [CR == up a level]: ') + except EOFError: + print + return + if not str: + return + try: + choice = string.atoi(str) + except string.atoi_error: + print 'Choice must be a number; try again:' + continue + if not 0 < choice <= len(list): + print 'Choice out of range; try again:' + continue + break + item = list[choice-1] + typechar = item[0] + [i_selector, i_host, i_port] = item[2:5] + if typebrowser.has_key(typechar): + browserfunc = typebrowser[typechar] + try: + browserfunc(i_selector, i_host, i_port) + except (IOError, socket.error): + print '***', sys.exc_type, ':', sys.exc_value + else: + print 'Unsupported object type' + +# Browse a text file +def browse_textfile(selector, host, port): + x = None + try: + p = os.popen('${PAGER-more}', 'w') + x = SaveLines().init(p) + get_alt_textfile(selector, host, port, x.writeln) + except IOError, msg: + print 'IOError:', msg + if x: + x.close() + f = open_savefile() + if not f: + return + x = SaveLines().init(f) + try: + get_alt_textfile(selector, host, port, x.writeln) + print 'Done.' + except IOError, msg: + print 'IOError:', msg + x.close() + +# Browse a search index +def browse_search(selector, host, port): + while 1: + print '----- SEARCH -----' + print 'Selector:', `selector` + print 'Host:', host, ' Port:', port + print + try: + query = raw_input('Query [CR == up a level]: ') + except EOFError: + print + break + query = string.strip(query) + if not query: + break + if '\t' in query: + print 'Sorry, queries cannot contain tabs' + continue + browse_menu(selector + TAB + query, host, port) + +# "Browse" telnet-based information, i.e. open a telnet session +def browse_telnet(selector, host, port): + if selector: + print 'Log in as', `selector` + if type(port) <> type(''): + port = `port` + sts = os.system('set -x; exec telnet ' + host + ' ' + port) + if sts: + print 'Exit status:', sts + +# "Browse" a binary file, i.e. save it to a file +def browse_binary(selector, host, port): + f = open_savefile() + if not f: + return + x = SaveWithProgress().init(f) + get_alt_binary(selector, host, port, x.write, 8*1024) + x.close() + +# "Browse" a sound file, i.e. play it or save it +def browse_sound(selector, host, port): + browse_binary(selector, host, port) + +# Dictionary mapping types to browser functions +typebrowser = {'0': browse_textfile, '1': browse_menu, \ + '4': browse_binary, '5': browse_binary, '6': browse_textfile, \ + '7': browse_search, \ + '8': browse_telnet, '9': browse_binary, 's': browse_sound} + +# Class used to save lines, appending a newline to each line +class SaveLines: + def init(self, f): + self.f = f + return self + def writeln(self, line): + self.f.write(line + '\n') + def close(self): + sts = self.f.close() + if sts: + print 'Exit status:', sts + +# Class used to save data while showing progress +class SaveWithProgress: + def init(self, f): + self.f = f + return self + def write(self, data): + sys.stdout.write('#') + sys.stdout.flush() + self.f.write(data) + def close(self): + print + sts = self.f.close() + if sts: + print 'Exit status:', sts + +# Ask for and open a save file, or return None if not to save +def open_savefile(): + try: + savefile = raw_input( \ + 'Save as file [CR == don\'t save; |pipeline or ~user/... OK]: ') + except EOFError: + print + return None + savefile = string.strip(savefile) + if not savefile: + return None + if savefile[0] == '|': + cmd = string.strip(savefile[1:]) + try: + p = os.popen(cmd, 'w') + except IOError, msg: + print `cmd`, ':', msg + return None + print 'Piping through', `cmd`, '...' + return p + if savefile[0] == '~': + savefile = os.path.expanduser(savefile) + try: + f = open(savefile, 'w') + except IOError, msg: + print `savefile`, ':', msg + return None + print 'Saving to', `savefile`, '...' + return f + +# Test program +def test(): + if sys.argv[4:]: + print 'usage: gopher [ [selector] host [port] ]' + sys.exit(2) + elif sys.argv[3:]: + browser(sys.argv[1], sys.argv[2], sys.argv[3]) + elif sys.argv[2:]: + try: + port = string.atoi(sys.argv[2]) + selector = '' + host = sys.argv[1] + except string.atoi_error: + selector = sys.argv[1] + host = sys.argv[2] + port = '' + browser(selector, host, port) + elif sys.argv[1:]: + browser('', sys.argv[1]) + else: + browser() + +# Call the test program as a main program +test()