Compare commits
56 Commits
Author | SHA1 | Date |
---|---|---|
Kelvin Sherlock | 103048b2db | |
Kelvin Sherlock | 9457120327 | |
Kelvin Sherlock | fad3fc48ea | |
Kelvin Sherlock | 09ebb6c842 | |
Kelvin Sherlock | aa6bcc05d3 | |
Kelvin Sherlock | b7e90885aa | |
Kelvin Sherlock | cd55a2de3b | |
Kelvin Sherlock | 58df66cc71 | |
Kelvin Sherlock | 86f0933ec7 | |
Kelvin Sherlock | 4e96ff9651 | |
Kelvin Sherlock | b8cc6ee43a | |
Kelvin Sherlock | 6c0594c255 | |
Kelvin Sherlock | 0497c1d4dd | |
Kelvin Sherlock | 8f0eb50d12 | |
Kelvin Sherlock | fff9954b70 | |
Kelvin Sherlock | ed6c2fc7f7 | |
Kelvin Sherlock | f67e5f11a8 | |
Kelvin Sherlock | 9f570ffc82 | |
Kelvin Sherlock | 394aa110c7 | |
Kelvin Sherlock | d1436d45df | |
Kelvin Sherlock | ab26d38790 | |
Kelvin Sherlock | 1d8bed53a7 | |
Kelvin Sherlock | 8b771de8d2 | |
Kelvin Sherlock | e7af668c67 | |
Kelvin Sherlock | caa54a0f3e | |
Kelvin Sherlock | 7d31bafffc | |
Kelvin Sherlock | b7420ca623 | |
Kelvin Sherlock | a8779193d6 | |
Kelvin Sherlock | 0ad36adf5e | |
Kelvin Sherlock | ff655c687e | |
Kelvin Sherlock | 3c860adce9 | |
Kelvin Sherlock | 3f65a4ed2e | |
Kelvin Sherlock | b17f0fa448 | |
Kelvin Sherlock | 29d7dd0181 | |
Kelvin Sherlock | f92f901742 | |
Kelvin Sherlock | 2b310a2161 | |
Kelvin Sherlock | 7287f069ba | |
Kelvin Sherlock | 681fb057ba | |
Kelvin Sherlock | bc5ac50f5e | |
Kelvin Sherlock | 62754f7431 | |
Kelvin Sherlock | 2903af5f60 | |
Kelvin Sherlock | 67ed428493 | |
Kelvin Sherlock | 933308432d | |
Kelvin Sherlock | f4c23d7d87 | |
Kelvin Sherlock | 5abb3cae53 | |
Kelvin Sherlock | aabed2e462 | |
Kelvin Sherlock | f4d9a6f8d4 | |
Kelvin Sherlock | 86b60c74e9 | |
Kelvin Sherlock | 20ad89176f | |
Kelvin Sherlock | 4dc81d82af | |
Kelvin Sherlock | c5253c6ce3 | |
Kelvin Sherlock | 39bc3dde14 | |
Kelvin Sherlock | a2fcb6ee11 | |
Kelvin Sherlock | 9b06d877b1 | |
Kelvin Sherlock | afe71ebe02 | |
Kelvin Sherlock | 9aca19b10f |
|
@ -0,0 +1 @@
|
|||
.DS_Store
|
|
@ -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.
|
7
TODO.txt
7
TODO.txt
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
72
connection.c
72
connection.c
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
3
data.c
|
@ -1,3 +1,6 @@
|
|||
#pragma optimize 79
|
||||
#pragma noroot
|
||||
|
||||
#include "Data.h"
|
||||
#include <Memory.h>
|
||||
#include "fast.memory.h"
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
#pragma optimize 79
|
||||
#pragma noroot
|
||||
#pragma debug 0x8000
|
||||
#pragma lint -1
|
||||
|
||||
#include "dictionary.h"
|
||||
#include <memory.h>
|
||||
#include <string.h>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
52
ftype.c
|
@ -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:
|
||||
|
|
40
ftype.txt
40
ftype.txt
|
@ -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;
|
||||
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef __orca__
|
||||
#define __orca__
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define fsetbinary(f) (f->_flag &= ~_IOTEXT)
|
||||
|
||||
|
||||
#endif
|
|
@ -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
486
gopher.c
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
29
makefile.mk
29
makefile.mk
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
.
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
@ -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
|
55
scheme.c
55
scheme.c
|
@ -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 --
|
||||
|
|
14
scheme.txt
14
scheme.txt
|
@ -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;
|
||||
.
|
||||
|
||||
|
|
75
setftype.c
75
setftype.c
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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';
|
||||
|
||||
}
|
|
@ -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
37
url.c
|
@ -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
16
url.h
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue