#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <memory.h>
#include <netdb.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <readline/readline.h>
struct _cmd
{
char *c_name;
char *c_help;
bool c_conn;
void (*c_handler) (int, char **);
};
typedef struct _cmd cmd;
void cmdscanner (void);
int fillbuffer (int sd);
int findeol (char *, int, int);
cmd *getcmd (char *);
void makeargv (void);
void list_handler (int, char **);
void open_handler (int, char **);
void quit_handler (int, char **);
enum {
CHUNK = 1024
};
char *default_prompt = "gopher> ";
char listhelp[] = "";
char openhelp[] = "";
char quithelp[] = "";
cmd cmdtab[] = {
{"ls", listhelp, true, list_handler},
{"open", openhelp, false, open_handler},
{"quit", quithelp, false, quit_handler}
};
bool connected = false;
char *buffer = NULL;
char *line = NULL;
char *margv[20];
char *prompt = NULL;
int margc = 0;
int nbuffer = 0;
struct hostent *host = NULL;
#define NCMDS (sizeof (cmdtab) / sizeof (cmdtab[0]))
#define NMARGV (sizeof (margv) / sizeof (margv[0]))
int
main (int argc, char *argv[])
{
prompt = default_prompt;
for (;;)
cmdscanner ();
return 0;
}
void
cmdscanner (void)
{
cmd *c;
if (line)
{
free (line);
line = NULL;
}
line = readline (prompt);
if (!line)
quit_handler (margc, margv);
if (*line)
add_history (line);
makeargv();
if (margc == 0)
return;
c = getcmd (margv[0]);
if (c == (cmd *) -1)
{
fputs ("?Ambiguous command\n", stderr);
return;
}
else if (c == NULL)
{
fputs ("?Invalid command\n", stderr);
return;
}
else if (c->c_conn && !connected)
{
fputs ("Not connected.\n", stderr);
return;
}
(*c->c_handler) (margc, margv);
return;
}
int
fillbuffer (int sd)
{
int nread;
int tread = 0;
if (buffer)
{
free (buffer);
buffer = NULL;
nbuffer = 0 ;
}
nbuffer = CHUNK;
buffer = (char *) malloc (sizeof (char) * nbuffer);
if (buffer == NULL)
return -1;
for (;;)
{
nread = read (sd, buffer + tread, CHUNK);
if (nread == 0)
break;
tread += nread;
if (tread > nbuffer - CHUNK)
{
nbuffer += 2 * CHUNK;
buffer = (char *) realloc (buffer, sizeof (char) * nbuffer);
if (buffer == NULL)
return -1;
}
}
return tread;
}
int
findeol (char *str, int size, int offset)
{
int i;
for (i = offset; i < size - 1; i++)
{
if (str[i] == '\r' && str[i+1] == '\n')
return i;
}
return -1;
}
cmd *
getcmd (char *name)
{
int i;
int match;
int nmatches = 0;
for (i = 0; i < NCMDS; i++)
{
if (strcmp (name, cmdtab[i].c_name) == 0)
return &cmdtab[i];
else if (strncmp (name, cmdtab[i].c_name, strlen (name)) == 0)
{
match = i;
nmatches++;
}
}
if (nmatches == 1)
return &cmdtab[match];
if (nmatches > 1)
return (cmd *) -1;
return NULL;
}
void
makeargv (void)
{
int base;
int i;
int len;
int lenmarg;
for (i = 0; i < margc; i++)
{
free (margv[i]);
margv[i] = NULL;
}
len = strlen (line);
base = 0;
margc = 0;
for (i = 0; i < len; i++)
{
if (line[i] == ' ')
{
if (margc == NMARGV)
break;
lenmarg = i - base;
margv[margc] = (char *) malloc (sizeof (char) * (lenmarg + 1));
strncpy (margv[margc], line + base, lenmarg);
margv[margc][lenmarg] = '\0';
margc++;
base = i + 1;
}
}
if (margc < NMARGV && len > 0)
{
lenmarg = i - base;
margv[margc] = (char *) malloc (sizeof (char) * (lenmarg + 1));
strncpy (margv[margc], line + base, lenmarg);
margv[margc][lenmarg] = '\0';
margc++;
base = i + 1;
}
return;
}
void
list_handler (int argc, char *argv[])
{
int base;
int i;
int offset;
int sd;
int status;
int tread;
struct sockaddr_in addr;
if (!connected)
{
fputs ("Not connected\n", stderr);
return;
}
sd = socket (PF_INET, SOCK_STREAM, 0);
if (sd == -1)
quit_handler (argc, argv);
memset ((void *) &addr, '\0', sizeof (addr));
addr.sin_family = AF_INET;
addr.sin_port = htons (70);
memmove ((void *) &addr.sin_addr, (void *) host->h_addr, host->h_length);
if (connect (sd, (struct sockaddr *) &addr, sizeof (addr)) == -1)
quit_handler (argc, argv);
write (sd, "\r\n", 2);
tread = fillbuffer (sd);
if (tread == -1)
quit_handler (argc, argv);
close (sd);
base = 0;
for (;;)
{
offset = findeol (buffer, tread, base);
if (offset == -1)
break;
if (buffer[base] == 'i')
{
for (i = base + 1; i < offset; i++)
{
if (buffer[i] == '\t')
{
putchar ('\n');
break;
}
putchar (buffer[i]);
}
}
else if (buffer[base] == '0')
{
for (i = base + 1; i < offset; i++)
{
if (buffer[i] == '\t')
{
putchar ('\n');
break;
}
putchar (buffer[i]);
}
}
else if (buffer[base] == '1')
{
for (i = base + 1; i < offset; i++)
{
if (buffer[i] == '\t')
{
putchar ('/');
putchar ('\n');
break;
}
putchar (buffer[i]);
}
}
base = offset + 2;
}
return;
}
void
open_handler (int argc, char *argv[])
{
if (argc < 2)
return;
host = gethostbyname (argv[1]);
if (host == NULL)
{
fprintf (stderr, "%s: Unknown host\n", argv[1]);
return;
}
connected = true;
}
void
quit_handler (int argc, char *argv[])
{
int i;
if (buffer)
{
free (buffer);
buffer = NULL;
}
if (line)
{
free (line);
line = NULL;
}
for (i = 0; i < argc; i++)
{
free (argv[i]);
argv[i] = NULL;
}
exit (EXIT_SUCCESS);
}