/*--------------------------------------------------------------------- Gopher Support I'm a big fan of Gopher, so RRE provides support for fetching files via the Gopher protocol. ---------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include #include typedef void (*Handler)(void); /*--------------------------------------------------------------------- First, a few things that relate to the Nga virtual machine that RETRO runs on. ---------------------------------------------------------------------*/ #define CELL int32_t /* Cell size (32 bit, signed integer */ #define IMAGE_SIZE 524288 * 48 /* Amount of RAM. 12MiB by default. */ #define ADDRESSES 2048 /* Depth of address stack */ #define STACK_DEPTH 512 /* Depth of data stack */ extern CELL sp, rp, ip; /* Stack & instruction pointers */ extern CELL data[STACK_DEPTH]; /* The data stack */ extern CELL address[ADDRESSES]; /* The address stack */ extern CELL memory[IMAGE_SIZE + 1]; /* The memory for the image */ #define TOS data[sp] /* Shortcut for top item on stack */ #define NOS data[sp-1] /* Shortcut for second item on stack */ #define TORS address[rp] /* Shortcut for top item on address stack */ /*--------------------------------------------------------------------- Function prototypes. ---------------------------------------------------------------------*/ CELL stack_pop(); void stack_push(CELL value); char *string_extract(CELL at); CELL string_inject(char *str, CELL buffer); /*--------------------------------------------------------------------- The first Gopher related function is `error()`, which prints an error message and exits if there is a problem. ---------------------------------------------------------------------*/ void error(const char *msg) { perror(msg); exit(0); } /*--------------------------------------------------------------------- `gopher_fetch()` is the part that does all the real work. ---------------------------------------------------------------------*/ void gopher_fetch(char *host, CELL port, char *selector, CELL dest) { int sockfd, portno, n; struct sockaddr_in serv_addr; struct hostent *server; char data[128 * 1024 + 1]; char buffer[1025]; portno = (int)port; sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) error("ERROR opening socket"); server = gethostbyname(host); if (server == NULL) { fprintf(stderr,"ERROR, no such host\n"); exit(0); } bzero(data, 128 * 1024 + 1); bzero((char *) &serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length); serv_addr.sin_port = htons(portno); if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) error("ERROR connecting"); n = write(sockfd,selector,strlen(selector)); if (n < 0) error("ERROR writing to socket"); n = write(sockfd,"\n",strlen("\n")); if (n < 0) error("ERROR writing to socket"); n = 1; while (n > 0) { bzero(buffer,1025); n = read(sockfd,buffer,1024); strcat(data, buffer); } close(sockfd); string_inject(data, dest); stack_push(strlen(data)); } /*--------------------------------------------------------------------- The last Gopher function, `ngaGopherUnit()` pulls the values needed from the stack and passes them to the `gopher_fetch()` function. This will take the following from the stack (TOS to bottom): Selector (NULL terminated string) Port (Number) Server (NULL terminated string) Buffer (Pointer to memory that will hold the received file) ---------------------------------------------------------------------*/ void ngaGopherUnit() { CELL port, dest; char server[1025], selector[4097]; strcpy(selector, string_extract(stack_pop())); port = stack_pop(); strcpy(server, string_extract(stack_pop())); dest = stack_pop(); gopher_fetch(server, port, selector, dest); } Handler GopherActions[1] = { ngaGopherUnit }; void io_gopher_query() { stack_push(0); stack_push(5); } void io_gopher_handler() { GopherActions[stack_pop()](); }