Compare commits

...

56 Commits

Author SHA1 Message Date
Kelvin Sherlock 103048b2db Merge branch 'bug1' 2013-09-24 22:48:17 -04:00
Kelvin Sherlock 9457120327 read_binary_size return value on read error 2013-09-24 22:47:08 -04:00
Kelvin Sherlock fad3fc48ea read_binary_size return value 2013-09-24 22:45:23 -04:00
Kelvin Sherlock 09ebb6c842 debug names/lint 2013-09-24 22:44:55 -04:00
Kelvin Sherlock aa6bcc05d3 updated mime functions (match prototypes) 2013-09-24 22:43:43 -04:00
Kelvin Sherlock b7e90885aa add verbose logging for the connection initiation. 2013-08-11 16:41:26 -04:00
Kelvin Sherlock cd55a2de3b replace flags with options 2013-08-11 13:55:41 -04:00
Kelvin Sherlock 58df66cc71 new options 2013-08-11 12:51:58 -04:00
Kelvin Sherlock 86f0933ec7 new options generator 2013-08-11 12:51:45 -04:00
Kelvin Sherlock 4e96ff9651 compiler fixes 2013-08-11 00:42:57 -04:00
Kelvin Sherlock b8cc6ee43a parse_extension/parse_extension_c 2013-08-11 00:42:42 -04:00
Kelvin Sherlock 6c0594c255 no message 2013-08-11 00:41:53 -04:00
Kelvin Sherlock 0497c1d4dd sweet 16 3.0 debug cops no longer include the line feed. 2013-08-11 00:41:38 -04:00
Kelvin Sherlock 8f0eb50d12 smb scheme smb://.... 2013-08-10 23:53:38 -04:00
Kelvin Sherlock fff9954b70 mimetype/extension updates 2013-08-10 20:25:15 -04:00
Kelvin Sherlock ed6c2fc7f7 makefile is text 2013-08-10 20:20:42 -04:00
Kelvin Sherlock f67e5f11a8 common code, http chunked responses. 2012-09-10 20:50:51 -04:00
Kelvin Sherlock 9f570ffc82 fix return value 2012-09-10 20:50:09 -04:00
Kelvin Sherlock 394aa110c7 add dict protocol 2012-09-10 19:23:02 -04:00
Kelvin Sherlock d1436d45df getopt replacement. 2012-09-10 19:13:49 -04:00
Kelvin Sherlock ab26d38790 cache memory ID 2012-08-26 17:54:18 -04:00
Kelvin Sherlock 1d8bed53a7 add time tool dependency (for later use) 2012-08-26 17:52:27 -04:00
Kelvin Sherlock 8b771de8d2 debug code 2012-08-26 15:52:43 -04:00
Kelvin Sherlock e7af668c67 chunked support 2012-08-26 15:52:32 -04:00
Kelvin Sherlock caa54a0f3e inc busy /dec busy code. 2012-08-26 15:51:46 -04:00
Kelvin Sherlock 7d31bafffc .asm files are text 2012-08-26 15:50:34 -04:00
Kelvin Sherlock b7420ca623 parse http headers, handle content-length 2012-05-02 19:19:28 -04:00
Kelvin Sherlock a8779193d6 add git ignore file 2012-05-02 19:05:21 -04:00
Kelvin Sherlock 0ad36adf5e http response parsing 2012-04-29 22:46:58 -04:00
Kelvin Sherlock ff655c687e update makefile 2012-04-29 22:46:43 -04:00
Kelvin Sherlock 3c860adce9 clean up white space 2012-04-29 22:46:36 -04:00
Kelvin Sherlock 3f65a4ed2e http parsing 2012-04-29 22:46:22 -04:00
Kelvin Sherlock b17f0fa448 move argument parsing to flags.c 2012-04-29 22:46:11 -04:00
Kelvin Sherlock 29d7dd0181 .mk (makefile) is text. 2012-04-28 14:04:15 -04:00
Kelvin Sherlock f92f901742 update dependencies. 2012-04-28 14:04:04 -04:00
Kelvin Sherlock 2b310a2161 more flags 2012-04-28 14:03:55 -04:00
Kelvin Sherlock 7287f069ba fix flags 2012-04-28 00:58:22 -04:00
Kelvin Sherlock 681fb057ba fix bugs 2012-04-28 00:58:13 -04:00
Kelvin Sherlock bc5ac50f5e http 2012-04-28 00:36:03 -04:00
Kelvin Sherlock 62754f7431 outfile parsing. 2012-04-28 00:35:51 -04:00
Kelvin Sherlock 2903af5f60 update makefile 2012-04-28 00:35:36 -04:00
Kelvin Sherlock 67ed428493 merge http code 2012-04-28 00:35:28 -04:00
Kelvin Sherlock 933308432d flags, move file code to do_gopher() 2012-04-27 23:36:25 -04:00
Kelvin Sherlock f4c23d7d87 updated makefile. 2012-04-27 21:19:56 -04:00
Kelvin Sherlock 5abb3cae53 use common code for gopher read + connect 2012-04-27 21:19:38 -04:00
Kelvin Sherlock aabed2e462 update url guard 2012-04-27 21:19:10 -04:00
Kelvin Sherlock f4d9a6f8d4 http code 2012-04-27 21:19:00 -04:00
Kelvin Sherlock 86b60c74e9 common code/prototypes 2012-04-27 21:18:46 -04:00
Kelvin Sherlock 20ad89176f noroot/optimize 2012-04-27 21:18:31 -04:00
Kelvin Sherlock 4dc81d82af update makefile 2012-04-24 23:55:24 -04:00
Kelvin Sherlock c5253c6ce3 url c malloc 2012-04-24 23:55:18 -04:00
Kelvin Sherlock 39bc3dde14 sweet 16 debug code 2012-04-24 23:55:06 -04:00
Kelvin Sherlock a2fcb6ee11 better check for unable to connect. 2012-04-24 23:54:46 -04:00
Kelvin Sherlock 9b06d877b1 split gopher code 2012-04-24 23:54:12 -04:00
Kelvin Sherlock afe71ebe02 url - path+query component. 2012-04-19 20:45:35 -04:00
Kelvin Sherlock 9aca19b10f 3.0b3, not 3.0b5 2012-04-16 22:20:33 -04:00
42 changed files with 3637 additions and 444 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.DS_Store

View File

@ -1 +1 @@
Gopher requires GNO (2.0.4, perhaps 2.0.6) and Marinetti (3.0b5). Usage: gopher [-o filename] [-O] gopher-url -o filename: save as filename -O: save as the remote filename gopher urls look like: gopher://server:port/path where :port is optional. The first character of path is actually the type code (5 and 9 are binary, others are text). A blank path is assumed to be a directory listing but could be anything.
Gopher requires GNO (2.0.4, perhaps 2.0.6) and Marinetti (3.0b3). Usage: gopher [-o filename] [-O] gopher-url -o filename: save as filename -O: save as the remote filename gopher urls look like: gopher://server:port/path where :port is optional. The first character of path is actually the type code (5 and 9 are binary, others are text). A blank path is assumed to be a directory listing but could be anything.

View File

@ -4,6 +4,13 @@
* orca/merlin compatability
* gopher -I http://yahoo.com ; gopher -I http://someplace else
- the second invocation sometimes returns the results of the first.
- seems to be an issue with the DNS lookup as it is, in fact, connecting
to the wrong address.
- gopher -i http://... crashed, possibly in setfileattr (which should have even been called)
----
fixed

165
common.c Normal file
View File

@ -0,0 +1,165 @@
/*
*
* common routines.
*/
#pragma noroot
#pragma optimize 79
#pragma debug 0x8000
#pragma lint -1
#include <Memory.h>
#include <MiscTool.h>
#include <tcpip.h>
#include <stdio.h>
#include "options.h"
#include "prototypes.h"
#include "connection.h"
int read_binary(Word ipid, FILE *file, ReadBlock *dcb)
{
Word rv = 0;
if (dcb) dcb->transferCount = 0;
IncBusy();
TCPIPPoll();
DecBusy();
for(;;)
{
static char buffer[512];
rrBuff rb;
Word count;
Word tcount;
IncBusy();
rv = TCPIPReadTCP(ipid, 0, (Ref)buffer, 512, &rb);
DecBusy();
count = rb.rrBuffCount;
if (!count)
{
if (rv) break;
IncBusy();
TCPIPPoll();
DecBusy();
continue;
}
tcount = fwrite(buffer, 1, count, file);
if (dcb) dcb->transferCount += tcount;
if (tcount != count) return -1;
}
return 0;
}
// ReadTCP will only return if the entire request is fulfilled or the connection closes,
// so it could just do that. For a large request, this is probably nicer.
int read_binary_size(Word ipid, FILE *file, ReadBlock *dcb)
{
Word rv = 0;
LongWord size;
if (!dcb) return -1;
dcb->transferCount = 0;
size = dcb->requestCount;
if (!size) return 0;
IncBusy();
TCPIPPoll();
DecBusy();
for(;;)
{
static char buffer[512];
rrBuff rb;
Word count;
Word tcount;
count = 512;
if (count > size) count = size;
IncBusy();
rv = TCPIPReadTCP(ipid, 0, (Ref)buffer, count, &rb);
DecBusy();
count = rb.rrBuffCount;
if (!count)
{
if (rv) return -1;
IncBusy();
TCPIPPoll();
DecBusy();
continue;
}
tcount = fwrite(buffer, 1, count, file);
dcb->transferCount += tcount;
size -= tcount;
if (tcount != count) return -1;
if (!size) return 0;
}
return 0;
}
int ConnectLoop(char *host, Word port, Connection *connection)
{
LongWord qtick;
ConnectionInit(connection, MMStartUp(), flags._v ? DisplayCallback : NULL);
ConnectionOpenC(connection, host, port);
// 30 second timeout.
qtick = GetTick() + 30 * 60;
while (!ConnectionPoll(connection))
{
if (GetTick() >= qtick)
{
fprintf(stderr, "Connection timed out.\n");
IncBusy();
TCPIPAbortTCP(connection->ipid);
TCPIPLogout(connection->ipid);
DecBusy();
return 0;
}
}
if (connection->state != kConnectionStateConnected)
{
fprintf(stderr, "Unable to open host: %s:%u\n",
host,
port);
return 0;
}
return 1;
}
int CloseLoop(Connection *connection)
{
ConnectionClose(connection);
while (!ConnectionPoll(connection)) ; // wait for it to close.
return 1;
}

View File

@ -1,7 +1,13 @@
#pragma optimize 79
#pragma noroot
#include <IntMath.h>
#include "Memory.h"
#include "connection.h"
#include <string.h>
#include "s16debug.h"
static char pstring[256];
@ -11,6 +17,27 @@ static Word LoginAndOpen(Connection *buffer)
Word ipid;
Word terr;
if (buffer->displayPtr)
{
static char message[] = "\pConnecting to xxx.xxx.xxx.xxx:xxxxx";
Word length;
Word tmp;
length = 15;
// first the ip addresss...
tmp = TCPIPConvertIPToCASCII(buffer->dnr.DNRIPaddress, message + length, 0);
length += tmp;
message[length++] = ':';
// now the port...
Int2Dec(buffer->port, message + length, 5, 0);
length += 5;
message[length] = 0;
message[0] = length;
buffer->displayPtr(message);
}
ipid = TCPIPLogin(
buffer->memID,
buffer->dnr.DNRIPaddress,
@ -42,6 +69,7 @@ static Word LoginAndOpen(Connection *buffer)
Word ConnectionPoll(Connection *buffer)
{
Word state;
if (!buffer) return -1;
state = buffer->state;
@ -62,6 +90,12 @@ Word ConnectionPoll(Connection *buffer)
else if (buffer->dnr.DNRstatus != DNR_Pending)
{
buffer->state = kConnectionStateError;
if (buffer->displayPtr)
{
static char message[] = "\pDNR lookup failed: $xxxx";
Int2Hex(buffer->dnr.DNRstatus, message + 21, 4);
buffer->displayPtr(message);
}
return -1;
}
}
@ -84,6 +118,9 @@ Word ConnectionPoll(Connection *buffer)
{
//CloseAndLogout(buffer);
s16_debug_printf("terr = %04x tool error = %04x\n", terr, _toolErr);
s16_debug_srbuff(&sr);
TCPIPCloseTCP(buffer->ipid);
TCPIPLogout(buffer->ipid);
buffer->ipid = 0;
@ -100,6 +137,9 @@ Word ConnectionPoll(Connection *buffer)
if (sr.srState == TCPSCLOSED || sr.srState == TCPSTIMEWAIT)
{
s16_debug_srbuff(&sr);
TCPIPLogout(buffer->ipid);
buffer->ipid = 0;
buffer->state = kConnectionStateDisconnected;
@ -143,7 +183,7 @@ Word ConnectionOpen(Connection *buffer, const char *host, Word port)
buffer->terr = 0;
buffer->port = port;
if (!buffer || !*buffer) return -1;
if (!buffer || !*buffer || !host || !*host) return -1;
// 1. check if we need to do DNR.
if (TCPIPValidateIPString(host))
@ -156,17 +196,31 @@ Word ConnectionOpen(Connection *buffer, const char *host, Word port)
return LoginAndOpen(buffer);
}
// do dnr.
if (buffer->displayPtr)
{
static char message[256] = "\pDNR lookup: ";
BlockMove(host + 1, message + 13, host[0]);
message[0] = 13 + host[0];
buffer->displayPtr(message);
}
TCPIPDNRNameToIP(host, &buffer->dnr);
if (_toolErr)
{
buffer->state = kConnectionStateError;
if (buffer->displayPtr)
{
static char message[] = "\pDNR lookup tool error: $xxxx";
Int2Hex(_toolErr, message + 25, 4);
buffer->displayPtr(message);
}
return -1;
}
buffer->state = kConnectionStateDNR;
return 0;
}
void ConnectionInit(Connection *buffer, Word memID)
void ConnectionInit(Connection *buffer, Word memID, ConnectionCallback displayPtr)
{
buffer->memID = memID;
buffer->ipid = 0;
@ -175,6 +229,7 @@ void ConnectionInit(Connection *buffer, Word memID)
buffer->port = 0;
buffer->dnr.DNRstatus = 0;
buffer->dnr.DNRIPaddress = 0;
buffer->displayPtr = displayPtr;
}
Word ConnectionClose(Connection *buffer)
@ -186,6 +241,13 @@ Word ConnectionClose(Connection *buffer)
{
buffer->state = kConnectionStateDisconnecting;
buffer->terr = TCPIPCloseTCP(buffer->ipid);
if (buffer->displayPtr)
{
static char message[] = "\pClosing connection: $0000";
Int2Hex(buffer->terr, message + 22, 4);
buffer->displayPtr(message);
}
return 0;
}
@ -193,6 +255,12 @@ Word ConnectionClose(Connection *buffer)
{
TCPIPCancelDNR(&buffer->dnr);
buffer->state = 0;
if (buffer->displayPtr)
{
static char message[] = "\pDNR lookup canceled";
buffer->displayPtr(message);
}
return 1;
}

View File

@ -1,5 +1,5 @@
#ifndef __CONNECT_H__
#define __CONNECT_H__
#ifndef __CONNECTION_H__
#define __CONNECTION_H__
#ifndef __TCPIP__
#include <tcpip.h>
@ -14,6 +14,7 @@ enum {
kConnectionStateError
};
typedef void (*ConnectionCallback)(const char *message);
typedef struct Connection {
Word memID;
Word ipid;
@ -21,11 +22,12 @@ typedef struct Connection {
Word state;
dnrBuffer dnr;
Word port;
ConnectionCallback displayPtr;
} Connection;
void ConnectionInit(Connection *, Word memID);
void ConnectionInit(Connection *, Word memID, ConnectionCallback displayPtr);
Word ConnectionOpen(Connection *, const char *host, Word port);
Word ConnectionOpenC(Connection *, const char *host, Word port);
@ -35,5 +37,4 @@ Word ConnectionClose(Connection *);
Word ConnectionPoll(Connection *);
#endif

3
data.c
View File

@ -1,3 +1,6 @@
#pragma optimize 79
#pragma noroot
#include "Data.h"
#include <Memory.h>
#include "fast.memory.h"

View File

@ -1,3 +1,8 @@
#pragma optimize 79
#pragma noroot
#pragma debug 0x8000
#pragma lint -1
#include "dictionary.h"
#include <memory.h>
#include <string.h>

View File

@ -24,9 +24,6 @@ Word DictionaryAdd(Handle h, const void *key, Word keySize,
// removal performance is awful.
void DictionaryRemove(Handle h, const char *key, Word keySize);
Word DictionaryEnumerate(Handle h, DictionaryEnumerator *e, Word cookie);
#endif

133
flags.c Normal file
View File

@ -0,0 +1,133 @@
#ifdef __ORCAC__
#pragma optimize 79
#pragma noroot
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "flags.h"
// like getopt.
Flags flags;
void help(void)
{
fputs("gopher [options] url\n", stdout);
fputs("-h display help information.\n", stdout);
fputs("-V display version information.\n", stdout);
fputs("-i display http headers.\n", stdout);
fputs("-I http HEAD\n", stdout);
fputs("-O write output to file.\n", stdout);
fputs("-o <file> write output to <file> instead of stdout.\n", stdout);
fputs("-0 use HTTP 1.0\n", stdout);
fputs("-9 use HTTP 0.9\n", stdout);
fputs("\n", stdout);
exit(0);
}
int ParseFlags(int argc, char **argv)
{
char *cp;
char c;
int i;
int j;
memset(&flags, 0, sizeof(flags));
for (i = 1; i < argc; ++i)
{
cp = argv[i];
c = cp[0];
if (c != '-')
return i;
// -- = end of options.
if (cp[1] == '-' && cp[2] == 0)
return i + 1;
// now scan all the flags in the string...
for (j = 1; ; ++j)
{
int skip = 0;
c = cp[j];
if (c == 0) break;
switch (c)
{
case 'h':
help();
break;
case 'i':
flags._i = 1;
break;
case 'I':
flags._I = 1;
break;
case 'O':
flags._O = 1;
flags._o = 0;
break;
case 'v':
flags._v = 1;
break;
case 'V':
flags._V = 1;
break;
case '0':
flags._0 = 1;
flags._9 = 0;
break;
case '9':
flags._9 = 1;
flags._0 = 0;
break;
// take an argument...
// -oarg or -o arg
case 'o':
flags._O = 0;
skip = 1;
if (cp[j + 1])
flags._o = cp + j + 1;
else
{
if (++i >= argc)
{
fprintf(stderr, "option requires an argument -- %c\n", c);
return -1;
}
flags._o = argv[i];
}
break;
default:
fprintf(stderr, "illegal option -- %c\n", c);
return -1;
}
if (skip) break;
}
}
return i;
}

24
flags.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef __flags_h__
#define __flags_h__
typedef struct Flags {
int _0:1; // -1 (use http 1.0)
int _9:1; // -9 (use http 0.9)
int _i:1; // -i (include http headers)
int _I:1; // -I (http HEAD command)
int _O:1; // -O (file name from url)
int _v:1; // -v (verbose)
int _V:1;
char *_o;
} Flags;
extern struct Flags flags;
int ParseFlags(int argc, char **argv);
void help(void);
#endif

313
flags.rb Normal file
View File

@ -0,0 +1,313 @@
#!/usr/bin/env ruby -w
# process the flags.yaml file
# and generate a flags.h and flags.c file.
#
#
# todo -- support for long-options (--longoption, --longoption=value, etc)
#
#
require 'erb'
require 'yaml'
header_preamble = <<EOF
#ifndef __flags_h__
#define __flags_h__
typedef struct Flags {
EOF
header_postamble = <<EOF
} Flags;
extern struct Flags flags;
int FlagsParse(int argc, char **argv);
void FlagsHelp(void);
#endif
EOF
class Option
@@map = {
# some of these are a bad idea but whatever...
'>' => 'gt',
'<' => 'lt',
',' => 'comma',
'.' => 'period',
'/' => 'forward_slash',
'\\' => 'back_slash',
'?' => 'question',
'|' => 'pipe',
'~' => 'tilde',
'`' => 'grave',
'!' => 'bang',
'@' => 'at',
'#' => 'hash',
'$' => 'dollar',
'%' => 'percent',
'^' => 'caret',
'&' => 'ampersand',
'*' => 'star',
'(' => 'left_paren',
')' => 'right_paren',
'-' => 'minus',
'+' => 'plus',
'=' => 'equal',
'[' => 'left_bracket',
']' => 'right_bracket',
'{' => 'left_brace',
'}' => 'right_brace',
':' => 'colon',
';' => 'semi_colon',
'\'' => 'apostrophe',
'"' => 'quote'
}
def initialize(hash)
@char = hash['char'].to_s
@argument = hash['argument'] || false
@flag_name = hash['flag_name']
@flag_name = @flag_name.to_s if @flag_name
@xor = hash['xor'] || []
@xor = case @xor
when Array
@xor
when Integer, String
[ @xor ]
else
raise "Invalid xor type: #{@xor}"
end
@xor.map! { |x| x.to_s }
end
attr_reader :char, :xor, :argument
def flag_name
return @flag_name if @flagname
return self.class.flag_name(@char)
end
def self.flag_name(char)
return '_' + @@map[char] if @@map[char]
return '_' + char
end
end
# better ARGF.
def argf_each
if ARGV.count > 0
ARGV.each {|file|
File.open(file, "r") {|io|
yield file, io
}
}
else
yield nil, $stdin
end
end
def escape_cstr(x)
# escape a c string
x.gsub(/([\\"])/, "\\\\1")
end
code = ERB.new(DATA.read(), 0, "%<>")
argf_each {|filename, file|
data = YAML.load(file)
help = data['help']
options = data['options']
# options is an array of items which may be hashes, strings, or numbers.
# normalize them.
options = options.map {|opt|
opt = case opt
when String, Integer
{ 'char' => opt }
when Hash
# {'o' => { ... }}
# or
# {'char' => , ... }
if opt['char']
opt
else
opt = opt.first
opt[1].merge({ 'char' => opt[0] })
end
else
raise "Unexpected data type: #{opt}"
end
Option.new(opt)
}
#data[options] = options
# check for help?
basename = filename
basename = $1 if filename && filename =~ /^(.*)./
b = binding # bind help, options for ERB.
io = basename ? File.open(basename + ".c", "w") : $stdout
io.write(code.result(b))
io.close unless io == $stdout
io = basename ? File.open(basename + ".h", "w") : $stdout
io.write(header_preamble)
# two passes - one with arguments, one without.
options.each {|opt|
if opt.argument
io.printf(" char *%s;\n", opt.flag_name)
end
}
io.puts()
options.each {|opt|
if !opt.argument
io.printf(" unsigned %s:1;\n", opt.flag_name)
end
}
io.puts
io.write(header_postamble)
io.close unless io == $stdout
# #puts options.to_yaml
# puts code.result(binding())
}
__END__
#ifdef __ORCAC__
#pragma optimize 79
#pragma noroot
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "flags.h"
void FlagsHelp(void)
{
% help.each do |h|
fputs("<%= escape_cstr(h) %>\n", stdout);
% end
fputs("\n", stdout);
exit(0);
}
int FlagsParse(int argc, char **argv)
{
char *cp;
char c;
int i;
int j;
memset(&flags, 0, sizeof(flags));
for (i = 1; i < argc; ++i)
{
cp = argv[i];
c = cp[0];
if (c != '-')
return i;
// -- = end of options.
if (cp[1] == '-' && cp[2] == 0)
return i + 1;
// now scan all the flags in the string...
for (j = 1; ; ++j)
{
int skip = 0;
c = cp[j];
if (c == 0) break;
switch (c)
{
% if help && !options.find_index {|x| x.char == 'h' }
case 'h':
FlagsHelp();
break;
% end
% #
% options.each do |opt|
case '<%= escape_cstr(opt.char) %>':
% # check for an argument.
% flag_name = 'flags.' + opt.flag_name
% #
% if opt.argument
// -xarg or -x arg
skip = 1;
if (cp[j + 1])
{
<%= flag_name %> = cp + j + 1;
}
else
{
if (++i >= argc)
{
fprintf(stderr, "option requires an argument -- %c\n", c);
return -1;
}
<%= flag_name %> = argv[i];
}
% else # no argument.
<%= flag_name %> = 1;
% end # if no argument.
% #
% # unset any exclusive or values
% opt.xor.each do |xor_opt|
flags.<%= Option.flag_name(xor_opt) %> = 0;
%end
break;
% end # options.each
default:
fprintf(stderr, "illegal option -- %c\n", c);
return -1;
}
if (skip) break;
}
}
return i;
}

21
flags.txt Normal file
View File

@ -0,0 +1,21 @@
---
options:
- o: { argument : true, xor: 'O' }
- i
- I
- v
- O
- 0: { xor: '1'}
- 1: { xor: '0' }
help:
- gopher [options] url
- -h display help information
- -V display version information
- -i display http headers
- -I http HEAD
- -O write output to file
- -o <file> write output to <file> instead of stdout
- -0 use HTTP 1.0
- -9 use HTTP 0.9

52
ftype.c
View File

@ -1,17 +1,47 @@
#pragma optimize 79
#pragma noroot
#include <Types.h>
int parse_ftype(const char *cp, Word size, Word *ftype, Word *atype)
// cp should be a filename w/ .ext
int parse_extension(const char *cp, Word size, Word *ftype, LongWord *atype);
int parse_extension_c(const char *cp, Word *ftype, LongWord *atype)
{
int i;
int pd;
if (!cp || !*cp) return 0;
pd = -1;
for (i = 0; ; ++i)
{
char c;
c = cp[i];
if (c == 0) break;
if (c == '.') pd = i;
}
// pd == position of final .
// i == strlen
if (pd == -1) return 0;
if (pd + 1 >= i) return 0;
pd++; // skip past it...
return parse_extension(cp + pd, i - pd, ftype, atype);
}
// cp is just the extension
int parse_extension(const char *cp, Word size, Word *ftype, LongWord *atype)
{
Word *wp = (Word *)cp;
Word h;
*ftype = 0;
*atype = 0;
if (!cp || !size) return 0;
h = ((*cp | 0x20) ^ size) & 0x0f;
switch (h)
@ -50,14 +80,6 @@ int parse_ftype(const char *cp, Word size, Word *ftype, Word *atype)
break;
case 0x02:
// c
if (size == 1
&& (cp[0] | 0x20) == 0x63 // 'c'
) {
*ftype = 0xb0;
*atype = 0x0008;
return 1;
}
// asm
if (size == 3
&& (wp[0] | 0x2020) == 0x7361 // 'as'
@ -67,6 +89,14 @@ int parse_ftype(const char *cp, Word size, Word *ftype, Word *atype)
*atype = 0x0003;
return 1;
}
// c
if (size == 1
&& (cp[0] | 0x20) == 0x63 // 'c'
) {
*ftype = 0xb0;
*atype = 0x0008;
return 1;
}
break;
case 0x03:

View File

@ -1,17 +1,47 @@
%%
#pragma optimize 79
#pragma noroot
#include <Types.h>
int parse_ftype(const char *cp, Word size, Word *ftype, Word *atype)
// cp should be a filename w/ .ext
int parse_extension(const char *cp, Word size, Word *ftype, LongWord *atype);
int parse_extension_c(const char *cp, Word *ftype, LongWord *atype)
{
int i;
int pd;
if (!cp || !*cp) return 0;
pd = -1;
for (i = 0; ; ++i)
{
char c;
c = cp[i];
if (c == 0) break;
if (c == '.') pd = i;
}
// pd == position of final .
// i == strlen
if (pd == -1) return 0;
if (pd + 1 >= i) return 0;
pd++; // skip past it...
return parse_extension(cp + pd, i - pd, ftype, atype);
}
// cp is just the extension
int parse_extension(const char *cp, Word size, Word *ftype, LongWord *atype)
{
Word *wp = (Word *)cp;
Word h;
*ftype = 0;
*atype = 0;
if (!cp || !size) return 0;
h = ((*cp | 0x20) ^ size) & 0x0f;

9
gno.orca.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef __orca__
#define __orca__
#include <stdio.h>
#define fsetbinary(f) (f->_flag &= ~_IOTEXT)
#endif

View File

@ -15,7 +15,10 @@ TEXT = {
".h" => true,
".txt" => true,
".text" => true,
".rb" => true
".rb" => true,
'.mk' => true,
'.asm' => true,
'makefile' => true
}
def do_error(client, message)
@ -27,9 +30,11 @@ end
def get_type(path)
ext = File.extname(path).downcase
path = path.downcase
ext = File.extname(path)
return 0 if TEXT[ext]
return 0 if TEXT[path]
return 9
end

486
gopher.c
View File

@ -1,13 +1,11 @@
#pragma optimize 79
#pragma noroot
#include <Locator.h>
#include <GSOS.h>
#include <Memory.h>
#include <MiscTool.h>
#include <tcpip.h>
#include "url.h"
#include "connection.h"
#include "readline2.h"
#include <stdio.h>
#include <string.h>
@ -16,7 +14,26 @@
#include <unistd.h>
extern int setfiletype(const char *filename);
#include "url.h"
#include "connection.h"
#include "readline2.h"
#include "options.h"
#include "s16debug.h"
#include "prototypes.h"
static FileInfoRecGS FileInfo;
static Word FileAttr;
static int gopher_binary(Word ipid, FILE *file)
{
ReadBlock dcb;
int ok;
ok = read_binary(ipid, file, &dcb);
return ok;
}
/*
* connect gopher.floodgap.com:70
@ -26,106 +43,7 @@ extern int setfiletype(const char *filename);
* bin -- ends when connection is closed.
*/
// startup/shutdown flags.
enum {
kLoaded = 1,
kStarted = 2,
kConnected = 4
};
Word StartUp(displayPtr fx)
{
word status;
word flags = 0;
// TCPIP is an init, not a tool, so it should always
// be loaded.
status = TCPIPStatus();
if (_toolErr)
{
LoadOneTool(54, 0x0300);
if (_toolErr) return -1;
status = 0;
flags |= kLoaded;
}
#if 0
// require 3.0b3
if (TCPIPLongVersion() < 0x03006003)
{
if (flags & kLoaded)
UnloadOneTool(54);
return -1;
}
#endif
if (!status)
{
TCPIPStartUp();
if (_toolErr) return -1;
flags |= kStarted;
}
status = TCPIPGetConnectStatus();
if (!status)
{
TCPIPConnect(fx);
flags |= kConnected;
}
return flags;
}
void ShutDown(word flags, Boolean force, displayPtr fx)
{
if (flags & kConnected)
{
TCPIPDisconnect(force, fx);
if (_toolErr) return;
}
if (flags & kStarted)
{
TCPIPShutDown();
if (_toolErr) return;
}
if (flags & kLoaded)
{
UnloadOneTool(54);
}
}
int gopher_binary(Word ipid, FILE *file)
{
// gopher binary support.
// format: raw data until eof.
Word rv = 0;
for(;;)
{
static char buffer[512];
rrBuff rb;
Word count;
TCPIPPoll();
rv = TCPIPReadTCP(ipid, 0, (Ref)buffer, 512, &rb);
count = rb.rrBuffCount;
if (rv == 0 && count == 0) continue;
if (rv && !count) break;
if (!count) continue;
fwrite(buffer, 1, count, file);
}
return rv;
}
int gopher_text(Word ipid, FILE *file)
static int gopher_text(Word ipid, FILE *file)
{
// text \r\n
// ...
@ -136,8 +54,10 @@ int gopher_text(Word ipid, FILE *file)
int rv = 0;
Word lastTerm = 0;
IncBusy();
TCPIPPoll();
DecBusy();
for(;;)
{
Word count;
@ -153,7 +73,10 @@ int gopher_text(Word ipid, FILE *file)
if (rv < 0) break; // eof
if (rv == 0) // no data available (yet)
{
IncBusy();
TCPIPPoll();
DecBusy();
continue;
}
@ -166,7 +89,7 @@ int gopher_text(Word ipid, FILE *file)
{
if (lastTerm == '\r' && rb.terminator == '\n')
{
/* nothing */
lastTerm = TERMINATOR_CR_LF;
}
else
fputc('\r', file);
@ -212,7 +135,7 @@ int gopher_text(Word ipid, FILE *file)
return eof ? 0 : -1;
}
int gopher_dir(Word ipid, FILE *file)
static int gopher_dir(Word ipid, FILE *file)
{
Word eof = 0;
int rv = 0;
@ -237,11 +160,17 @@ int gopher_dir(Word ipid, FILE *file)
if (rv < 0) break;
if (rv == 0)
{
IncBusy();
TCPIPPoll();
DecBusy();
continue;
}
if (!rb.moreFlag) TCPIPPoll();
if (!rb.moreFlag)
{
IncBusy();
TCPIPPoll();
DecBusy();
}
if (!count)
{
@ -341,274 +270,167 @@ int gopher_dir(Word ipid, FILE *file)
return eof ? 0 : -1;
}
void do_gopher(const char *url, URLComponents *components, FILE *file)
int do_gopher(const char *url, URLComponents *components)
{
Connection buffer;
Connection connection;
char *host;
char *path;
char type;
int ok;
LongWord qtick;
FILE *file;
char *filename;
file = stdout;
FileAttr = 0;
memset(&FileInfo, 0, sizeof(FileInfo));
if (!components->portNumber) components->portNumber = 70;
host = malloc(components->host.length + 1);
URLComponentGetC(url, components, URLComponentHost, host);
ConnectionInit(&buffer, MMStartUp());
host = URLComponentGetCMalloc(url, components, URLComponentHost);
path = URLComponentGetCMalloc(url, components, URLComponentPath);
ConnectionOpenC(&buffer, host, components->portNumber);
// 30 second timeout.
qtick = GetTick() + 30 * 60;
while (!ConnectionPoll(&buffer))
if (!host)
{
if (GetTick() >= qtick)
fprintf(stderr, "URL `%s': no host.", url);
free(path);
return -1;
}
if (path && components->path.length <= 2)
{
free(path);
path = NULL;
}
// open the file.
filename = NULL;
if (flags._o)
{
filename = flags._o;
if (filename && !filename[0])
filename = NULL;
if (filename && filename[0] == '-' && !filename[1])
filename = NULL;
}
if (flags._O)
{
if (path)
{
fprintf(stderr, "Connection timed out.\n");
// todo -- still need to close it...
free(host);
return;
filename = strrchr(path + 2, '/');
if (filename) // *filename == '/'
{
filename++;
}
else
{
filename = path + 2;
}
}
if (!filename || !filename[0])
{
// path/ ?
fprintf(stderr, "-O flag cannot be used with this URL.\n");
free(host);
free(path);
return -1;
}
}
if (filename)
{
file = fopen(filename, "w");
if (!file)
{
fprintf(stderr, "Unable to to open file ``%s'': %s\n",
filename, strerror(errno));
free(host);
free(path);
return -1;
}
if (parse_extension_c(filename, &FileInfo.fileType, &FileInfo.auxType))
{
FileAttr |= ATTR_FILETYPE | ATTR_AUXTYPE;
setfileattr(filename, &FileInfo, FileAttr);
}
}
if (buffer.state == kConnectionStateError)
ok = ConnectLoop(host, components->portNumber, &connection);
if (!ok)
{
fprintf(stderr, "Unable to open host: %s:%u\n",
host,
components->portNumber);
free(host);
return;
free(path);
if (file != stdout) fclose(file);
return -1;
}
// connected....
// path is /[type][resource]
// where [type] is 1 char and the leading / is ignored.
IncBusy();
if (components->path.length <= 1)
if (path)
{
// / or blank
type = '1'; // directory
}
else if (components->path.length == 2)
{
// / type
// invalid -- treat as /
type = '1';
// path[0] = '/'
// path[1] = type
type = path[1];
TCPIPWriteTCP(
connection.ipid,
path + 2,
components->path.length - 2,
false,
false);
}
else
{
type = url[components->path.location+1];
TCPIPWriteTCP(
buffer.ipid,
url + components->path.location + 2,
components->path.length - 2,
0,
0);
type = 1;
}
//
TCPIPWriteTCP(buffer.ipid, "\r\n", 2, true, 0);
TCPIPWriteTCP(connection.ipid, "\r\n", 2, true, false);
DecBusy();
// 5 and 9 are binary, 1 is dir, all others text.
switch(type)
{
case '1':
gopher_dir(buffer.ipid, file);
gopher_dir(connection.ipid, file);
break;
case '5':
case '9':
fsetbinary(file);
gopher_binary(buffer.ipid, file);
ok = gopher_binary(connection.ipid, file);
break;
default:
gopher_text(buffer.ipid, file);
ok = gopher_text(connection.ipid, file);
break;
}
fflush(file);
if (file != stdout) fclose(file);
ConnectionClose(&buffer);
while (!ConnectionPoll(&buffer)) ; // wait for it to close.
free (host);
}
void help(void)
{
fputs("gopher [options] url\n", stdout);
fputs("-h display help information.\n", stdout);
fputs("-v display version information.\n", stdout);
fputs("-O write output to file.\n", stdout);
fputs("-o <file> write output to <file> instead of stdout.\n", stdout);
fputs("\n", stdout);
exit(0);
}
/*
*
*
*/
char *get_url_filename(const char *cp, URLComponents *components)
{
URLRange path;
int slash;
int i, j;
char *out;
path = components->path;
if (path.length <= 0) return NULL;
cp += path.location;
// scan the path, for the last '/'
slash = -1;
for (i = 0; i < path.length; ++i)
{
if (cp[i] == '/') slash = i;
}
if (slash == -1 || slash + 1 >= path.length) return NULL;
out = (char *)malloc(path.length - slash);
if (!out) return NULL;
j = 0;
i = slash + 1; // skip the slash.
while (i < path.length)
out[j++] = cp[i++];
out[j] = 0; // null terminate.
return out;
}
int main(int argc, char **argv)
{
int i;
Word flags;
int ch;
char *filename = NULL;
int flagO = 0;
flags = StartUp(NULL);
while ((ch = getopt(argc, argv, "o:Oh")) != -1)
{
switch (ch)
{
case 'v':
fputs("gopher v 0.1\n", stdout);
exit(0);
break;
case 'o':
filename = optarg;
break;
case 'O':
flagO = 1;
break;
case 'h':
case '?':
case ':':
default:
help();
break;
}
}
argc -= optind;
argv += optind;
if (argc != 1)
{
help();
}
if (argc == 1)
{
const char *url;
URLComponents components;
url = *argv;
if (!ParseURL(url, strlen(url), &components))
{
fprintf(stderr, "Invalid URL: %s\n", url);
exit(1);
}
if (!components.host.length)
{
fprintf(stderr, "No host.\n");
exit(1);
}
if (components.schemeType == SCHEME_GOPHER)
{
FILE *file = NULL;
if (!components.portNumber) components.portNumber = 70;
if (filename)
{
file = fopen(filename, "w");
if (!file)
{
fprintf(stderr, "Unable to open file ``%s'': %s",
filename, strerror(errno));
exit(1);
}
setfiletype(filename);
}
else if (flagO)
{
// get the file name from the URL.
filename = get_url_filename(url, &components);
if (!filename)
{
fprintf(stderr, "-O flag cannot be used with this URL.\n");
exit(1);
}
file = fopen(filename, "w");
if (!file)
{
fprintf(stderr, "Unable to open file ``%s'': %s",
filename, strerror(errno));
exit(1);
}
setfiletype(filename);
free(filename);
filename = NULL;
}
else file = stdout;
do_gopher(url, &components, file);
if (file != stdout) fclose(file);
}
else
{
fprintf(stderr, "Unsupported scheme.\n");
exit(1);
}
}
ShutDown(flags, false, NULL);
CloseLoop(&connection);
free(host);
free(path);
return 0;
}

56
htest.c Normal file
View File

@ -0,0 +1,56 @@
#include <stdio.h>
#include <string.h>
#include "http.utils.h"
int main(int argc, char **argv)
{
int version;
int status;
int ok;
char *cp;
URLRange k, v;
cp = "HTTP/1.0 200 OK";
ok = parseStatusLine(cp, strlen(cp), &version, &status);
if (!ok || version != 0x0100 || status != 200)
{
fprintf(stderr, "%s: %x %d\n", cp, version, status);
}
cp = "HTTP/1.1 404 bleh";
ok = parseStatusLine(cp, strlen(cp), &version, &status);
if (!ok || version != 0x0101 || status != 404)
{
fprintf(stderr, "%s: %x %d\n", cp, version, status);
}
cp = "http/1.10 200 asdasd as dsad asd";
ok = parseStatusLine(cp, strlen(cp), &version, &status);
if (!ok || version != 0x010a || status != 200)
{
fprintf(stderr, "%s: %x %d\n", cp, version, status);
}
cp = "key: value";
ok = parseHeaderLine(cp, strlen(cp), &k, &v);
if (!ok
|| k.location != 0
|| k.length != 3
|| v.location != 5
|| v.length != 5)
{
fprintf(stderr, "%s: %.*s, %.*s\n", cp,
k.length, cp + k.location,
v.length, cp + v.location);
}
return 0;
}

729
http.c Normal file
View File

@ -0,0 +1,729 @@
/*
* HTTP 1.0
*
* request:
* verb url version CR LF
* header CR LF *
* CR LF
*
* response:
* status CR LF
* header CR LF *
* CR LF
* data
*/
#pragma optimize 79
#pragma noroot
#pragma debug 0x8000
#pragma lint -1
#include <TCPIP.h>
#include <MiscTool.h>
#include <Memory.h>
#include <IntMath.h>
#include <TimeTool.h>
#include <GSOS.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include "url.h"
#include "connection.h"
#include "dictionary.h"
#include "options.h"
#include "readline2.h"
#include "http.utils.h"
#include "s16debug.h"
#include "prototypes.h"
static FileInfoRecGS FileInfo;
static Word FileAttr;
static int do_http_0_9(
const char *url,
URLComponents *components,
Word ipid,
FILE *file,
const char *filename)
{
ReadBlock dcb;
int ok;
char *cp;
int length;
IncBusy();
TCPIPWriteTCP(ipid, "GET ", 4, false, false);
length = components->pathAndQuery.length;
cp = url + components->pathAndQuery.location;
if (!length)
{
length = 1;
cp = "/";
}
TCPIPWriteTCP(ipid, cp, length, false, false);
TCPIPWriteTCP(ipid, "\r\n", 2, true, false);
DecBusy();
ok = read_binary(ipid, file, &dcb);
return ok;
}
static int parseHeaders(Word ipid, FILE *file, Handle dict)
{
int prevTerm = 0;
int term = 0;
int line;
int status = -1;
// todo -- timeout?
/*
* HTTP/1.1 200 OK <CRLF>
* header: value <CRLF>
* header: value <CRLF>
* ...
* <CRLF>
*/
line = 0;
IncBusy();
TCPIPPoll();
DecBusy();
for (;;)
{
rlBuffer rb;
Handle h;
char *cp;
int ok;
ok = ReadLine2(ipid, &rb);
if (ok == 0)
{
IncBusy();
TCPIPPoll();
DecBusy();
continue;
}
// eof = error.
if (ok == -1) return -1;
// could be end of header...
// or a split cr/lf
if (rb.bufferSize == 0)
{
if (rb.terminator == '\n' && prevTerm == '\r')
{
term = TERMINATOR_CR_LF;
prevTerm = TERMINATOR_CR_LF;
continue;
}
// end of header.
// TODO -- if term is CRLF and only a CR was read,
// need to yank it later.
// if rb.terminater == '\r' && term == TERMINATOR_CR_LF ...
if (rb.terminator)
{
if (flags._i || flags._I)
fputc('\r', file);
}
if (line == 0) return -1;
break;
}
prevTerm = rb.terminator;
h = rb.bufferHandle;
HLock(h);
cp = *(char **)h;
if (flags._i || flags._I)
{
fwrite(cp, 1, rb.bufferSize, file);
fputc('\r', file);
}
// line 1 is the status.
if (++line == 1)
{
// HTTP/1.1 200 OK
int i, l;
int version;
term = rb.terminator;
if (parseStatusLine(cp, rb.bufferSize, &version, &status))
{
/* ok... */
}
else
{
fprintf(stderr, "Bad HTTP status: %.*s\n", (int)rb.bufferSize, cp);
DisposeHandle(h);
return -1;
}
}
else
{
// ^([^\s:]+):\s*(.*)$
// ^\s+*(.*)$ -- continuation of previous line (ignored)
URLRange key, value;
if (parseHeaderLine(cp, rb.bufferSize, &key, &value))
{
if (key.length)
{
DictionaryAdd(dict,
cp + key.location, key.length,
cp + value.location, value.length,
false);
}
}
}
DisposeHandle(h);
}
return status;
}
enum {
TE_unknown = -1,
TE_identity = 0,
TE_chunked
};
int http_read_chunked(word ipid, FILE *file)
{
ReadBlock dcb;
rlBuffer rb;
LongWord chunkSize;
LongWord count;
Handle h;
word i;
int ok;
for (;;)
{
char *cp;
// get the chunk size.
// 0 indicates end.
for (;;)
{
ok = ReadLine2(ipid, &rb);
if (ok == 0)
{
IncBusy();
TCPIPPoll();
DecBusy();
continue;
}
if (ok == -1) return -1;
h = rb.bufferHandle;
HLock(h);
cp = *(char **)h;
for (i = 0; i < (Word)rb.bufferSize; ++i)
{
if (!isxdigit(cp[i])) break;
}
if (i == 0) return -1;
chunkSize = Hex2Long(cp, i);
// is there another <CRLF> pair?
break;
}
// now read the data.
if (chunkSize == 0) return 0; // eof.
dcb.requestCount = chunkSize;
ok = read_binary_size(ipid, file, &dcb);
if (ok < 0) return -1;
if (dcb.requestCount != dcb.transferCount)
{
fprintf(stderr, "Read error - requested %ld, received %ld\n",
dcb.requestCount, dcb.transferCount);
return -1;
}
// read CRLF.
for(;;)
{
ok = ReadLine2(ipid, &rb);
if (ok == -1) return -1;
if (ok == 0)
{
TCPIPPoll();
continue;
}
if (ok == 1)
{
if (rb.bufferSize)
{
fprintf(stderr, "Unexpected data in chunked response.\n");
return -1;
}
break;
}
}
}
return 0;
}
int read_response(Word ipid, FILE *file, Handle dict)
{
// get the file size and content encoding
// from the dict.
// todo -- check for text/* ?
// -m <mimetype>?
char *value;
Word valueSize;
int transferEncoding;
LongWord contentSize;
int haveTime = 0;
contentSize = 0;
transferEncoding = -1;
value = DictionaryGet(dict, "Content-Length", 14, &valueSize);
if (value)
{
contentSize = Dec2Long(value, valueSize, 0);
transferEncoding = 0;
}
/*
* check the transfer encoding header
* should be chunked or identity (default)
*
*/
value = DictionaryGet(dict, "Transfer-Encoding", 17, &valueSize);
if (value)
{
Word *wp = (Word *)value;
// id en ti ty ?
// ch un ke d ?
if (valueSize == 8
&& (wp[0] | 0x2020) == 0x6469
&& (wp[1] | 0x2020) == 0x6e65
&& (wp[2] | 0x2020) == 0x6974
&& (wp[3] | 0x2020) == 0x7974
)
{
transferEncoding = 0;
}
else if (valueSize == 7
&& (wp[0] | 0x2020) == 0x6863
&& (wp[1] | 0x2020) == 0x6e75
&& (wp[2] | 0x2020) == 0x656b
&& (value[6] | 0x20) == 0x64
)
{
transferEncoding = 1;
}
else
{
fprintf(stderr, "Unsupported Transfer Encoding: %.*s\n",
valueSize, value);
return -1;
}
}
/*
* convert a content-type header mime string into
* a file type / aux type.
*
*/
value = DictionaryGet(dict, "Content-Type", 12, &valueSize);
if (value && valueSize)
{
int i;
int slash = -1;
// strip ';'
for (i = 0; i < valueSize; ++i)
{
char c = value[i];
if (c == ';') break;
if (c == '/') slash = i;
}
// todo -- flag for this or not.
valueSize = i;
if (parse_mime(value, valueSize,
&FileInfo.fileType,
&FileInfo.auxType))
{
FileAttr |= ATTR_FILETYPE | ATTR_AUXTYPE;
}
else if (slash != -1 && parse_mime(value, slash,
&FileInfo.fileType,
&FileInfo.auxType))
{
FileAttr |= ATTR_FILETYPE | ATTR_AUXTYPE;
}
}
/*
* convert the Last Modified header into a file mod date
*
*/
value = DictionaryGet(dict, "Last-Modified", 13, &valueSize);
if (value && valueSize <= 255)
{
char *pstring;
pstring = (char *)malloc(valueSize + 1);
if (pstring)
{
struct {
LongWord lo;
LongWord hi;
} xcomp;
*pstring = valueSize;
memcpy(pstring + 1, value, valueSize);
// parse the last-modified timestamp.
// 0x0e00 is rfc 822 format.
// (which is now obsoleted by rfc 2822 but close enough)
// should use _tiDateString2Sec to get seconds
// then use ConvSeconds to get the date
// this should handle timezones.
tiDateString2Sec(&xcomp, pstring, 0x0e00);
if (!_toolErr && xcomp.hi == 0)
{
ConvSeconds(secs2TimeRec, (Long)xcomp.lo,
(Pointer)&FileInfo.modDateTime);
FileAttr |= ATTR_MODTIME;
haveTime = 1;
}
free(pstring);
}
}
// ?
if (transferEncoding == -1)
{
fprintf(stderr, "Content Size not provided.\n");
return -1;
}
if (transferEncoding == 0)
{
// identity.
// just read contentLength bytes.
ReadBlock dcb;
int ok;
dcb.requestCount = contentSize;
ok = read_binary_size(ipid, file, &dcb);
if (ok < 0 || dcb.transferCount != dcb.requestCount)
{
fprintf(stderr, "Read error - requested %ld, received %ld\n",
dcb.requestCount, dcb.transferCount);
return -1;
}
return 0;
}
if (transferEncoding == 1)
{
return http_read_chunked(ipid, file);
}
return -1;
}
static int do_http_1_1(
const char *url,
URLComponents *components,
Word ipid,
FILE *file,
const char *filename)
{
Handle dict;
DictionaryEnumerator e;
Word cookie;
Word MyID = MMStartUp();
int ok;
char *cp;
int length;
// html headers.
// (not really needed until 1.1)
dict = DictionaryCreate(MyID, 2048);
length = components->host.length;
cp = url + components->host.location;
DictionaryAdd(dict, "Host", 4, cp, length, false);
DictionaryAdd(dict, "Connection", 10, "close", 5, false);
// no gzip or compress.
DictionaryAdd(dict, "Accept-Encoding", 15, "identity", 8, false);
// connected....
// send the request.
// GET path HTTP/version\r\n
IncBusy();
if (flags._I)
TCPIPWriteTCP(ipid, "HEAD ", 5, false, false);
else
TCPIPWriteTCP(ipid, "GET ", 4, false, false);
length = components->pathAndQuery.length;
cp = url + components->pathAndQuery.location;
if (!length)
{
length = 1;
cp = "/";
}
TCPIPWriteTCP(ipid, cp, length, false, false);
if (flags._0)
{
TCPIPWriteTCP(ipid, " HTTP/1.0\r\n", 11, false, false);
}
else
{
TCPIPWriteTCP(ipid, " HTTP/1.1\r\n", 11, false, false);
}
// send the headers.
cookie = 0;
while ((cookie = DictionaryEnumerate(dict, &e, cookie)))
{
if (!e.keySize) continue;
TCPIPWriteTCP(ipid, e.key, e.keySize, false, false);
TCPIPWriteTCP(ipid, ": ", 2, false, false);
TCPIPWriteTCP(ipid, e.value, e.valueSize, false, false);
TCPIPWriteTCP(ipid, "\r\n", 2, false, false);
}
// end headers and push.
TCPIPWriteTCP(ipid, "\r\n", 2, true, false);
DecBusy();
DisposeHandle(dict);
dict = NULL;
dict = DictionaryCreate(MyID, 2048);
ok = parseHeaders(ipid, file, dict);
// todo -- check the headers for content length, transfer-encoding.
//
#if 0
cookie = 0;
while ((cookie = DictionaryEnumerate(dict, &e, cookie)))
{
s16_debug_printf("%.*s -> %.*s\n",
e.keySize, e.key, e.valueSize, e.value);
}
#endif
if (ok == 200)
{
if (!flags._I)
read_response(ipid, file, dict);
}
DisposeHandle(dict);
return 0;
}
int do_http(const char *url, URLComponents *components)
{
char *host;
char *path;
char *filename;
Connection connection;
int ok;
FILE *file;
file = stdout;
FileAttr = 0;
memset(&FileInfo, 0, sizeof(FileInfo));
if (!components->portNumber) components->portNumber = 80;
host = URLComponentGetCMalloc(url, components, URLComponentHost);
path = URLComponentGetCMalloc(url, components, URLComponentPath);
if (!host)
{
fprintf(stderr, "URL `%s': no host.", url);
free(path);
return -1;
}
// outfile.
filename = NULL;
if (flags._o)
{
filename = flags._o;
if (filename && !filename[0])
filename = NULL;
if (filename && filename[0] == '-' && !filename[1])
filename = NULL;
}
if (flags._O)
{
if (path)
{
// path starts with /.
// todo -- also need to strip any ? parameters.
filename = strrchr(path + 1, '/');
if (filename) // *filename == '/'
{
filename++;
}
else
{
filename = path + 1;
}
}
if (!filename || !filename[0])
{
// path/ ?
fprintf(stderr, "-O flag cannot be used with this URL.\n");
free(host);
free(path);
return -1;
}
}
// todo -- write to a tmp file rather than the named file.
// this will allow things like If-Modified-Since or If-None-Match
// so it only updates if changed.
// or -C continue to append
// -N -- if-modified-since header.
if (filename)
{
file = fopen(filename, "w");
if (!file)
{
fprintf(stderr, "Unable to to open file ``%s'': %s\n",
filename, strerror(errno));
free(host);
free(path);
return -1;
}
// hmm, flag for this vs content type?
if (parse_extension_c(filename, &FileInfo.fileType, &FileInfo.auxType))
{
FileAttr |= ATTR_FILETYPE | ATTR_AUXTYPE;
}
}
ok = ConnectLoop(host, components->portNumber, &connection);
if (!ok)
{
free(host);
free(path);
fclose(file);
return -1;
}
if (flags._9)
{
ok = do_http_0_9(url, components, connection.ipid, file, filename);
}
else
{
ok = do_http_1_1(url, components, connection.ipid, file, filename);
}
fflush(file);
if (file != stdout) fclose(file);
if (filename) setfileattr(filename, &FileInfo, FileAttr);
CloseLoop(&connection);
free(host);
free(path);
return 0;
}

156
http.utils.c Normal file
View File

@ -0,0 +1,156 @@
#ifdef __ORCAC__
#pragma optimize 79
#pragma noroot
#endif
#include <ctype.h>
#include "url.h"
int parseHeaderLine(const char *cp, unsigned length, URLRange *key, URLRange *value)
{
unsigned i, l;
key->location = 0;
key->length = 0;
value->location = 0;
value->length = 0;
// trim any trailing whitespace.
while (length)
{
if (isspace(cp[length - 1]))
--length;
else break;
}
if (!length) return 0;
/* format:
* key: value
* /^([^:]+):\s+(.*)$/
* -or-
* value [continuation of previous line]
* /^\s+(.*)$/
*/
for (i = 0; i < length; ++i)
{
if (cp[i] == ':') break;
}
if (i == length)
{
// try as value only
i = 0;
}
else
{
key->length = i;
i = i + 1;
}
// now gobble up all the whitespace...
for ( ; i < length; ++i)
{
if (!isspace(cp[i])) break;
}
// no value? no problem!
if (i == length) return 1;
value->location = i;
value->length = length - i;
return 1;
}
int parseStatusLine(const char *cp, unsigned length, int *version, int *status)
{
/*
* HTTP/1.1 200 OK etc.
*
*/
unsigned short *wp;
int i;
char c;
int x;
*version = 0;
*status = 0;
wp = (unsigned short *)cp;
// HTTP/
if (length <= 5) return 0;
if ((wp[0] | 0x2020) != 0x7468) return 0; // 'ht'
if ((wp[1] | 0x2020) != 0x7074) return 0; // 'tp'
if (cp[4] != '/') return 0;
// version string.
// \d+ . \d+
i = 5;
c = cp[i];
if (!isdigit(c)) return 0;
x = c - '0';
for (i = i + 1; i < length; ++i)
{
c = cp[i];
if (!isdigit(c)) break;
x = (x << 1) + (x << 3) + (c - '0');
}
*version = x << 8;
if (i == length) return 0;
if (cp[i++] != '.') return 0;
c = cp[i];
if (!isdigit(c)) return 0;
x = c - '0';
for (i = i + 1; i < length; ++i)
{
c = cp[i];
if (!isdigit(c)) break;
x = (x << 1) + (x << 3) + (c - '0');
}
*version |= x;
// 1+ space
if (i == length) return 0;
c = cp[i];
if (!isspace(c)) return 0;
for (i = i + 1; i < length; ++i)
{
c = cp[i];
if (!isspace(c)) break;
}
if (i == length) return 0;
c = cp[i];
if (!isdigit(c)) return 0;
x = c - '0';
for (i = i + 1; i < length; ++i)
{
c = cp[i];
if (!isdigit(c)) break;
x = (x << 1) + (x << 3) + (c - '0');
}
*status = x;
// rest of status line unimportant.
return 1;
}

11
http.utils.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef __HTTP_UTILS_H__
#define __HTTP_UTILS_H__
#include "url.h"
int parseHeaderLine(const char *line, unsigned length, URLRange *key, URLRange *value);
int parseStatusLine(const char *cp, unsigned length, int *version, int *status);
#endif

291
main.c Normal file
View File

@ -0,0 +1,291 @@
#pragma optimize 79
#include <Locator.h>
#include <TimeTool.h>
#include <tcpip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "url.h"
#include "connection.h"
#include "options.h"
#include "prototypes.h"
// startup/shutdown flags.
enum {
kLoaded = 1,
kStarted = 2,
kConnected = 4,
kLoadError = -1,
kVersionError = -2
};
int StartUpTZ(void)
{
Word status;
Word flags = 0;
status = tiStatus();
if (_toolErr)
{
LoadOneTool(0x38, 0x104);
if (_toolErr == toolVersionErr) return kVersionError;
if (_toolErr) return kLoadError;
status = 0;
flags |= kLoaded;
}
if (tiVersion() < 0x0104)
{
return kVersionError;
}
if (!status)
{
tiStartUp();
flags |= kStarted;
}
return flags;
}
int StartUpTCP(displayPtr fx)
{
word status;
word flags = 0;
// TCPIP is an init, not a tool, so it should always
// be loaded.
status = TCPIPStatus();
if (_toolErr)
{
LoadOneTool(54, 0x0300);
if (_toolErr == toolVersionErr) return kVersionError;
if (_toolErr) return kLoadError;
status = 0;
flags |= kLoaded;
}
// require 3.0b3
if (TCPIPLongVersion() < 0x03006003)
{
if (flags & kLoaded)
UnloadOneTool(54);
return kVersionError;
}
if (!status)
{
TCPIPStartUp();
if (_toolErr) return kLoadError;
flags |= kStarted;
}
status = TCPIPGetConnectStatus();
if (!status)
{
TCPIPConnect(fx);
flags |= kConnected;
}
return flags;
}
void ShutDownTZ(int flags)
{
if (flags <= 0) return;
if (flags & kStarted) tiShutDown();
if (flags & kLoaded) UnloadOneTool(0x38);
}
void ShutDownTCP(int flags, Boolean force, displayPtr fx)
{
if (flags <= 0) return;
if (flags & kConnected)
{
TCPIPDisconnect(force, fx);
if (_toolErr) return;
}
if (flags & kStarted)
{
TCPIPShutDown();
if (_toolErr) return;
}
if (flags & kLoaded)
{
UnloadOneTool(54);
}
}
/*
*
*
*/
char *get_url_filename(const char *cp, URLComponents *components)
{
URLRange path;
int slash;
int i, j;
char *out;
path = components->path;
if (path.length <= 0) return NULL;
cp += path.location;
// scan the path, for the last '/'
slash = -1;
for (i = 0; i < path.length; ++i)
{
if (cp[i] == '/') slash = i;
}
if (slash == -1 || slash + 1 >= path.length) return NULL;
out = (char *)malloc(path.length - slash);
if (!out) return NULL;
j = 0;
i = slash + 1; // skip the slash.
while (i < path.length)
out[j++] = cp[i++];
out[j] = 0; // null terminate.
return out;
}
#define GOPHER_VERSION "0.3"
void version(void)
{
puts("gopher version " GOPHER_VERSION);
}
void help(void)
{
puts("gopher version " GOPHER_VERSION);
puts("usage: gopher [options] url");
puts("");
puts("-h show help");
puts("-V show version");
puts("-v be verbose");
puts("-o file write output to file");
puts("-O write output to file based on URL");
puts("");
puts("HTTP options:");
puts("-9 use HTTP version 0.9");
puts("-0 use HTTP version 1.0");
puts("-1 use HTTP version 1.1");
puts("-i print headers");
puts("-I print only headers (HEAD)");
}
// #pragma databank [push | pop] would be nice...
#pragma databank 1
pascal void DisplayCallback(const char *message)
{
unsigned length;
// message is a p-string.
length = message ? message[0] : 0;
if (!length) return;
fprintf(stderr, "%.*s\n", length, message + 1);
}
#pragma databank 0
int main(int argc, char **argv)
{
int i;
int mf;
int tf;
int x;
memset(&flags, 0, sizeof(flags));
argc = GetOptions(argc, argv, &flags);
if (argc != 2)
{
help();
return 1;
}
tf = StartUpTZ();
if (tf < 0)
{
fprintf(stderr, "Time Tool 1.0.4 or greater is required.\n");
exit(1);
}
mf = StartUpTCP(flags._v ? DisplayCallback : NULL);
if (mf < 0)
{
ShutDownTZ(tf);
fprintf(stderr, "Marinetti 3.0b3 or greater is required.\n");
exit(1);
}
if (argc == 2)
{
const char *url;
URLComponents components;
url = argv[1];
if (!ParseURL(url, strlen(url), &components))
{
fprintf(stderr, "Invalid URL: %s\n", url);
exit(1);
}
if (!components.host.length)
{
fprintf(stderr, "No host.\n");
exit(1);
}
if (components.schemeType == SCHEME_GOPHER)
{
do_gopher(url, &components);
}
else if (components.schemeType == SCHEME_HTTP)
{
do_http(url, &components);
}
else
{
fprintf(stderr, "Unsupported scheme.\n");
exit(1);
}
}
ShutDownTCP(mf, false, flags._v ? DisplayCallback : NULL);
ShutDownTZ(tf);
return 0;
}

View File

@ -1,20 +1,33 @@
CFLAGS += $(DEFINES) -v -w
OBJS = gopher.o url.o connection.o readline2.o scheme.o ftype.o setftype.o
OBJS = main.o gopher.o url.o connection.o readline2.o scheme.o ftype.o \
mime.o setftype.o s16debug.o common.o http.o http.utils.o \
dictionary.o options.o time.o
gopher: $(OBJS)
$(CC) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $@
utest: utest.o url.o
$(CC) $(LDFLAGS) utest.o url.o -o $@
utest: utest.o url.o scheme.o
$(CC) $(LDFLAGS) -o $@ $<
dtest: dtest.o dictionary.o
$(CC) $(LDFLAGS) dtest.o dictionary.o -o $@
$(CC) $(LDFLAGS) -o $@ $<
gopher.o: gopher.c url.h connection.h
htest: htest.o http.utils.o
$(CC) $(LDFLAGS) -o $@ $<
main.o: main.c url.h
url.o: url.c url.h
connection.o: connection.c connection.h
readline2.o: readline2.c readline2.h
common.o: common.c
options.o: options.c options.h
gopher.o: gopher.c url.h connection.h options.h
http.o: http.c url.h connection.h options.h
http.utils.o: http.utils.c
data.o: data.c data.h
dictionary.o: dictionary.c dictionary.h
@ -22,10 +35,16 @@ dictionary.o: dictionary.c dictionary.h
setftype.o: setftype.c
scheme.o: scheme.c url.h
ftype.o: ftype.c
mime.o: mime.c
time.o: time.c
s16debug.o: s16debug.c s16debug.h
# tests
utest.o: utest.c
dtest.o: dtest.c
htest.o: htest.c
clean:

134
mime.c Normal file
View File

@ -0,0 +1,134 @@
#pragma optimize 79
#pragma noroot
#pragma debug 0x8000
#include <Types.h>
#include "prototypes.h"
int parse_mime_c(const char *cp, Word *ftype, LongWord *atype)
{
int i;
int slash;
int semi;
if (!cp || !*cp)
return 0;
/*
* two pass
* 1. type/subtype
* 2. type
*/
semi = slash = -1;
for (i = 0; ; ++i)
{
char c = cp[i];
if (c == 0 || c == ';') break;
if (c == '/')
{
slash = i;
}
}
// try type/subtype
if (parse_mime(cp, i, ftype, atype));
return 1;
// try type
if (slash != -1)
return parse_mime(cp, slash, ftype, atype);
return 0;
}
int parse_mime(const char *cp, Word size, Word *ftype, LongWord *atype)
{
Word *wp = (Word *)cp;
Word h;
if (!cp || !size) return 0;
retry:
h = ((*cp | 0x20) ^ size) & 0x0f;
switch (h)
{
case 0x00:
// text
if (size == 4
&& (wp[0] | 0x2020) == 0x6574 // 'te'
&& (wp[1] | 0x2020) == 0x7478 // 'xt'
) {
*ftype = 0x04;
*atype = 0x0000;
return 1;
}
break;
case 0x09:
// text/x-pascal
if (size == 13
&& (wp[0] | 0x2020) == 0x6574 // 'te'
&& (wp[1] | 0x2020) == 0x7478 // 'xt'
&& (wp[2] | 0x2020) == 0x782f // '/x'
&& (wp[3] | 0x2020) == 0x702d // '-p'
&& (wp[4] | 0x2020) == 0x7361 // 'as'
&& (wp[5] | 0x2020) == 0x6163 // 'ca'
&& (cp[12] | 0x20) == 0x6c // 'l'
) {
*ftype = 0xb0;
*atype = 0x0005;
return 1;
}
// application/octet-stream
if (size == 24
&& (wp[0] | 0x2020) == 0x7061 // 'ap'
&& (wp[1] | 0x2020) == 0x6c70 // 'pl'
&& (wp[2] | 0x2020) == 0x6369 // 'ic'
&& (wp[3] | 0x2020) == 0x7461 // 'at'
&& (wp[4] | 0x2020) == 0x6f69 // 'io'
&& (wp[5] | 0x2020) == 0x2f6e // 'n/'
&& (wp[6] | 0x2020) == 0x636f // 'oc'
&& (wp[7] | 0x2020) == 0x6574 // 'te'
&& (wp[8] | 0x2020) == 0x2d74 // 't-'
&& (wp[9] | 0x2020) == 0x7473 // 'st'
&& (wp[10] | 0x2020) == 0x6572 // 're'
&& (wp[11] | 0x2020) == 0x6d61 // 'am'
) {
*ftype = 0x02;
*atype = 0x0000;
return 1;
}
break;
case 0x0c:
// text/x-c
if (size == 8
&& (wp[0] | 0x2020) == 0x6574 // 'te'
&& (wp[1] | 0x2020) == 0x7478 // 'xt'
&& (wp[2] | 0x2020) == 0x782f // '/x'
&& (wp[3] | 0x2020) == 0x632d // '-c'
) {
*ftype = 0xb0;
*atype = 0x0008;
return 1;
}
break;
}
/*
// try again as type
while (--size)
{
if (cp[size] == '/') goto retry;
}
*/
return 0;
}

100
mime.txt Normal file
View File

@ -0,0 +1,100 @@
%%
#pragma optimize 79
#pragma noroot
#pragma debug 0x8000
#include <Types.h>
#include "prototypes.h"
int parse_mime_c(const char *cp, Word *ftype, LongWord *atype)
{
int i;
int slash;
int semi;
if (!cp || !*cp)
return 0;
/*
* two pass
* 1. type/subtype
* 2. type
*/
semi = slash = -1;
for (i = 0; ; ++i)
{
char c = cp[i];
if (c == 0 || c == ';') break;
if (c == '/')
{
slash = i;
}
}
// try type/subtype
if (parse_mime(cp, i, ftype, atype));
return 1;
// try type
if (slash != -1)
return parse_mime(cp, slash, ftype, atype);
return 0;
}
int parse_mime(const char *cp, Word size, Word *ftype, LongWord *atype)
{
Word *wp = (Word *)cp;
Word h;
if (!cp || !size) return 0;
retry:
h = ((*cp | 0x20) ^ size) & 0x0f;
switch (h)
{
%%
}
/*
// try again as type
while (--size)
{
if (cp[size] == '/') goto retry;
}
*/
return 0;
}
%%
'text' ->
*ftype = 0x04;
*atype = 0x0000;
return 1;
.
'text/x-c' ->
*ftype = 0xb0;
*atype = 0x0008;
return 1;
.
'text/x-pascal' ->
*ftype = 0xb0;
*atype = 0x0005;
return 1;
.
'application/octet-stream' ->
*ftype = 0x02;
*atype = 0x0000;
return 1;
.

116
options.c Normal file
View File

@ -0,0 +1,116 @@
#ifdef __ORCAC__
#pragma optimize 79
#pragma noroot
#endif
#include <stdio.h>
#include <stdlib.h>
#include "options.h"
extern void help(void);
extern void version(void);
/* global */
struct Options flags;
int GetOptions(int argc, char **argv,
struct Options *options)
{
int i, j;
int eof = 0;
int mindex = 1; /* mutation index */
for (i = 1; i < argc; ++i)
{
char *cp = argv[i];
char c = cp[0];
if (eof || c != '-')
{
if (mindex != i) argv[mindex] = argv[i];
++mindex;
continue;
}
// long opt check would go here...
if (cp[1] == '-' && cp[2] == 0)
{
eof = 1;
continue;
}
// special case for '-'
j = 0;
if (cp[1] != 0) j = 1;
for (; ; ++j)
{
char *optarg = 0;
c = cp[j];
if (!c) break;
switch(c)
{
case '0':
options->_0 = 1;
options->_1 = 0;
options->_9 = 0;
break;
case '1':
options->_1 = 1;
options->_0 = 0;
options->_9 = 0;
break;
case '9':
options->_9 = 1;
options->_0 = 0;
options->_1 = 0;
break;
case 'I':
options->_I = 1;
break;
case 'O':
options->_O = 1;
break;
case 'V':
version();
exit(0);
break;
case 'h':
help();
exit(0);
break;
case 'i':
options->_i = 1;
break;
case 'o':
++j;
if (cp[j]) {
optarg = cp + j;
} else {
++i;
if (i < argc) optarg = argv[i];
}
if (!optarg) {
fputs("-o requires an argument\n", stderr);
exit(1);
}
options->_o = optarg;
options->_O = 0;
break;
case 'v':
options->_v = 1;
break;
default:
fprintf(stderr, "-%c : invalid option\n", c);
exit(1);
break;
}
// could optimize out if no options have flags.
if (optarg) break;
}
}
return mindex;
}

18
options.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef __Options__
#define __Options__
typedef struct Options
{
unsigned _0:1;
unsigned _1:1;
unsigned _9:1;
unsigned _I:1;
unsigned _O:1;
unsigned _i:1;
char *_o;
unsigned _v:1;
} Options;
int GetOptions(int argc, char **argv,
Options *options);
#endif

413
options.rb Normal file
View File

@ -0,0 +1,413 @@
#!/bin/env ruby -w
require 'erb'
require 'optparse'
class Option
attr_reader :name
attr_reader :description
attr_reader :argument # nil, :, =, ?
# name of the field, in the struct
# will be null if virtual.
attr_reader :field
# array of custom code.
attr_accessor :code
attr_reader :modifiers
def _fieldName(name, modifiers)
return nil if modifiers[:virtual]
return modifiers[:field] if modifiers[:field]
return "_#{name}"
end
def initialize(name, argument, modifiers)
@code = []
modifiers ||= {}
@name = name
@field = _fieldName(name, modifiers)
@argument = argument
@modifiers = modifiers
end
def to_s()
return "Option: #{description}#{argument} #{field}"
end
# def addCode(x)
# @code.push(x)
# end
# def finishCode
# # remove all blank lines at the end
# while @code.length && @code.last =~ /^\s*$/
# @code.pop
# end
# end
end
class ShortOption < Option
def initialize(name, argument, modifiers)
super(name, argument, modifiers)
@description = "-#{name}"
end
def generateCase()
rv = []
indent = " " * 8
rv.push indent, "case '#{@name}':\n"
indent += " "
if (@argument)
#print indent, "GETOPTARG(#{@description})\n"
rv.push indent, "++j;\n"
rv.push indent, "if (cp[j]) {\n"
rv.push indent, " optarg = cp + j;\n"
rv.push indent, "} else {\n"
rv.push indent, " ++i;\n"
rv.push indent, " if (i < argc) optarg = argv[i];\n"
rv.push indent, "}\n"
rv.push indent, "if (!optarg) {\n"
rv.push indent, " fputs(\"#{@description} requires an argument\\n\", stderr);\n"
rv.push indent, " exit(1);\n"
rv.push indent, "}\n"
end
if @field
if @argument
rv.push indent, "options->#{@field} = optarg;\n"
else
if @modifiers[:increment]
rv.push indent, "options->#{@field} += 1;\n"
else
rv.push indent, "options->#{@field} = 1;\n"
end
end
end
@code.each {|x| rv.push indent, x }
rv.push indent,"break;\n"
end
def generateField()
# generate the field for the struct.
return "" unless @field
return " char *#{@field};\n" if @argument
return " unsigned #{@field};\n" if @modifiers[:increment]
return " unsigned #{@field}:1;\n"
end
end
def arrayTrim(x)
while x.length && x.last =~ /^\s*$/
x.pop
end
x
end
def parseModifiers(string)
# splits a key=value (, key=value)* string
# key is equivalent to key={true}
rv = {}
return rv unless string
args = string.split(',')
args.each{|x|
x.strip!
next unless x.length
key, value = x.split('=')
value ||= true # key == key={true}
key = key.intern
rv[key] = value
}
return rv;
end
@shortRE = /
^
-([\w]) # 1. letter
([:])? # 2. argument type?
\s*
(?:\[
([\w\s,=]*) # 3. modifiers?
\])?
\s*
(->)? # 4. code?
$
/x
@longRE = /
^
--([\w-]*) # 1. name
([=?])? # 2. argument type?
\s*
(?:\[
([\w\s,=]*) # 3. modifiers?
\])?
\s*
(->)? # 4. code?
$
/x
@headerTemplate = <<EOD
#ifndef __<%= config[:prefix] %>Options__
#define __<%= config[:prefix] %>Options__
typedef struct <%= config[:prefix] %>Options
{
<%= (config[:extra_fields] || []).join() %>
<%= (options.map {|x| x.generateField }).join() %>
} <%= config[:prefix] %>Options;
int Get<%= config[:prefix] %>Options(int argc, char **argv,
<%= config[:prefix] %>Options *options);
#endif
EOD
#
def stripExtension(path)
return $1 if path =~ /^(.*?)\.([^.\/]*)$/
return path
end
def process(infile, keepName)
opt = nil # current option being processed
tmp = [] # code block array
callback = nil # callback when code is finished.
options = [] # list of options
config = {}
infile.each_line { |line|
line.chomp!
line.rstrip!
if callback
if line == "" || line =~ /^\s+/
tmp.push(line + "\n")
next
end
# store...
callback.call(arrayTrim(tmp));
callback = nil
tmp = []
code = false
end
next if line =~ /^\s*#/
if line =~ /^%/
if m = line.match(/^%(\w+)=(\w+)$/)
key = m[1].intern
config[key] = m[2]
next
end
if m = line.match(/^%(\w+)$/)
key = m[1].intern
config[key] = true
next
end
if m = line.match(/^%(\w+)\s*->$/)
key = m[1].intern
callback = Proc.new {|code| config[key] = code }
next
end
$stderr.puts "Not supported: #{line}"
next
end
if m = line.match(@shortRE)
tmp = []
flag = m[1]
arg = m[2]
modifiers = m[3]
modifiers = parseModifiers(modifiers)
opt = ShortOption.new(flag, arg, modifiers)
options.push(opt)
# any code?
if m[4]
callback = Proc.new {|code| opt.code = code }
end
end
#
}
# if it ended within a code block...
if callback
callback.call(arrayTrim(tmp))
callback = nil
tmp = []
end
options.sort! {|a, b| a.name <=> b.name }
keepName = nil if keepName == ""
keepBase = stripExtension(keepName)
headerName = (config[:prefix] || '') + 'Options.h'
headerName = keepBase + '.h' if keepBase
io = $stdout
io = File.open(keepName, "w") if keepName
b = binding
erb = ERB.new(DATA.read(), 0, "%<>")
io.write(erb.result(b))
io.close unless io == $stdout
# header file.
io = $stdout
io = File.open(headerName, "w") if headerName && keepName
erb = ERB.new(@headerTemplate, 0, "%<>")
io.write(erb.result(b))
io.close unless io == $stdout
end
keepFile = nil
op = OptionParser.new {|opts|
opts.banner = "Usage: options.rb [options] [infile]"
opts.on("-o", "--keep OUTFILE", "Specify output file name") do |filename|
keepFile = filename
end
}
op.parse!
keepFile = nil if keepFile == ""
case ARGV.length
when 0
process($stdin, keepFile)
when 1
file = ARGV[0]
io = $stdin
if file != '-'
keepFile = stripExtension(file) + '.c' if keepFile == nil
io = File.open(file, "r")
end
process(io, keepFile)
io.close unless io == $stdin
else
op.help
exit(1)
end
exit(0)
__END__
#ifdef __ORCAC__
#pragma optimize 79
#pragma noroot
#endif
#include <stdio.h>
#include <stdlib.h>
#include "<%= File.basename(headerName) %>"
<%= (config[:extra_includes] || []).join() %>
int Get<%= config[:prefix] %>Options(int argc, char **argv,
struct <%= config[:prefix] %>Options *options)
{
int i, j;
int eof = 0;
int mindex = 1; /* mutation index */
for (i = 1; i < argc; ++i)
{
char *cp = argv[i];
char c = cp[0];
<% if config[:posixly_correct] %>
// stop processing at first non-opt.
if (c != '-')
{
eof = 1;
if (i == mindex) return argc;
argv[mindex] = argv[i];
++mindex;
continue;
}
<% end %>
if (eof || c != '-')
{
if (mindex != i) argv[mindex] = argv[i];
++mindex;
continue;
}
// long opt check would go here...
if (cp[1] == '-' && cp[2] == 0)
{
eof = 1;
continue;
}
// special case for '-'
j = 0;
if (cp[1] != 0) j = 1;
for (; ; ++j)
{
char *optarg = 0;
c = cp[j];
if (!c) break;
switch(c)
{
<%= options.map{|o| o.generateCase() }.join() %>
default:
fprintf(stderr, "-%c : invalid option\n", c);
exit(1);
break;
}
// could optimize out if no options have flags.
if (optarg) break;
}
}
return mindex;
}

44
options.text Normal file
View File

@ -0,0 +1,44 @@
#
# gopher command line options.
#
%extra_includes ->
extern void help(void);
extern void version(void);
/* global */
struct Options flags;
-h [virtual] ->
help();
exit(0);
-V [virtual] ->
version();
exit(0);
# -o specifies the output name
# -O gets the name from the URL.
-o: ->
options->_O = 0;
-O
-i
-I
-v
# -[0|1] -- set HTTP version.
-0 ->
options->_1 = 0;
options->_9 = 0;
-1 ->
options->_0 = 0;
options->_9 = 0;
-9 ->
options->_0 = 0;
options->_1 = 0;

69
prototypes.h Normal file
View File

@ -0,0 +1,69 @@
#ifndef __prototypes_h__
#define __prototypes_h__
#include <stdio.h>
#define IncBusy() asm { jsl 0xE10064 }
#define DecBusy() asm { jsl 0xE10068 }
#define Resched() asm { cop 0x7f }
#define BusyFlag ((byte *)0xE100FFl)
#define SEI() asm { sei }
#define CLI() asm { cli }
pascal void DisplayCallback(const char *message);
typedef struct ReadBlock
{
LongWord requestCount;
LongWord transferCount;
} ReadBlock;
int read_binary(unsigned ipid, FILE *file, ReadBlock *);
int read_binary_size(unsigned ipid, FILE *file, ReadBlock *);
int parse_extension_c(const char *cp, Word *ftype, LongWord *atype);
int parse_extension(const char *cp, Word size, Word *ftype, LongWord *atype);
int parse_mime_c(const char *cp, Word *ftype, LongWord *atype);
int parse_mime(const char *cp, Word size, Word *ftype, LongWord *atype);
#ifdef __GSOS__
enum {
ATTR_ACCESS = 1,
ATTR_FILETYPE = 2,
ATTR_AUXTYPE = 4,
ATTR_CREATETIME = 8,
ATTR_MODTIME = 16
};
int setfileattr(const char *filename, FileInfoRecGS *info, unsigned flags);
#endif
#ifdef __CONNECTION_H__
int ConnectLoop(char *host, Word port, Connection *connection);
int CloseLoop(Connection *connection);
#endif
#ifdef __url_h__
int do_gopher(const char *url, URLComponents *components);
int do_http(const char *url, URLComponents *components);
#endif
#ifdef __TYPES__
void tiTimeRec2ISO8601(const TimeRecPtr t, char *str);
void tiTimeRec2GMTString(const TimeRecPtr t, char *str);
#endif
#ifdef __Options__
extern struct Options flags;
#endif
#endif

View File

@ -1,4 +1,5 @@
#pragma optimize 79
#pragma noroot
#include "readline2.h"
#include <tcpipx.h>
@ -179,7 +180,7 @@ int ReadLine2(Word ipid, rlBuffer *buffer)
hsize = size + tlen;
h = NewHandle(hsize, ur->uwUserID, attrNoSpec | attrLocked, 0);
if (_toolErr) return tcperrNoResources;
if (_toolErr) return -1; //tcperrNoResources;
buffer->bufferSize = size;
buffer->bufferHandle = h;

208
s16debug.c Normal file
View File

@ -0,0 +1,208 @@
#include "s16debug.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <tcpip.h>
#include <tcpipx.h>
#pragma optimize 79
#pragma noroot
static char buffer[512];
static Word CheckSweet16(void)
{
static Word Sweet16 = -1;
if (Sweet16 == -1)
{
/*
* Apple II tech note 201
*/
word emu_id = 0;
word emu_version = 0;
asm {
// the lda ..,x is to prevent
// the 2nd lda from being optimized out.
lda #0
ldx #0
sep #0x20
sta >0x00c04f,x
lda >0x00c04f,x
sta <emu_id
lda >0x00c04f,x
sta <emu_version
rep #0x20
}
if (emu_id == 0x16 && emu_version >= 0x23) Sweet16 = 1;
else Sweet16 = 0;
}
return Sweet16;
}
void s16_debug_puts(const char *str)
{
if (!CheckSweet16()) return;
asm {
ldx <str+2
ldy <str
cop 0x84
}
}
void s16_debug_dump(const char *bytes, unsigned length)
{
static const char *HexMap = "0123456789abcdef";
if (!CheckSweet16()) return;
while (length)
{
Word i, j, l;
l = 16;
if (l > length) l = length;
memset(buffer, ' ', sizeof(buffer));
for (i = 0, j = 0; i < l; ++i)
{
unsigned x = bytes[i];
buffer[j++] = HexMap[x >> 4];
buffer[j++] = HexMap[x & 0x0f];
j++;
if (i == 7) j++;
buffer[50 + i] = isascii(x) && isprint(x) ? x : '.';
}
buffer[50 + 16 + 1] = 0;
s16_debug_puts(buffer);
length -= l;
bytes += l;
}
}
void s16_debug_printf(const char *format, ...)
{
// need to format the data even if sweet-16 is
// not present in order to clean up the stack.
va_list ap;
va_start(ap, format);
vsnprintf(buffer, sizeof(buffer), format, ap);
va_end(ap);
s16_debug_puts(buffer);
}
//---
void s16_debug_srbuff(const srBuff *sb)
{
if (!CheckSweet16()) return;
/*
s16_debug_printf("%04x %04x %08lx %08lx %08lx %04x %04x %04x",
sb->srState,
sb->srNetworkError,
sb->srSndQueued,
sb->srRcvQueued,
sb->srDestIP,
sb->srDestPort,
sb->srConnectType,
sb->srAcceptCount
);
*/
s16_debug_printf(" srState: $%04x\n", sb->srState);
s16_debug_printf(" srNetworkError: $%04x\n", sb->srNetworkError);
s16_debug_printf(" srSndQueued: $%08lx\n", sb->srSndQueued);
s16_debug_printf(" srRcvQueued: $%08lx\n", sb->srRcvQueued);
s16_debug_printf(" srDestIP: $%08lx\n", sb->srDestIP);
s16_debug_printf(" srDestPort: $%04x\n", sb->srDestPort);
s16_debug_printf(" srConnectType: $%04x\n", sb->srConnectType);
s16_debug_printf(" srAcceptCount: $%04x\n", sb->srAcceptCount);
}
void s16_debug_tcp(unsigned ipid)
{
userRecordPtr ur;
userRecordHandle urh;
if (!CheckSweet16()) return;
urh = TCPIPGetUserRecord(ipid);
if (!urh) return;
ur = *urh;
if (!ur) return;
if (!ur->uwUserID) return;
// -- auto-generated --
s16_debug_printf(" uwUserID: $%04x\n", ur->uwUserID);
s16_debug_printf(" uwDestIP: $%08lx\n", ur->uwDestIP);
s16_debug_printf(" uwDestPort: $%04x\n", ur->uwDestPort);
s16_debug_printf(" uwIP_TOS: $%04x\n", ur->uwIP_TOS);
s16_debug_printf(" uwIP_TTL: $%04x\n", ur->uwIP_TTL);
s16_debug_printf(" uwSourcePort: $%04x\n", ur->uwSourcePort);
s16_debug_printf(" uwLogoutPending: $%04x\n", ur->uwLogoutPending);
s16_debug_printf(" uwICMPQueue: $%08lx\n", ur->uwICMPQueue);
s16_debug_printf(" uwTCPQueue: $%08lx\n", ur->uwTCPQueue);
s16_debug_printf(" uwTCPMaxSendSeg: $%04x\n", ur->uwTCPMaxSendSeg);
s16_debug_printf(" uwTCPMaxReceiveSeg: $%04x\n", ur->uwTCPMaxReceiveSeg);
s16_debug_printf(" uwTCPDataInQ: $%08lx\n", ur->uwTCPDataInQ);
s16_debug_printf(" uwTCPDataIn: $%08lx\n", ur->uwTCPDataIn);
s16_debug_printf(" uwTCPPushInFlag: $%04x\n", ur->uwTCPPushInFlag);
s16_debug_printf(" uwTCPPushInOffset: $%08lx\n", ur->uwTCPPushInOffset);
s16_debug_printf(" uwTCPPushOutFlag: $%04x\n", ur->uwTCPPushOutFlag);
s16_debug_printf(" uwTCPPushOutSEQ: $%08lx\n", ur->uwTCPPushOutSEQ);
s16_debug_printf(" uwTCPDataOut: $%08lx\n", ur->uwTCPDataOut);
s16_debug_printf(" uwSND_UNA: $%08lx\n", ur->uwSND_UNA);
s16_debug_printf(" uwSND_NXT: $%08lx\n", ur->uwSND_NXT);
s16_debug_printf(" uwSND_WND: $%04x\n", ur->uwSND_WND);
s16_debug_printf(" uwSND_UP: $%04x\n", ur->uwSND_UP);
s16_debug_printf(" uwSND_WL1: $%08lx\n", ur->uwSND_WL1);
s16_debug_printf(" uwSND_WL2: $%08lx\n", ur->uwSND_WL2);
s16_debug_printf(" uwISS: $%08lx\n", ur->uwISS);
s16_debug_printf(" uwRCV_NXT: $%08lx\n", ur->uwRCV_NXT);
s16_debug_printf(" uwRCV_WND: $%04x\n", ur->uwRCV_WND);
s16_debug_printf(" uwRCV_UP: $%04x\n", ur->uwRCV_UP);
s16_debug_printf(" uwIRS: $%08lx\n", ur->uwIRS);
s16_debug_printf(" uwTCP_State: $%04x\n", ur->uwTCP_State);
s16_debug_printf(" uwTCP_StateTick: $%08lx\n", ur->uwTCP_StateTick);
s16_debug_printf(" uwTCP_ErrCode: $%04x\n", ur->uwTCP_ErrCode);
s16_debug_printf(" uwTCP_ICMPError: $%04x\n", ur->uwTCP_ICMPError);
s16_debug_printf(" uwTCP_Server: $%04x\n", ur->uwTCP_Server);
s16_debug_printf(" uwTCP_ChildList: $%08lx\n", ur->uwTCP_ChildList);
s16_debug_printf(" uwTCP_ACKPending: $%04x\n", ur->uwTCP_ACKPending);
s16_debug_printf(" uwTCP_ForceFIN: $%04x\n", ur->uwTCP_ForceFIN);
s16_debug_printf(" uwTCP_FINSEQ: $%08lx\n", ur->uwTCP_FINSEQ);
s16_debug_printf(" uwTCP_MyFINACKed: $%04x\n", ur->uwTCP_MyFINACKed);
s16_debug_printf(" uwTCP_Timer: $%08lx\n", ur->uwTCP_Timer);
s16_debug_printf(" uwTCP_TimerState: $%04x\n", ur->uwTCP_TimerState);
s16_debug_printf(" uwTCP_rt_timer: $%04x\n", ur->uwTCP_rt_timer);
s16_debug_printf(" uwTCP_2MSL_timer: $%04x\n", ur->uwTCP_2MSL_timer);
s16_debug_printf(" uwTCP_SaveTTL: $%04x\n", ur->uwTCP_SaveTTL);
s16_debug_printf(" uwTCP_SaveTOS: $%04x\n", ur->uwTCP_SaveTOS);
s16_debug_printf(" uwTCP_TotalIN: $%08lx\n", ur->uwTCP_TotalIN);
s16_debug_printf(" uwTCP_TotalOUT: $%08lx\n", ur->uwTCP_TotalOUT);
s16_debug_printf(" uwUDP_Server: $%04x\n", ur->uwUDP_Server);
s16_debug_printf(" uwUDPQueue: $%08lx\n", ur->uwUDPQueue);
s16_debug_printf(" uwUDPError: $%04x\n", ur->uwUDPError);
s16_debug_printf(" uwUDPErrorTick: $%08lx\n", ur->uwUDPErrorTick);
s16_debug_printf(" uwUDPCount: $%08lx\n", ur->uwUDPCount);
}

14
s16debug.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef __s16_debug_h__
#define __s16_debug_h__
void s16_debug_puts(const char *str);
void s16_debug_printf(const char *format, ...);
void s16_debug_dump(const char *data, unsigned size);
void s16_debug_tcp(unsigned ipid);
#ifdef __TCPIP__
void s16_debug_srbuff(const srBuff *sb);
#endif
#endif

View File

@ -1,4 +1,5 @@
#pragma optimize 79
#pragma noroot
#include <Types.h>
#include "url.h"
@ -23,6 +24,24 @@ void parse_scheme(const char *cp, unsigned size, URLComponents *c)
{
// --- begin auto-generated --
case 0x00:
// dict
if (size == 4
&& (wp[0] | 0x2020) == 0x6964 // 'di'
&& (wp[1] | 0x2020) == 0x7463 // 'ct'
) {
c->schemeType = SCHEME_DICT;
c->portNumber = 2628;
return;
}
// smb
if (size == 3
&& (wp[0] | 0x2020) == 0x6d73 // 'sm'
&& (cp[2] | 0x20) == 0x62 // 'b'
) {
c->schemeType = SCHEME_SMB;
c->portNumber = 445;
return;
}
// ssh
if (size == 3
&& (wp[0] | 0x2020) == 0x7373 // 'ss'
@ -48,6 +67,15 @@ void parse_scheme(const char *cp, unsigned size, URLComponents *c)
break;
case 0x02:
// file
if (size == 4
&& (wp[0] | 0x2020) == 0x6966 // 'fi'
&& (wp[1] | 0x2020) == 0x656c // 'le'
) {
c->schemeType = SCHEME_FILE;
c->portNumber = 0;
return;
}
// afp
if (size == 3
&& (wp[0] | 0x2020) == 0x6661 // 'af'
@ -67,15 +95,6 @@ void parse_scheme(const char *cp, unsigned size, URLComponents *c)
c->portNumber = 23;
return;
}
// file
if (size == 4
&& (wp[0] | 0x2020) == 0x6966 // 'fi'
&& (wp[1] | 0x2020) == 0x656c // 'le'
) {
c->schemeType = SCHEME_FILE;
c->portNumber = 0;
return;
}
break;
case 0x05:
@ -127,6 +146,15 @@ void parse_scheme(const char *cp, unsigned size, URLComponents *c)
break;
case 0x0d:
// nfs
if (size == 3
&& (wp[0] | 0x2020) == 0x666e // 'nf'
&& (cp[2] | 0x20) == 0x73 // 's'
) {
c->schemeType = SCHEME_NFS;
c->portNumber = 2049;
return;
}
// https
if (size == 5
&& (wp[0] | 0x2020) == 0x7468 // 'ht'
@ -137,15 +165,6 @@ void parse_scheme(const char *cp, unsigned size, URLComponents *c)
c->portNumber = 443;
return;
}
// nfs
if (size == 3
&& (wp[0] | 0x2020) == 0x666e // 'nf'
&& (cp[2] | 0x20) == 0x73 // 's'
) {
c->schemeType = SCHEME_NFS;
c->portNumber = 2049;
return;
}
break;
// --- end auto-generated --

View File

@ -1,5 +1,6 @@
%%
#pragma optimize 79
#pragma noroot
#include <Types.h>
#include "url.h"
@ -97,3 +98,16 @@ void parse_scheme(const char *cp, unsigned size, URLComponents *c)
c->portNumber = 2049;
return;
.
'dict' ->
c->schemeType = SCHEME_DICT;
c->portNumber = 2628;
return;
.
'smb' ->
c->schemeType = SCHEME_SMB;
c->portNumber = 445;
return;
.

View File

@ -5,54 +5,35 @@
#include <stdlib.h>
#include <gno/gno.h>
extern int parse_ftype(const char *cp, Word size, Word *ftype, Word *atype);
#include "prototypes.h"
int setfiletype(const char *filename)
int setfileattr(const char *filename, FileInfoRecGS *info, unsigned flags)
{
int pd;
int i;
Word ftype;
Word atype;
int rv;
FileInfoRecGS info;
// find the extension in the filename.
pd = -1;
for (i = 0; ; ++i)
{
char c;
c = filename[i];
if (c == 0) break;
if (c == '.') pd = i;
}
// pd == position of final .
// i == strlen
if (pd == -1) return 0;
if (pd + 1 >= i) return 0;
pd++; // skip past it...
if (!parse_ftype(filename + pd, i - pd, &ftype, &atype))
return 0;
info.pCount = 4;
info.pathname = (GSString255Ptr)__C2GSMALLOC(filename);
info.access = 0xe3;
info.auxType = atype;
info.fileType = ftype;
//GetFileInfoGS(&info);
//if (_toolErr) return 0;
SetFileInfoGS(&info);
rv = _toolErr;
if (_toolErr)
Word rv;
FileInfoRecGS tmp;
if (!info) return 0;
if (!flags) return 1;
tmp.pCount = 7;
tmp.pathname = (GSString255Ptr)__C2GSMALLOC(filename);
if (!tmp.pathname) return 0;
GetFileInfoGS(&tmp);
rv = _toolErr;
if (!_toolErr)
{
if (flags & ATTR_ACCESS) tmp.access = info->access;
if (flags & ATTR_FILETYPE) tmp.fileType = info->fileType;
if (flags & ATTR_AUXTYPE) tmp.auxType = info->auxType;
if (flags & ATTR_CREATETIME) tmp.createDateTime = info->createDateTime;
if (flags & ATTR_MODTIME) tmp.modDateTime = info->modDateTime;
SetFileInfoGS(&tmp);
rv = _toolErr;
}
free (tmp.pathname);
free(info.pathname);
return rv ? 0 : 1;
}
}

132
time.c Normal file
View File

@ -0,0 +1,132 @@
#pragma noroot
#pragma optimize -1
#pragma lint -1
#pragma debug 0x8000
#include <timetool.h>
#include <misctool.h>
#include <intmath.h>
/*
* From Silver Platter.
*
*/
// yyyy-mm-ddThh:mm:ssZ
void tiTimeRec2ISO8601(const TimeRecPtr t, char *str)
{
LongWord secs;
tiPrefRec tiPrefs;
TimeRec tr;
tiPrefs.pCount = 3;
tiGetTimePrefs(&tiPrefs);
secs = ConvSeconds(TimeRec2Secs, 0, (Pointer)t);
secs += tiPrefs.secOffset;
ConvSeconds(secs2TimeRec, secs, (Pointer)&tr);
str[0] = 20;
// yyyy-
Int2Dec(tr.year + 1900, &str[1], 4, 0);
str[5] = '-';
// mm-
Int2Dec(tr.month + 1, &str[6], 2, 0);
str[6] |= 0x10; // convert ' ' -> '0'
str[8] = '-';
// ddT
Int2Dec(tr.day + 1, &str[9], 2, 0);
str[9] |= 0x10; // convert ' ' -> '0'
str[11] = 'T';
// hh:
Int2Dec(tr.hour, &str[12], 2, 0);
str[12] |= 0x10; // convert ' ' -> '0'
str[14] = ':';
// mm:
Int2Dec(tr.minute, &str[15], 2, 0);
str[15] |= 0x10; // convert ' ' -> '0'
str[17] = ':';
// ss:
Int2Dec(tr.second, &str[18], 2, 0);
str[18] |= 0x10; // convert ' ' -> '0'
str[20] = 'Z';
}
void tiTimeRec2GMTString(const TimeRecPtr t, char *str)
{
static const char weekday[] = "Sun,Mon,Tue,Wed,Thu,Fri,Sat,";
static const char month[] = "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ";
int i;
LongWord secs;
tiPrefRec tiPrefs;
TimeRec tr;
tiPrefs.pCount = 3;
tiGetTimePrefs(&tiPrefs);
secs = ConvSeconds(TimeRec2Secs, 0, (Pointer)t);
secs += tiPrefs.secOffset;
#if 0
//add daylight savings time...
if (ReadBParam(0x5e) & 0x02 == 0) secs += 3600;
#endif
ConvSeconds(secs2TimeRec, secs, (Pointer)&tr);
str[0] = 29;
i = (tr.weekDay - 1) << 2;
// Day of week
str[1] = weekday[i++];
str[2] = weekday[i++];
str[3] = weekday[i++];
str[4] = weekday[i++];
str[5] = ' ';
// day
Int2Dec(tr.day + 1, &str[6], 2, 0);
str[6] |= 0x10;
str[8] = ' ';
i = tr.month << 2;
str[9] = month[i++];
str[10] = month[i++];
str[11] = month[i++];
str[12] = month[i++];
// year
Int2Dec(tr.year + 1900, &str[13], 4, 0);
str[17] = ' ';
Int2Dec(tr.hour, &str[18], 2, 0);
str[18] |= 0x10;
str[20] = ':';
Int2Dec(tr.minute, &str[21], 2, 0);
str[21] |= 0x10;
str[23] = ':';
Int2Dec(tr.second, &str[24], 2, 0);
str[24] |= 0x10;
str[26] = ' ';
str[27] = 'G';
str[28] = 'M';
str[29] = 'T';
}

View File

@ -1,4 +1,4 @@
#!/usr/binenv ruby -w
#!/usr/bin/env ruby -w
def dump_rules(rules)
@ -115,7 +115,7 @@ ARGV.each {|filename|
if !rule
next if line == ''
if line =~ /^'([a-zA-Z0-9.+_-]+)'\s*->$/
if line =~ /^'([a-zA-Z0-9.+_\/-]+)'\s*->$/
rule = $1;
raise "duplicate rule: #{rule}" if rules[rule]
next

37
url.c
View File

@ -4,10 +4,9 @@
#include <stdlib.h>
#include <ctype.h>
#include "url.h"
enum {
kScheme,
kUser,
@ -29,6 +28,35 @@ extern void parse_scheme(const char *cp, unsigned size, URLComponents *c);
*
*/
char *URLComponentGetCMalloc(
const char *url,
URLComponents *components,
int type)
{
URLRange *rangePtr;
URLRange r;
char *tmp;
if (!url || !components) return NULL;
if (type < URLComponentScheme || type > URLComponentPathAndQuery)
return NULL;
rangePtr = &components->scheme;
r = rangePtr[type];
if (!r.length) return NULL;
tmp = (char *)malloc(r.length + 1);
if (!tmp) return NULL;
memcpy(tmp, url + r.location, r.length);
tmp[r.length] = 0;
return tmp;
}
int URLComponentGetC(const char *url, URLComponents *components, int type, char *dest)
{
URLRange *rangePtr;
@ -145,7 +173,7 @@ int ParseURL(const char *url, int length, struct URLComponents *components)
components->scheme = range;
parseScheme(url, i, components);
parse_scheme(url, i, components);
++i; // skip the ':'
}
@ -415,7 +443,6 @@ int ParseURL(const char *url, int length, struct URLComponents *components)
components->portNumber = p;
}
#if 0
// path and query.
// path;params?query
range = components->path;
@ -423,12 +450,12 @@ int ParseURL(const char *url, int length, struct URLComponents *components)
{
if (components->params.length)
range.length += components->params.length + 1;
if (components->query.length)
range.length += components->query.length + 1;
components->pathAndQuery = range;
}
#endif
return 1;

16
url.h
View File

@ -2,20 +2,22 @@
#define __url_h__
enum {
SCHEME_UNKNOWN = -1,
SCHEME_UNKNOWN = 0xffff,
SCHEME_NONE = 0,
SCHEME_FILE = 0xfffe,
SCHEME_AFP = 548,
SCHEME_DICT = 2628,
SCHEME_FTP = 21,
SCHEME_GOPHER = 70,
SCHEME_HTTP = 80,
SCHEME_HTTPS = 443,
SCHEME_NFS = 2049,
SCHEME_NNTP = 119,
SCHEME_TELNET = 23,
SCHEME_SSH = 22,
SCHEME_SFTP = 115,
SCHEME_AFP = 548,
SCHEME_NFS = 2049
SCHEME_SMB = 445,
SCHEME_SSH = 22,
SCHEME_TELNET = 23
};
typedef struct URLRange {
@ -57,6 +59,8 @@ typedef struct URLComponents {
int ParseURL(const char *url, int length, URLComponents *components);
char *URLComponentGetCMalloc(const char *url, URLComponents *, int);
int URLComponentGet(const char *url, URLComponents *, int, char *);
int URLComponentGetC(const char *url, URLComponents *, int, char *);
//int URLComponentGetGS(const char *url, URLComponents *, int, GSString255Ptr);

View File

@ -46,6 +46,9 @@ void test(const char *url)
URLComponentGetC(url, &data, URLComponentFragment, buffer);
printf(" fragment: %s\n", buffer);
URLComponentGetC(url, &data, URLComponentPathAndQuery, buffer);
printf(" path+query: %s\n", buffer);
free(buffer);