Mercurial > cpython-lru_cache-weakref
annotate Demo/sockets/gopher.py @ 5817:48b94de85bf3 legacy-trunk
Extensive changes by AMK.
| author | Guido van Rossum <guido@python.org> |
|---|---|
| date | Thu, 17 Jul 1997 16:29:42 +0000 |
| parents | 73758db2f464 |
| children | 71a294bd7950 |
| rev | line source |
|---|---|
|
4573
73758db2f464
/usr/local/bin/python -> /usr/bin/env python
Guido van Rossum <guido@python.org>
parents:
1429
diff
changeset
|
1 #! /usr/bin/env python |
| 1080 | 2 |
| 3 # A simple gopher client. | |
| 4 # | |
| 5 # Usage: gopher [ [selector] host [port] ] | |
| 6 | |
| 7 import string | |
| 8 import sys | |
| 9 import os | |
| 10 import socket | |
| 11 | |
| 12 # Default selector, host and port | |
| 13 DEF_SELECTOR = '' | |
| 14 DEF_HOST = 'gopher.micro.umn.edu' | |
| 15 DEF_PORT = 70 | |
| 16 | |
| 17 # Recognized file types | |
| 18 T_TEXTFILE = '0' | |
| 19 T_MENU = '1' | |
| 20 T_CSO = '2' | |
| 21 T_ERROR = '3' | |
| 22 T_BINHEX = '4' | |
| 23 T_DOS = '5' | |
| 24 T_UUENCODE = '6' | |
| 25 T_SEARCH = '7' | |
| 26 T_TELNET = '8' | |
| 27 T_BINARY = '9' | |
| 28 T_REDUNDANT = '+' | |
| 29 T_SOUND = 's' | |
| 30 | |
| 31 # Dictionary mapping types to strings | |
| 32 typename = {'0': '<TEXT>', '1': '<DIR>', '2': '<CSO>', '3': '<ERROR>', \ | |
| 33 '4': '<BINHEX>', '5': '<DOS>', '6': '<UUENCODE>', '7': '<SEARCH>', \ | |
| 34 '8': '<TELNET>', '9': '<BINARY>', '+': '<REDUNDANT>', 's': '<SOUND>'} | |
| 35 | |
| 36 # Oft-used characters and strings | |
| 37 CRLF = '\r\n' | |
| 38 TAB = '\t' | |
| 39 | |
| 40 # Open a TCP connection to a given host and port | |
| 41 def open_socket(host, port): | |
| 1092 | 42 if not port: |
| 43 port = DEF_PORT | |
| 44 elif type(port) == type(''): | |
| 1080 | 45 port = string.atoi(port) |
| 46 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
| 47 s.connect((host, port)) | |
| 48 return s | |
| 49 | |
| 50 # Send a selector to a given host and port, return a file with the reply | |
| 51 def send_request(selector, host, port): | |
| 52 s = open_socket(host, port) | |
| 53 s.send(selector + CRLF) | |
| 54 s.shutdown(1) | |
| 55 return s.makefile('r') | |
| 56 | |
| 57 # Get a menu in the form of a list of entries | |
| 58 def get_menu(selector, host, port): | |
| 59 f = send_request(selector, host, port) | |
| 60 list = [] | |
| 61 while 1: | |
| 62 line = f.readline() | |
| 63 if not line: | |
| 64 print '(Unexpected EOF from server)' | |
| 65 break | |
| 66 if line[-2:] == CRLF: | |
| 67 line = line[:-2] | |
| 68 elif line[-1:] in CRLF: | |
| 69 line = line[:-1] | |
| 70 if line == '.': | |
| 71 break | |
| 72 if not line: | |
| 73 print '(Empty line from server)' | |
| 74 continue | |
| 75 typechar = line[0] | |
| 76 parts = string.splitfields(line[1:], TAB) | |
| 77 if len(parts) < 4: | |
| 78 print '(Bad line from server:', `line`, ')' | |
| 79 continue | |
| 80 if len(parts) > 4: | |
| 81 print '(Extra info from server:', parts[4:], ')' | |
| 82 parts.insert(0, typechar) | |
| 83 list.append(parts) | |
| 84 f.close() | |
| 85 return list | |
| 86 | |
| 87 # Get a text file as a list of lines, with trailing CRLF stripped | |
| 88 def get_textfile(selector, host, port): | |
| 89 list = [] | |
| 90 get_alt_textfile(selector, host, port, list.append) | |
| 91 return list | |
| 92 | |
| 93 # Get a text file and pass each line to a function, with trailing CRLF stripped | |
| 94 def get_alt_textfile(selector, host, port, func): | |
| 95 f = send_request(selector, host, port) | |
| 96 while 1: | |
| 97 line = f.readline() | |
| 98 if not line: | |
| 99 print '(Unexpected EOF from server)' | |
| 100 break | |
| 101 if line[-2:] == CRLF: | |
| 102 line = line[:-2] | |
| 103 elif line[-1:] in CRLF: | |
| 104 line = line[:-1] | |
| 105 if line == '.': | |
| 106 break | |
| 107 if line[:2] == '..': | |
| 108 line = line[1:] | |
| 109 func(line) | |
| 110 f.close() | |
| 111 | |
| 112 # Get a binary file as one solid data block | |
| 113 def get_binary(selector, host, port): | |
| 114 f = send_request(selector, host, port) | |
| 115 data = f.read() | |
| 116 f.close() | |
| 117 return data | |
| 118 | |
| 119 # Get a binary file and pass each block to a function | |
| 120 def get_alt_binary(selector, host, port, func, blocksize): | |
| 121 f = send_request(selector, host, port) | |
| 122 while 1: | |
| 123 data = f.read(blocksize) | |
| 124 if not data: | |
| 125 break | |
| 126 func(data) | |
| 127 | |
| 128 # A *very* simple interactive browser | |
| 129 | |
| 130 # Browser main command, has default arguments | |
| 131 def browser(*args): | |
| 132 selector = DEF_SELECTOR | |
| 133 host = DEF_HOST | |
| 134 port = DEF_PORT | |
| 135 n = len(args) | |
| 136 if n > 0 and args[0]: | |
| 137 selector = args[0] | |
| 138 if n > 1 and args[1]: | |
| 139 host = args[1] | |
| 140 if n > 2 and args[2]: | |
| 141 port = args[2] | |
| 142 if n > 3: | |
| 143 raise RuntimeError, 'too many args' | |
| 144 try: | |
| 145 browse_menu(selector, host, port) | |
| 146 except socket.error, msg: | |
| 147 print 'Socket error:', msg | |
| 148 sys.exit(1) | |
| 149 except KeyboardInterrupt: | |
| 150 print '\n[Goodbye]' | |
| 151 | |
| 152 # Browse a menu | |
| 153 def browse_menu(selector, host, port): | |
| 154 list = get_menu(selector, host, port) | |
| 155 while 1: | |
| 156 print '----- MENU -----' | |
| 157 print 'Selector:', `selector` | |
| 158 print 'Host:', host, ' Port:', port | |
| 159 print | |
| 160 for i in range(len(list)): | |
| 161 item = list[i] | |
| 162 typechar, description = item[0], item[1] | |
| 163 print string.rjust(`i+1`, 3) + ':', description, | |
| 164 if typename.has_key(typechar): | |
| 165 print typename[typechar] | |
| 166 else: | |
| 167 print '<TYPE=' + `typechar` + '>' | |
| 168 print | |
| 169 while 1: | |
| 170 try: | |
| 171 str = raw_input('Choice [CR == up a level]: ') | |
| 172 except EOFError: | |
| 173 print | |
| 174 return | |
| 175 if not str: | |
| 176 return | |
| 177 try: | |
| 178 choice = string.atoi(str) | |
| 179 except string.atoi_error: | |
| 180 print 'Choice must be a number; try again:' | |
| 181 continue | |
| 182 if not 0 < choice <= len(list): | |
| 183 print 'Choice out of range; try again:' | |
| 184 continue | |
| 185 break | |
| 186 item = list[choice-1] | |
| 187 typechar = item[0] | |
| 188 [i_selector, i_host, i_port] = item[2:5] | |
| 189 if typebrowser.has_key(typechar): | |
| 190 browserfunc = typebrowser[typechar] | |
| 191 try: | |
| 192 browserfunc(i_selector, i_host, i_port) | |
| 193 except (IOError, socket.error): | |
| 194 print '***', sys.exc_type, ':', sys.exc_value | |
| 195 else: | |
| 196 print 'Unsupported object type' | |
| 197 | |
| 198 # Browse a text file | |
| 199 def browse_textfile(selector, host, port): | |
| 200 x = None | |
| 201 try: | |
| 202 p = os.popen('${PAGER-more}', 'w') | |
|
1429
ada56b0765c1
*** empty log message ***
Guido van Rossum <guido@python.org>
parents:
1092
diff
changeset
|
203 x = SaveLines(p) |
| 1080 | 204 get_alt_textfile(selector, host, port, x.writeln) |
| 205 except IOError, msg: | |
| 206 print 'IOError:', msg | |
| 207 if x: | |
| 208 x.close() | |
| 209 f = open_savefile() | |
| 210 if not f: | |
| 211 return | |
|
1429
ada56b0765c1
*** empty log message ***
Guido van Rossum <guido@python.org>
parents:
1092
diff
changeset
|
212 x = SaveLines(f) |
| 1080 | 213 try: |
| 214 get_alt_textfile(selector, host, port, x.writeln) | |
| 215 print 'Done.' | |
| 216 except IOError, msg: | |
| 217 print 'IOError:', msg | |
| 218 x.close() | |
| 219 | |
| 220 # Browse a search index | |
| 221 def browse_search(selector, host, port): | |
| 222 while 1: | |
| 223 print '----- SEARCH -----' | |
| 224 print 'Selector:', `selector` | |
| 225 print 'Host:', host, ' Port:', port | |
| 226 print | |
| 227 try: | |
| 228 query = raw_input('Query [CR == up a level]: ') | |
| 229 except EOFError: | |
| 230 print | |
| 231 break | |
| 232 query = string.strip(query) | |
| 233 if not query: | |
| 234 break | |
| 235 if '\t' in query: | |
| 236 print 'Sorry, queries cannot contain tabs' | |
| 237 continue | |
| 238 browse_menu(selector + TAB + query, host, port) | |
| 239 | |
| 240 # "Browse" telnet-based information, i.e. open a telnet session | |
| 241 def browse_telnet(selector, host, port): | |
| 242 if selector: | |
| 243 print 'Log in as', `selector` | |
| 244 if type(port) <> type(''): | |
| 245 port = `port` | |
| 246 sts = os.system('set -x; exec telnet ' + host + ' ' + port) | |
| 247 if sts: | |
| 248 print 'Exit status:', sts | |
| 249 | |
| 250 # "Browse" a binary file, i.e. save it to a file | |
| 251 def browse_binary(selector, host, port): | |
| 252 f = open_savefile() | |
| 253 if not f: | |
| 254 return | |
|
1429
ada56b0765c1
*** empty log message ***
Guido van Rossum <guido@python.org>
parents:
1092
diff
changeset
|
255 x = SaveWithProgress(f) |
| 1080 | 256 get_alt_binary(selector, host, port, x.write, 8*1024) |
| 257 x.close() | |
| 258 | |
| 259 # "Browse" a sound file, i.e. play it or save it | |
| 260 def browse_sound(selector, host, port): | |
| 261 browse_binary(selector, host, port) | |
| 262 | |
| 263 # Dictionary mapping types to browser functions | |
| 264 typebrowser = {'0': browse_textfile, '1': browse_menu, \ | |
| 265 '4': browse_binary, '5': browse_binary, '6': browse_textfile, \ | |
| 266 '7': browse_search, \ | |
| 267 '8': browse_telnet, '9': browse_binary, 's': browse_sound} | |
| 268 | |
| 269 # Class used to save lines, appending a newline to each line | |
| 270 class SaveLines: | |
|
1429
ada56b0765c1
*** empty log message ***
Guido van Rossum <guido@python.org>
parents:
1092
diff
changeset
|
271 def __init__(self, f): |
| 1080 | 272 self.f = f |
| 273 def writeln(self, line): | |
| 274 self.f.write(line + '\n') | |
| 275 def close(self): | |
| 276 sts = self.f.close() | |
| 277 if sts: | |
| 278 print 'Exit status:', sts | |
| 279 | |
| 280 # Class used to save data while showing progress | |
| 281 class SaveWithProgress: | |
|
1429
ada56b0765c1
*** empty log message ***
Guido van Rossum <guido@python.org>
parents:
1092
diff
changeset
|
282 def __init__(self, f): |
| 1080 | 283 self.f = f |
| 284 def write(self, data): | |
| 285 sys.stdout.write('#') | |
| 286 sys.stdout.flush() | |
| 287 self.f.write(data) | |
| 288 def close(self): | |
| 289 print | |
| 290 sts = self.f.close() | |
| 291 if sts: | |
| 292 print 'Exit status:', sts | |
| 293 | |
| 294 # Ask for and open a save file, or return None if not to save | |
| 295 def open_savefile(): | |
| 296 try: | |
| 297 savefile = raw_input( \ | |
| 298 'Save as file [CR == don\'t save; |pipeline or ~user/... OK]: ') | |
| 299 except EOFError: | |
| 300 print | |
| 301 return None | |
| 302 savefile = string.strip(savefile) | |
| 303 if not savefile: | |
| 304 return None | |
| 305 if savefile[0] == '|': | |
| 306 cmd = string.strip(savefile[1:]) | |
| 307 try: | |
| 308 p = os.popen(cmd, 'w') | |
| 309 except IOError, msg: | |
| 310 print `cmd`, ':', msg | |
| 311 return None | |
| 312 print 'Piping through', `cmd`, '...' | |
| 313 return p | |
| 314 if savefile[0] == '~': | |
| 315 savefile = os.path.expanduser(savefile) | |
| 316 try: | |
| 317 f = open(savefile, 'w') | |
| 318 except IOError, msg: | |
| 319 print `savefile`, ':', msg | |
| 320 return None | |
| 321 print 'Saving to', `savefile`, '...' | |
| 322 return f | |
| 323 | |
| 324 # Test program | |
| 325 def test(): | |
| 326 if sys.argv[4:]: | |
| 327 print 'usage: gopher [ [selector] host [port] ]' | |
| 328 sys.exit(2) | |
| 329 elif sys.argv[3:]: | |
| 330 browser(sys.argv[1], sys.argv[2], sys.argv[3]) | |
| 331 elif sys.argv[2:]: | |
| 332 try: | |
| 333 port = string.atoi(sys.argv[2]) | |
| 334 selector = '' | |
| 335 host = sys.argv[1] | |
| 336 except string.atoi_error: | |
| 337 selector = sys.argv[1] | |
| 338 host = sys.argv[2] | |
| 339 port = '' | |
| 340 browser(selector, host, port) | |
| 341 elif sys.argv[1:]: | |
| 342 browser('', sys.argv[1]) | |
| 343 else: | |
| 344 browser() | |
| 345 | |
| 346 # Call the test program as a main program | |
| 347 test() |
