mirror of
https://github.com/ksherlock/gopher.git
synced 2025-01-20 00:30:09 +00:00
initial version
This commit is contained in:
commit
f305f78412
171
connect.c
Normal file
171
connect.c
Normal file
@ -0,0 +1,171 @@
|
||||
#include "Connect.h"
|
||||
|
||||
//static char pstring[256];
|
||||
|
||||
|
||||
static Word LoginAndOpen(ConnectBuffer *buffer)
|
||||
{
|
||||
Word ipid;
|
||||
Word terr;
|
||||
|
||||
|
||||
ipid = TCPIPLogin(
|
||||
buffer->memID,
|
||||
buffer->dnr.DNRIPaddress,
|
||||
buffer->port,
|
||||
0x0000, 0x0040);
|
||||
|
||||
if (_toolErr)
|
||||
{
|
||||
buffer->state = ConnectStateError;
|
||||
return -1;
|
||||
}
|
||||
|
||||
terr = TCPIPOpenTCP(ipid);
|
||||
if (_toolErr || terr)
|
||||
{
|
||||
TCPIPLogout(ipid);
|
||||
buffer->state = ConnectStateError;
|
||||
buffer->terr = terr;
|
||||
buffer->ipid = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
buffer->ipid = ipid;
|
||||
buffer->state = ConnectStateConnecting;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Word ConnectionPoll(ConnectBuffer *buffer)
|
||||
{
|
||||
Word state;
|
||||
if (!buffer) return -1;
|
||||
state = buffer->state;
|
||||
|
||||
if (state == 0) return -1;
|
||||
if (state == ConnectStateConnected) return 1;
|
||||
if (state == ConnectStateDisconnected) return 1;
|
||||
if (state == ConnectStateError) return -1;
|
||||
|
||||
TCPIPPoll();
|
||||
|
||||
if (state == ConnectStateDNR)
|
||||
{
|
||||
if (buffer->dnr.DNRstatus == DNR_OK)
|
||||
{
|
||||
return LoginAndOpen(buffer);
|
||||
}
|
||||
else if (buffer->dnr.DNRstatus != DNR_Pending)
|
||||
{
|
||||
buffer->state = ConnectStateError;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (state == ConnectStateConnecting || state == ConnectStateDisconnecting)
|
||||
{
|
||||
Word terr;
|
||||
static srBuff sr;
|
||||
|
||||
terr = TCPIPStatusTCP(buffer->ipid, &sr);
|
||||
|
||||
if (state == ConnectStateDisconnecting)
|
||||
{
|
||||
// these are not errors.
|
||||
if (terr == tcperrConClosing || terr == tcperrClosing)
|
||||
terr = tcperrOK;
|
||||
}
|
||||
|
||||
if (terr || _toolErr)
|
||||
{
|
||||
//CloseAndLogout(buffer);
|
||||
|
||||
TCPIPCloseTCP(buffer->ipid);
|
||||
TCPIPLogout(buffer->ipid);
|
||||
buffer->ipid = 0;
|
||||
buffer->state = ConnectStateError;
|
||||
buffer->terr = terr;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sr.srState == TCPSESTABLISHED) // && state == ConnectStateConnecting)
|
||||
{
|
||||
buffer->state = ConnectStateConnected;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (sr.srState == TCPSCLOSED || sr.srState == TCPSTIMEWAIT)
|
||||
{
|
||||
TCPIPLogout(buffer->ipid);
|
||||
buffer->ipid = 0;
|
||||
buffer->state = ConnectStateDisconnected;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Word ConnectionOpen(ConnectBuffer *buffer, const char *host, Word port)
|
||||
{
|
||||
buffer->state = 0;
|
||||
buffer->ipid = 0;
|
||||
buffer->terr = 0;
|
||||
buffer->port = port;
|
||||
|
||||
// 1. check if we need to do DNR.
|
||||
if (TCPIPValidateIPString(host))
|
||||
{
|
||||
cvtRec cvt;
|
||||
TCPIPConvertIPToHex(&cvt, host);
|
||||
buffer->dnr.DNRIPaddress = cvt.cvtIPAddress;
|
||||
buffer->dnr.DNRstatus = DNR_OK; // fake it.
|
||||
|
||||
return LoginAndOpen(buffer);
|
||||
}
|
||||
// do dnr.
|
||||
TCPIPDNRNameToIP(host, &buffer->dnr);
|
||||
if (_toolErr)
|
||||
{
|
||||
buffer->state = ConnectStateError;
|
||||
return -1;
|
||||
}
|
||||
buffer->state = ConnectStateDNR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ConnectionInit(ConnectBuffer *buffer, Word memID)
|
||||
{
|
||||
buffer->memID = memID;
|
||||
buffer->ipid = 0;
|
||||
buffer->terr = 0;
|
||||
buffer->state = 0;
|
||||
buffer->port = 0;
|
||||
buffer->dnr.DNRstatus = 0;
|
||||
buffer->dnr.DNRIPaddress = 0;
|
||||
}
|
||||
|
||||
Word ConnectionClose(ConnectBuffer *buffer)
|
||||
{
|
||||
Word state = buffer->state;
|
||||
|
||||
// todo -- how do you close if not yet connected?
|
||||
if (state == ConnectStateConnected)
|
||||
{
|
||||
buffer->state = ConnectStateDisconnecting;
|
||||
buffer->terr = TCPIPCloseTCP(buffer->ipid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (state == ConnectStateDNR)
|
||||
{
|
||||
TCPIPCancelDNR(&buffer->dnr);
|
||||
buffer->state = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
206
gopher.c
Normal file
206
gopher.c
Normal file
@ -0,0 +1,206 @@
|
||||
#include <Locator.h>
|
||||
#include <Memory.h>
|
||||
|
||||
#include <tcpip.h>
|
||||
|
||||
|
||||
//#include "url.h"
|
||||
#include "Connect.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/*
|
||||
* connect gopher.floodgap.com:70
|
||||
* send path
|
||||
* read output.
|
||||
* text -- ends with . <CR><LF>
|
||||
* bin -- ends when connection is closed.
|
||||
*/
|
||||
|
||||
// startup/shutdown flags.
|
||||
enum {
|
||||
kLoaded = 1,
|
||||
kStarted = 2,
|
||||
kConnected = 4
|
||||
};
|
||||
|
||||
Word StartUp(void)
|
||||
{
|
||||
word status;
|
||||
word flags = 0;
|
||||
|
||||
status = TCPIPStatus();
|
||||
if (_toolErr)
|
||||
{
|
||||
LoadOneTool(54,0x0200);
|
||||
if (_toolErr) return -1;
|
||||
|
||||
status = 0;
|
||||
flags |= kLoaded;
|
||||
}
|
||||
|
||||
if (!status)
|
||||
{
|
||||
TCPIPStartUp();
|
||||
if (_toolErr) return -1;
|
||||
flags |= kStarted;
|
||||
}
|
||||
|
||||
status = TCPIPGetConnectStatus();
|
||||
if (!status)
|
||||
{
|
||||
TCPIPConnect(NULL);
|
||||
flags |= kConnected;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
void ShutDown(word flags)
|
||||
{
|
||||
if (flags & kConnected)
|
||||
{
|
||||
TCPIPDisconnect(false, NULL);
|
||||
if (_toolErr) return;
|
||||
}
|
||||
if (flags & kStarted)
|
||||
{
|
||||
TCPIPShutDown();
|
||||
if (_toolErr) return;
|
||||
}
|
||||
if (flags & kLoaded)
|
||||
{
|
||||
UnloadOneTool(54);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
void do_url(const char *url)
|
||||
{
|
||||
URLComponents components;
|
||||
ConnectBuffer buffer;
|
||||
char *host;
|
||||
|
||||
if (ParseURL(url, strlen(url), &components))
|
||||
{
|
||||
fprintf(stderr, "Invalid URL: %s\n", url);
|
||||
return;
|
||||
}
|
||||
if (!url.host.length)
|
||||
{
|
||||
fprintf(stderr, "No host\n");
|
||||
return;
|
||||
}
|
||||
if (!components.portNumber) components.portNumber = 70;
|
||||
host = malloc(url.host.length + 1);
|
||||
URLComponentGetCString(url, &components, host, URLComponentHost);
|
||||
|
||||
ConnectInit(&buffer, myID);
|
||||
|
||||
|
||||
ConnectCString(&buffer, host, components.portNumber);
|
||||
|
||||
if (components.path.length)
|
||||
{
|
||||
rv = TCPIPWriteTCP(buffer.ipid,
|
||||
url + components.path.location,
|
||||
components.path.length,
|
||||
0, 0);
|
||||
}
|
||||
rv = TCPIPWriteTCP(buffer.ipid, "\r\n", 2, 1, 0);
|
||||
|
||||
|
||||
while (!ConnectionPoll(&buffer)) ;
|
||||
if (buffer.state == ConnectionStateError)
|
||||
{
|
||||
fprintf(stderr, "Unable to open host: %s:%u\n", host, components.portNumber);
|
||||
free(host);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
ConnectionClose(&buffer);
|
||||
|
||||
while (!ConnectionPoll(&buffer)) ; // wait for it to close.
|
||||
|
||||
free (host);
|
||||
}
|
||||
|
||||
#endif
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
Handle h;
|
||||
ConnectBuffer buffer;
|
||||
|
||||
int i;
|
||||
Word rv;
|
||||
Word flags;
|
||||
|
||||
// getopt for -b binary
|
||||
|
||||
#if 0
|
||||
for (i = 1; i < argc; ++i)
|
||||
{
|
||||
do_url(argv[i]);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
flags = StartUp();
|
||||
fprintf(stdout, "flags: %u\n", flags);
|
||||
|
||||
ConnectionInit(&buffer, MMStartUp());
|
||||
|
||||
rv = ConnectionOpen(&buffer, "\pgopher.floodgap.com", 70);
|
||||
while (!rv)
|
||||
{
|
||||
rv = ConnectionPoll(&buffer);
|
||||
}
|
||||
if (buffer.state == ConnectStateConnected)
|
||||
{
|
||||
fprintf(stdout, "Connected!\n");
|
||||
}
|
||||
|
||||
fprintf(stdout, "%x %x %x\n", rv, buffer.state, buffer.terr);
|
||||
|
||||
rv = TCPIPWriteTCP(buffer.ipid, "\r\n", 2, 1, 0);
|
||||
if (rv)
|
||||
{
|
||||
fprintf(stdout, "TCPIPWriteTCP: %x\n", rv);
|
||||
}
|
||||
|
||||
for(;;)
|
||||
{
|
||||
rrBuff rb;
|
||||
//rlrBuff rb;
|
||||
Handle h;
|
||||
Word count;
|
||||
|
||||
TCPIPPoll();
|
||||
rv = TCPIPReadTCP(buffer.ipid, 2, NULL, 4096, &rb);
|
||||
|
||||
//rv = TCPIPReadLineTCP(buffer.ipid, "\p\r\n", 2, NULL, 4096, &rb);
|
||||
|
||||
if (rv) break;
|
||||
h = rb.rrBuffHandle;
|
||||
count = rb.rrBuffCount;
|
||||
|
||||
if (!h) continue;
|
||||
|
||||
HLock(h);
|
||||
fwrite(*h, count, 1, stdout);
|
||||
fputs("", stdout);
|
||||
DisposeHandle(h);
|
||||
}
|
||||
|
||||
|
||||
rv = ConnectionClose(&buffer);
|
||||
while (!rv)
|
||||
{
|
||||
rv = ConnectionPoll(&buffer);
|
||||
}
|
||||
fprintf(stdout, "%x %x %x\n", rv, buffer.state, buffer.terr);
|
||||
|
||||
ShutDown(flags);
|
||||
|
||||
}
|
||||
|
519
url.c
Normal file
519
url.c
Normal file
@ -0,0 +1,519 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
|
||||
#include "url.h"
|
||||
|
||||
enum {
|
||||
kScheme,
|
||||
kUser,
|
||||
kPassword,
|
||||
kHost,
|
||||
kPort,
|
||||
kPath,
|
||||
kParams,
|
||||
kQuery,
|
||||
kFragment
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* scheme://username:password@domain:port/path;parms?query_string#fragment_id
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
int URLGetComponentCString(const char *url, struct URLComponents *components, int type, char *dest)
|
||||
{
|
||||
URLRange *rangePtr;
|
||||
URLRange r;
|
||||
|
||||
if (!url || !components) return -1;
|
||||
|
||||
|
||||
if (type < URLComponentScheme || type > URLComponentPathAndQuery) return -1;
|
||||
|
||||
rangePtr = &components->scheme;
|
||||
|
||||
r = rangePtr[type];
|
||||
|
||||
if (!dest) return r.length;
|
||||
else
|
||||
{
|
||||
if (r.length)
|
||||
{
|
||||
memcpy(dest, url + r.location, r.length);
|
||||
}
|
||||
dest[r.length] = 0;
|
||||
|
||||
return r.length;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
int schemeType(const char *string)
|
||||
{
|
||||
if (!string || !*string) return SCHEME_NONE;
|
||||
|
||||
static struct {
|
||||
int length;
|
||||
const char *data
|
||||
} table[] = {
|
||||
{ 4, "file:" },
|
||||
{ 3, "ftp:" },
|
||||
{ 6, "gopher:" },
|
||||
{ 4, "http:" },
|
||||
{ 5, "https:" },
|
||||
{ 6, "mailto:" },
|
||||
{ 4, "news:" },
|
||||
{ 4, "nntp:" },
|
||||
{ 6, "telnet:" }
|
||||
};
|
||||
|
||||
switch(*string)
|
||||
{
|
||||
case 'f':
|
||||
// ftp, file
|
||||
if (!strcmp(string, "file")) return SCHEME_FILE;
|
||||
if (!strcmp(string, "ftp")) return SCHEME_FTP;
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
// gopher
|
||||
if (!strcmp(string, "gopher")) return SCHEME_GOPHER;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
// http, https
|
||||
if (!strcmp(string, "https")) return SCHEME_HTTPS;
|
||||
if (!strcmp(string, "http")) return SCHEME_HTTP;
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
// mailto
|
||||
if (!strcmp(string, "mailto")) return SCHEME_MAILTO;
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
// news, nntp
|
||||
if (!strcmp(string, "news")) return SCHEME_NEWS;
|
||||
if (!strcmp(string, "nntp")) return SCHEME_NNTP;
|
||||
break;
|
||||
|
||||
case 't':
|
||||
// telnet
|
||||
if (!strcmp(string, "telnet")) return SCHEME_TELNET;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return SCHEME_UNKNOWN;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int ParseURL(const char *url, int length, struct URLComponents *components)
|
||||
{
|
||||
char c;
|
||||
int state;
|
||||
int hasScheme;
|
||||
int hasAt;
|
||||
|
||||
int i, l;
|
||||
|
||||
URLRange range = { 0, 0 };
|
||||
URLRange *rangePtr;
|
||||
|
||||
|
||||
if (!components || !url) return 0;
|
||||
|
||||
rangePtr = &components->scheme;
|
||||
memset(components, 0, sizeof(URLComponents));
|
||||
|
||||
state = kScheme;
|
||||
|
||||
// 1 check for a scheme.
|
||||
// [A-Za-z0-9.+-]+ ':'
|
||||
|
||||
|
||||
hasScheme = 0;
|
||||
hasAt = 0;
|
||||
range.location = 0;
|
||||
|
||||
for(i = 0; i < length; ++i)
|
||||
{
|
||||
c = url[i];
|
||||
|
||||
if (c == ':')
|
||||
{
|
||||
hasScheme = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (c >= 'a' && c <= 'z') continue;
|
||||
if (c >= 'A' && c <= 'Z') continue;
|
||||
if (c >= '0' && c <= '9') continue;
|
||||
|
||||
//if (isalnum(c)) continue;
|
||||
if (c == '.') continue;
|
||||
if (c == '+') continue;
|
||||
if (c == '-') continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (hasScheme)
|
||||
{
|
||||
if (i == 0) return 0;
|
||||
range.length = i;
|
||||
|
||||
components->scheme = range;
|
||||
++i; // skip the ':'
|
||||
}
|
||||
else
|
||||
{
|
||||
state = kPath;
|
||||
i = 0;
|
||||
}
|
||||
|
||||
// 2. check for //
|
||||
// //user:password@domain:port/
|
||||
// otherwise, it's a path
|
||||
|
||||
if (strncmp(url + i, "//", 2) == 0)
|
||||
{
|
||||
state = kUser;
|
||||
i += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = kPath;
|
||||
}
|
||||
|
||||
//range = (URLRange){ i, 0 };
|
||||
range.location = i; range.length = 0;
|
||||
|
||||
for( ; i < length; ++i)
|
||||
{
|
||||
c = url[i];
|
||||
|
||||
if ((c < 32) || (c & 0x80))
|
||||
{
|
||||
// goto invalid_char;
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch(c)
|
||||
{
|
||||
// illegal chars.
|
||||
// % # not allowed but allowed.
|
||||
|
||||
case ' ':
|
||||
case '"':
|
||||
case '<':
|
||||
case '>':
|
||||
case '[':
|
||||
case '\\':
|
||||
case ']':
|
||||
case '^':
|
||||
case '`':
|
||||
case '{':
|
||||
case '|':
|
||||
case '}':
|
||||
case 0x7f:
|
||||
// goto invalid_char;
|
||||
return 0;
|
||||
break;
|
||||
|
||||
|
||||
case ':':
|
||||
// username -> password
|
||||
// domain -> port
|
||||
// password -> error
|
||||
// port -> error
|
||||
// all others, no transition.
|
||||
switch(state)
|
||||
{
|
||||
case kUser:
|
||||
case kHost:
|
||||
l = i - range.location;
|
||||
if (l > 0)
|
||||
{
|
||||
range.length = l;
|
||||
rangePtr[state] = range;
|
||||
}
|
||||
//range = (URLRange){ i + 1 , 0 };
|
||||
range.location = i + 1; range.length = 0;
|
||||
|
||||
state++;
|
||||
break;
|
||||
|
||||
|
||||
case kPassword:
|
||||
case kPort:
|
||||
return 0;
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case '@':
|
||||
// username -> domain
|
||||
// password -> domain
|
||||
// all others no transition.
|
||||
// path -> error?
|
||||
|
||||
switch(state)
|
||||
{
|
||||
case kUser:
|
||||
case kPassword:
|
||||
hasAt = 1;
|
||||
l = i - range.location;
|
||||
if (l > 0)
|
||||
{
|
||||
range.length = l;
|
||||
rangePtr[state] = range;
|
||||
}
|
||||
//range = (URLRange){ i + 1 , 0 };
|
||||
range.location = i + 1; range.length = 0;
|
||||
|
||||
state = kHost;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case '/':
|
||||
// domain -> path
|
||||
// port -> path
|
||||
|
||||
// (must be switched to domain:port)
|
||||
// username -> path
|
||||
// password -> path
|
||||
// all others, no transition.
|
||||
|
||||
// '/' is part of the filename.
|
||||
switch (state)
|
||||
{
|
||||
case kUser:
|
||||
hasAt = 1; // close enough
|
||||
state = kHost; // username was actually the domain.
|
||||
// pass through.
|
||||
case kPassword: // need to switch to domain:port later.
|
||||
case kHost:
|
||||
case kPort:
|
||||
l = i - range.location;
|
||||
if (l > 0)
|
||||
{
|
||||
range.length = l;
|
||||
rangePtr[state] = range;
|
||||
}
|
||||
|
||||
//range = (URLRange){ i, 0 }; // '/' is part of path.
|
||||
range.location = i; range.length = 0;
|
||||
|
||||
state = kPath;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ';':
|
||||
// path -> param
|
||||
// all others, no transition.
|
||||
|
||||
if (state == kPath)
|
||||
{
|
||||
l = i - range.location;
|
||||
if (l > 0)
|
||||
{
|
||||
range.length = l;
|
||||
rangePtr[state] = range;
|
||||
}
|
||||
//range = (URLRange){ i + 1 , 0 };
|
||||
range.location = i + 1; range.length = 0;
|
||||
state = kParams;
|
||||
}
|
||||
break;
|
||||
|
||||
case '?':
|
||||
// path -> query
|
||||
// param -> query
|
||||
// all others, no transition.
|
||||
|
||||
if (state == kPath || state == kParams)
|
||||
{
|
||||
l = i - range.location;
|
||||
if (l > 0)
|
||||
{
|
||||
range.length = l;
|
||||
rangePtr[state] = range;
|
||||
}
|
||||
//range = (URLRange){ i + 1 , 0 };
|
||||
range.location = i + 1; range.length = 0;
|
||||
|
||||
state = kQuery;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case '#':
|
||||
// fragment -> no transition
|
||||
// everything else transitions to a fragment.
|
||||
// can immediately end parsing since we know the length...
|
||||
|
||||
l = i - range.location;
|
||||
if (l > 0)
|
||||
{
|
||||
range.length = l;
|
||||
rangePtr[state] = range;
|
||||
}
|
||||
|
||||
state = kFragment;
|
||||
range.location = i + 1; range.length = 0;
|
||||
|
||||
i = length; // break from loop.
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
// finish up the last portion.
|
||||
if (state)
|
||||
{
|
||||
l = i - range.location;
|
||||
if (l > 0)
|
||||
{
|
||||
range.length = l;
|
||||
rangePtr[state] = range;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!hasAt)
|
||||
{
|
||||
// user:name was actually domain:port
|
||||
components->host = components->user;
|
||||
components->port = components->password;
|
||||
|
||||
components->user.location = 0;
|
||||
components->user.length = 0;
|
||||
components->password.location = 0;
|
||||
components->password.length = 0;
|
||||
}
|
||||
|
||||
// port number conversion.
|
||||
if (components->port.location)
|
||||
{
|
||||
const char *tmp = url + components->port.location;
|
||||
int p = 0;
|
||||
|
||||
l = components->port.length;
|
||||
|
||||
|
||||
for (i = 0; i < l; ++i)
|
||||
{
|
||||
c = tmp[i];
|
||||
|
||||
if ((c < '0') || (c > '9'))
|
||||
{
|
||||
p = 0;
|
||||
break;
|
||||
}
|
||||
// convert to number.
|
||||
c &= 0x0f;
|
||||
|
||||
// p *= 10;
|
||||
// 10x = 2x + 8x = (x << 1) + (x << 3)
|
||||
if (p)
|
||||
{
|
||||
int p2;
|
||||
|
||||
p2 = p << 1;
|
||||
p = p2 << 2;
|
||||
p = p + p2;
|
||||
}
|
||||
p += c;
|
||||
}
|
||||
|
||||
components->portNumber = p;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// path and query.
|
||||
// path;params?query
|
||||
range = components->path;
|
||||
if (range.length)
|
||||
{
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
void test(const char *url)
|
||||
{
|
||||
URLComponents data;
|
||||
int ok;
|
||||
|
||||
char *buffer;
|
||||
buffer = strdup(url); // enough space.
|
||||
|
||||
if (!url || !*url) return;
|
||||
|
||||
ok = ParseURL(url, strlen(url), &data);
|
||||
|
||||
printf("%s (%s)\n", url, ok ? "ok" : "error");
|
||||
|
||||
|
||||
URLGetComponentCString(url, &data, URLComponentScheme, buffer);
|
||||
printf(" scheme: %s\n", buffer);
|
||||
|
||||
URLGetComponentCString(url, &data, URLComponentUser, buffer);
|
||||
printf(" username: %s\n", buffer);
|
||||
|
||||
URLGetComponentCString(url, &data, URLComponentPassword, buffer);
|
||||
printf(" password: %s\n", buffer);
|
||||
|
||||
URLGetComponentCString(url, &data, URLComponentHost, buffer);
|
||||
printf(" host: %s\n", buffer);
|
||||
|
||||
URLGetComponentCString(url, &data, URLComponentPort, buffer);
|
||||
printf(" port: %s [%d]\n", buffer, data.portNumber);
|
||||
|
||||
URLGetComponentCString(url, &data, URLComponentPath, buffer);
|
||||
printf(" path: %s\n", buffer);
|
||||
|
||||
URLGetComponentCString(url, &data, URLComponentParams, buffer);
|
||||
printf(" params: %s\n", buffer);
|
||||
|
||||
URLGetComponentCString(url, &data, URLComponentQuery, buffer);
|
||||
printf(" query: %s\n", buffer);
|
||||
|
||||
URLGetComponentCString(url, &data, URLComponentFragment, buffer);
|
||||
printf(" fragment: %s\n", buffer);
|
||||
|
||||
free(buffer);
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 1; i < argc; ++i)
|
||||
{
|
||||
test(argv[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
63
url.h
Normal file
63
url.h
Normal file
@ -0,0 +1,63 @@
|
||||
#ifndef __url_h__
|
||||
#define __url_h__
|
||||
|
||||
enum {
|
||||
SCHEME_UNKNOWN = -1,
|
||||
SCHEME_NONE = 0,
|
||||
SCHEME_FILE,
|
||||
SCHEME_FTP,
|
||||
SCHEME_GOPHER,
|
||||
SCHEME_HTTP,
|
||||
SCHEME_HTTPS,
|
||||
SCHEME_MAILTO,
|
||||
SCHEME_NEWS,
|
||||
SCHEME_NNTP,
|
||||
SCHEME_TELNET
|
||||
};
|
||||
|
||||
|
||||
typedef struct URLRange {
|
||||
int location;
|
||||
int length;
|
||||
} URLRange;
|
||||
|
||||
enum {
|
||||
URLComponentScheme,
|
||||
URLComponentUser,
|
||||
URLComponentPassword,
|
||||
URLComponentHost,
|
||||
URLComponentPort,
|
||||
URLComponentPath,
|
||||
URLComponentParams,
|
||||
URLComponentQuery,
|
||||
URLComponentFragment,
|
||||
URLComponentPathAndQuery
|
||||
};
|
||||
|
||||
typedef struct URLComponents {
|
||||
|
||||
int schemeType;
|
||||
int portNumber;
|
||||
|
||||
URLRange scheme;
|
||||
URLRange user;
|
||||
URLRange password;
|
||||
URLRange host;
|
||||
URLRange port;
|
||||
URLRange path;
|
||||
URLRange params;
|
||||
URLRange query;
|
||||
URLRange fragment;
|
||||
|
||||
URLRange pathAndQuery;
|
||||
|
||||
} URLComponents;
|
||||
|
||||
int ParseURL(const char *url, int length, struct URLComponents *components);
|
||||
|
||||
int URLGetComponentCString(const char *url, struct URLComponents *, int, char *);
|
||||
int URLGetComponentPString(const char *url, struct URLComponents *, int, char *);
|
||||
//int URLGetComponentGSString(const char *url, struct URLComponents *, int, GSString255Ptr);
|
||||
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user