commit 9258996b4ff1b3293023192594dea22de09514c4 Author: Lukáš Hozda Date: Mon Aug 17 22:32:36 2020 +0200 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..003063d --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,21 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10" + +[[package]] +name = "rgp" +version = "0.1.0" +dependencies = [ + "lazy_static", + "libc", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..0ead06e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "rgp" +version = "0.1.0" +authors = ["Lukáš Hozda "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +libc = "*" +lazy_static = "1.4.0" diff --git a/a.out b/a.out new file mode 100755 index 0000000..e6032f7 Binary files /dev/null and b/a.out differ diff --git a/cgo.c b/cgo.c new file mode 100644 index 0000000..89d2470 --- /dev/null +++ b/cgo.c @@ -0,0 +1,854 @@ +/* + * cgo - a simple terminal based gopher client + * Copyright (c) 2019 Sebastian Steinhauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* some "configuration" */ +#define START_URI "gopher://gopher.floodgap.com:70" +#define CMD_TEXT "less" +#define CMD_IMAGE "display" +#define CMD_BROWSER "firefox" +#define CMD_PLAYER "mplayer" +#define CMD_TELNET "telnet" +#define COLOR_PROMPT "1;34" +#define COLOR_SELECTOR "1;32" +#define HEAD_CHECK_LEN 5 +#define GLOBAL_CONFIG_FILE "/etc/cgorc" +#define LOCAL_CONFIG_FILE "/.cgorc" +#define NUM_BOOKMARKS 20 +#define VERBOSE "true" + +/* some internal defines */ +#define KEY_RANGE (('z' - 'a') + 1) + +/* structs */ +typedef struct link_s link_t; +struct link_s { + link_t *next; + char which; + short key; + char *host; + char *port; + char *selector; +}; + +typedef struct config_s config_t; +struct config_s { + char start_uri[512]; + char cmd_text[512]; + char cmd_image[512]; + char cmd_browser[512]; + char cmd_player[512]; + char color_prompt[512]; + char color_selector[512]; + char verbose[512]; +}; + +char tmpfilename[256]; +link_t *links = NULL; +link_t *history = NULL; +int link_key; +char current_host[512], current_port[64], current_selector[1024]; +char parsed_host[512], parsed_port[64], parsed_selector[1024]; +char bookmarks[NUM_BOOKMARKS][512]; +config_t config; + +/* function prototypes */ +int parse_uri(const char *uri); + +/* implementation */ +void usage() +{ + fputs("usage: cgo [-v] [-H] [gopher URI]\n", + stderr); + exit(EXIT_SUCCESS); +} + +void banner(FILE *f) +{ + fputs("cgo 0.6.1 Copyright (c) 2020 Sebastian Steinhauer\n", f); +} + +int check_option_true(const char *option) +{ + return strcasecmp(option, "false") && strcasecmp(option, "off"); +} + +void parse_config_line(const char *line) +{ + char token[1024]; + char bkey[128]; + char *value = NULL; + int i, j; + + while (*line == ' ' || *line == '\t') line++; + for (i = 0; *line && *line != ' ' && *line != '\t'; line++) + if (i < sizeof(token) - 1) token[i++] = *line; + token[i] = 0; + + if (! strcmp(token, "start_uri")) value = &config.start_uri[0]; + else if (! strcmp(token, "cmd_text")) value = &config.cmd_text[0]; + else if (! strcmp(token, "cmd_browser")) value = &config.cmd_browser[0]; + else if (! strcmp(token, "cmd_image")) value = &config.cmd_image[0]; + else if (! strcmp(token, "cmd_player")) value = &config.cmd_player[0]; + else if (! strcmp(token, "color_prompt")) value = &config.color_prompt[0]; + else if (! strcmp(token, "color_selector")) value = &config.color_selector[0]; + else if (! strcmp(token, "verbose")) value = &config.verbose[0]; + else { + for (j = 0; j < NUM_BOOKMARKS; j++) { + snprintf(bkey, sizeof(bkey), "bookmark%d", j+1); + if (! strcmp(token, bkey)) { + value = &bookmarks[j][0]; + break; + } + } + if (! value) return; + }; + + while (*line == ' ' || *line == '\t') line++; + for (i = 0; *line; line++) + if (i < 512-1) value[i++] = *line; + for (i--; i > 0 && (value[i] == ' ' || value[i] == '\t'); i--) ; + value[++i] = 0; + +} + +void load_config(const char *filename) +{ + FILE *fp; + int ch, i; + char line[1024]; + + fp = fopen(filename, "r"); + if (! fp) return; + + memset(line, 0, sizeof(line)); + i = 0; + ch = fgetc(fp); + while (1) { + switch (ch) { + case '#': + while (ch != '\n' && ch != -1) + ch = fgetc(fp); + break; + case -1: + parse_config_line(line); + fclose(fp); + return; + case '\r': + ch = fgetc(fp); + break; + case '\n': + parse_config_line(line); + memset(line, 0, sizeof(line)); + i = 0; + ch = fgetc(fp); + break; + default: + if (i < sizeof(line) - 1) + line[i++] = ch; + ch = fgetc(fp); + break; + } + } +} + +void init_config() +{ + char filename[1024]; + const char *home; + int i; + + /* copy defaults */ + snprintf(config.start_uri, sizeof(config.start_uri), START_URI); + snprintf(config.cmd_text, sizeof(config.cmd_text), "%s", CMD_TEXT); + snprintf(config.cmd_image, sizeof(config.cmd_image), "%s", CMD_IMAGE); + snprintf(config.cmd_browser, sizeof(config.cmd_browser), "%s", CMD_BROWSER); + snprintf(config.cmd_player, sizeof(config.cmd_player), "%s", CMD_PLAYER); + snprintf(config.color_prompt, sizeof(config.color_prompt), "%s", COLOR_PROMPT); + snprintf(config.color_selector, sizeof(config.color_selector), "%s", COLOR_SELECTOR); + snprintf(config.verbose, sizeof(config.verbose), "%s", VERBOSE); + for (i = 0; i < NUM_BOOKMARKS; i++) bookmarks[i][0] = 0; + /* read configs */ + load_config(GLOBAL_CONFIG_FILE); + home = getenv("HOME"); + if (home) { + snprintf(filename, sizeof(filename), "%s%s", home, LOCAL_CONFIG_FILE); + load_config(filename); + } +} + +int dial(const char *host, const char *port, const char *selector) +{ + struct addrinfo hints; + struct addrinfo *res, *r; + int srv = -1, l; + char request[512]; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + if (getaddrinfo(host, port, &hints, &res) != 0) { + fprintf(stderr, "error: cannot resolve hostname '%s:%s': %s\n", + host, port, strerror(errno)); + return -1; + } + for (r = res; r; r = r->ai_next) { + srv = socket(r->ai_family, r->ai_socktype, r->ai_protocol); + if (srv == -1) + continue; + if (connect(srv, r->ai_addr, r->ai_addrlen) == 0) + break; + close(srv); + } + freeaddrinfo(res); + if (! r) { + fprintf(stderr, "error: cannot connect to host '%s:%s'\n", + host, port); + return -1; + } + snprintf(request, sizeof(request), "%s\r\n", selector); + l = strlen(request); + if (write(srv, request, l) != l) { + fprintf(stderr, "error: cannot complete request\n"); + close(srv); + return -1; + } + return srv; +} + +int read_line(int fd, char *buf, size_t buf_len) +{ + size_t i = 0; + char c = 0; + + do { + if (read(fd, &c, sizeof(char)) != sizeof(char)) + return 0; + if (c != '\r') + buf[i++] = c; + } while (c != '\n' && i < buf_len); + buf[i - 1] = '\0'; + return 1; +} + +int download_file(const char *host, const char *port, + const char *selector, int fd) +{ + int srvfd, len; + unsigned long total = 0; + char buffer[4096]; + + if (check_option_true(config.verbose)) + printf("downloading [%s]...\r", selector); + srvfd = dial(host, port, selector); + if (srvfd == -1) { + printf("\033[2Kerror: downloading [%s] failed\n", selector); + close(fd); + return 0; + } + while ((len = read(srvfd, buffer, sizeof(buffer))) > 0) { + write(fd, buffer, len); + total += len; + if (check_option_true(config.verbose)) + printf("downloading [%s] (%ld kb)...\r", selector, total / 1024); + } + close(fd); + close(srvfd); + if (check_option_true(config.verbose)) + printf("\033[2Kdownloading [%s] complete\n", selector); + return 1; +} + +int download_temp(const char *host, const char *port, const char *selector) +{ + int tmpfd; + +#if defined(__OpenBSD__) + strlcpy(tmpfilename, "/tmp/cgoXXXXXX", sizeof(tmpfilename)); +#else + strcpy(tmpfilename, "/tmp/cgoXXXXXX"); +#endif + tmpfd = mkstemp(tmpfilename); + if (tmpfd == -1) { + fputs("error: unable to create tmp file\n", stderr); + return 0; + } + if (! download_file(host, port, selector, tmpfd)) { + unlink(tmpfilename); + return 0; + } + return 1; +} + +int make_key(char c1, char c2, char c3) +{ + if (! c1 || ! c2) + return -1; + + if (! c3) + return ((c1 - 'a') * KEY_RANGE) + (c2 - 'a'); + else + return (((c1 - 'a' + 1) * KEY_RANGE * KEY_RANGE) + ((c2 - 'a') * KEY_RANGE) + (c3 - 'a')); +} + +void make_key_str(int key, char *c1, char *c2, char *c3) { + if (key < (KEY_RANGE * KEY_RANGE)) { + *c1 = 'a' + (key / KEY_RANGE); + *c2 = 'a' + (key % KEY_RANGE); + *c3 = 0; + } else { + *c1 = 'a' + (key / (KEY_RANGE * KEY_RANGE)) - 1; + *c2 = 'a' + ((key / KEY_RANGE) % KEY_RANGE); + *c3 = 'a' + (key % KEY_RANGE); + } +} + +void add_link(char which, const char *name, + const char *host, const char *port, const char *selector) +{ + link_t *link; + char a = 0, b = 0, c = 0; + + if (! host || ! port || ! selector) + return; /* ignore incomplete selectors */ + link = calloc(1, sizeof(link_t)); + link->which = which; + link->key = link_key; + link->host = strdup(host); + link->port = strdup(port); + link->selector = strdup(selector); + if (! links) + link->next = NULL; + else + link->next = links; + links = link; + + make_key_str(link_key++, &a, &b, &c); + printf("\033[%sm%c%c%c\033[0m \033[1m%s\033[0m\n", + config.color_selector, a, b, c, name); +} + +void clear_links() +{ + link_t *link, *next; + + for (link = links; link; ) { + next = link->next; + free(link->host); + free(link->port); + free(link->selector); + free(link); + link = next; + } + links = NULL; + link_key = 0; +} + +void add_history() +{ + link_t *link; + + link = calloc(1, sizeof(link_t)); + link->host = strdup(current_host); + link->port = strdup(current_port); + link->selector = strdup(current_selector); + link->which = 0; /* not needed for history...just clear them */ + link->key = 0; + if (! history) + link->next = NULL; + else + link->next = history; + history = link; +} + +void handle_directory_line(char *line) +{ + int i; + char *lp, *last, *fields[4]; + + /* tokenize */ + for (i = 0; i < 4; i++) + fields[i] = NULL; + last = &line[1]; + for (lp = last, i = 0; i < 4; lp++) { + if (*lp == '\t' || *lp == '\0') { + fields[i] = last; + last = lp + 1; + if (*lp == '\0') + break; + *lp = '\0'; + i++; + } + } + /* determine listing type */ + switch (line[0]) { + case 'i': + case '3': + printf(" %s\n", fields[0]); + break; + case '.': /* some gopher servers use this */ + puts(""); + break; + case '0': + case '1': + case '5': + case '7': + case '8': + case '9': + case 'g': + case 'I': + case 'p': + case 'h': + case 's': + add_link(line[0], fields[0], fields[2], fields[3], fields[1]); + break; + default: + printf("miss [%c]: %s\n", line[0], fields[0]); + break; + } +} + +int is_valid_directory_entry(const char *line) +{ + switch (line[0]) { + case 'i': + case '3': + case '.': /* some gopher servers use this */ + case '0': + case '1': + case '5': + case '7': + case '8': + case '9': + case 'g': + case 'I': + case 'p': + case 'h': + case 's': + return 1; + default: + return 0; + } +} + +void view_directory(const char *host, const char *port, + const char *selector, int make_current) +{ + int is_dir; + int srvfd, i, head_read; + char line[1024]; + char head[HEAD_CHECK_LEN][1024]; + + srvfd = dial(host, port, selector); + if (srvfd != -1) { /* only adapt current prompt when successful */ + /* make history entry */ + if (make_current) + add_history(); + /* don't overwrite the current_* things... */ + if (host != current_host) + snprintf(current_host, sizeof(current_host), "%s", host); + if (port != current_port) + snprintf(current_port, sizeof(current_port), "%s", port); + if (selector != current_selector) + snprintf(current_selector, sizeof(current_selector), + "%s", selector); + } + clear_links(); /* clear links *AFTER* dialing out!! */ + if (srvfd == -1) + return; /* quit if not successful */ + head_read = 0; + is_dir = 1; + while (head_read < HEAD_CHECK_LEN && read_line(srvfd, line, sizeof(line))) { + strcpy(head[head_read], line); + if (!is_valid_directory_entry(head[head_read])) { + is_dir = 0; + break; + } + head_read++; + } + if (!is_dir) { + puts("error: Not a directory."); + close(srvfd); + return; + } + for (i = 0; i < head_read; i++) { + handle_directory_line(head[i]); + } + while (read_line(srvfd, line, sizeof(line))) { + handle_directory_line(line); + } + close(srvfd); +} + +void view_file(const char *cmd, const char *host, + const char *port, const char *selector) +{ + pid_t pid; + int status, i, j; + char buffer[1024], *argv[32], *p; + + if (check_option_true(config.verbose)) + printf("h(%s) p(%s) s(%s)\n", host, port, selector); + + if (! download_temp(host, port, selector)) + return; + + /* parsed command line string */ + argv[0] = &buffer[0]; + for (p = (char*) cmd, i = 0, j = 1; *p && i < sizeof(buffer) - 1 && j < 30; ) { + if (*p == ' ' || *p == '\t') { + buffer[i++] = 0; + argv[j++] = &buffer[i]; + while (*p == ' ' || *p == '\t') p++; + } else buffer[i++] = *p++; + } + buffer[i] = 0; + argv[j++] = tmpfilename; + argv[j] = NULL; + + /* fork and execute */ + if (check_option_true(config.verbose)) + printf("executing: %s %s\n", cmd, tmpfilename); + pid = fork(); + if (pid == 0) { + if (execvp(argv[0], argv) == -1) + puts("error: execvp() failed!"); + } else if (pid == -1) puts("error: fork() failed"); + sleep(1); /* to wait for browsers etc. that return immediatly */ + waitpid(pid, &status, 0); + unlink(tmpfilename); +} + +void view_telnet(const char *host, const char *port) +{ + pid_t pid; + int status; + + printf("executing: %s %s %s\n", CMD_TELNET, host, port); + pid = fork(); + if (pid == 0) { + if (execlp(CMD_TELNET, CMD_TELNET, host, port, NULL) == -1) + puts("error: execlp() failed!"); + } else if (pid == -1) puts("error: fork() failed!"); + waitpid(pid, &status, 0); + puts("(done)"); +} + +void view_download(const char *host, const char *port, const char *selector) +{ + int fd; + char filename[1024], line[1024]; + + snprintf(filename, sizeof(filename), "%s", strrchr(selector, '/') + 1); + printf("enter filename for download [%s]: ", filename); + fflush(stdout); + if (! read_line(0, line, sizeof(line))) { + puts("download aborted"); + return; + } + if (strlen(line) > 0) +#if defined(__OpenBSD__) + strlcpy(filename, line, sizeof(filename)); +#else + strcpy(filename, line); +#endif + fd = open(filename, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR); + if (fd == -1) { + printf("error: unable to create file [%s]: %s\n", + filename, strerror(errno)); + return; + } + if (! download_file(host, port, selector, fd)) { + printf("error: unable to download [%s]\n", selector); + unlink(filename); + return; + } +} + +void view_search(const char *host, const char *port, const char *selector) +{ + char search_selector[1024]; + char line[1024]; + + printf("enter search string: "); + fflush(stdout); + if (! read_line(0, line, sizeof(line))) { + puts("search aborted"); + return; + } + snprintf(search_selector, sizeof(search_selector), "%s\t%s", + selector, line); + view_directory(host, port, search_selector, 1); +} + +void view_history(int key) +{ + int history_key = 0; + char a, b, c; + link_t *link; + + if (! history) { + puts("(empty history)"); + return; + } + if ( key < 0 ) { + puts("(history)"); + for ( link = history; link; link = link->next ) { + make_key_str(history_key++, &a, &b, &c); + printf("\033[%sm%c%c%c\033[0m \033[1m%s:%s/1%s\033[0m\n", + COLOR_SELECTOR, a, b, c, link->host, link->port, link->selector); + } + } else { + /* traverse history list */ + for ( link = history; link; link = link->next, ++history_key ) { + if ( history_key == key ) { + view_directory(link->host, link->port, link->selector, 0); + return; + } + } + puts("history item not found"); + } +} + +void view_bookmarks(int key) +{ + int i; + char a, b, c; + + if (key < 0) { + puts("(bookmarks)"); + for (i = 0; i < NUM_BOOKMARKS; i++) { + if (bookmarks[i][0]) { + make_key_str(i, &a, &b, &c); + printf("\033[%sm%c%c%c\033[0m \033[1m%s\033[0m\n", + COLOR_SELECTOR, a, b, c, &bookmarks[i][0]); + } + } + } else { + for (i = 0; i < NUM_BOOKMARKS; i++) { + if (bookmarks[i][0] && i == key) { + if (parse_uri(&bookmarks[i][0])) view_directory(parsed_host, parsed_port, parsed_selector, 0); + else printf("invalid gopher URI: %s", &bookmarks[i][0]); + return; + } + } + } +} + +void pop_history() +{ + link_t *next; + + if (! history) { + puts("(empty history)"); + return; + } + /* reload page from history (and don't count as history) */ + view_directory(history->host, history->port, history->selector, 0); + /* history is history... :) */ + next = history->next; + free(history->host); + free(history->port); + free(history->selector); + free(history); + history = next; +} + +int follow_link(int key) +{ + link_t *link; + + for (link = links; link; link = link->next) { + if (link->key != key) + continue; + switch (link->which) { + case '0': + view_file(&config.cmd_text[0], link->host, link->port, link->selector); + break; + case '1': + view_directory(link->host, link->port, link->selector, 1); + break; + case '7': + view_search(link->host, link->port, link->selector); + break; + case '5': + case '9': + view_download(link->host, link->port, link->selector); + break; + case '8': + view_telnet(link->host, link->port); + break; + case 'g': + case 'I': + case 'p': + view_file(&config.cmd_image[0], link->host, link->port, link->selector); + break; + case 'h': + view_file(&config.cmd_browser[0], link->host, link->port, link->selector); + break; + case 's': + view_file(&config.cmd_player[0], link->host, link->port, link->selector); + break; + default: + printf("missing handler [%c]\n", link->which); + break; + } + return 1; /* return the array is broken after view! */ + } + return 0; +} + +void download_link(int key) +{ + link_t *link; + + for (link = links; link; link = link->next) { + if (link->key != key) + continue; + view_download(link->host, link->port, link->selector); + return; + } + puts("link not found"); +} + +int parse_uri(const char *uri) +{ + int i; + + /* strip gopher:// */ + if (! strncmp(uri, "gopher://", 9)) + uri += 9; + /* parse host */ + for (i = 0; *uri && *uri != ':' && *uri != '/'; uri++) { + if (*uri != ' ' && i < sizeof(parsed_host) - 1) + parsed_host[i++] = *uri; + } + if (i > 0) parsed_host[i] = 0; + else return 0; + /* parse port */ + if (*uri == ':') { + uri++; + for (i = 0; *uri && *uri != '/'; uri++) + if (*uri != ' ' && i < sizeof(parsed_port) - 1) + parsed_port[i++] = *uri; + parsed_port[i] = 0; + } else snprintf(parsed_port, sizeof(parsed_port), "%d", 70); + /* parse selector (ignore slash and selector type) */ + if (*uri) ++uri; + if (*uri) ++uri; + for (i = 0; *uri && i < sizeof(parsed_selector) - 1; ++uri, ++i) + parsed_selector[i] = *uri; + parsed_selector[i] = '\0'; + + return 1; +} + +int main(int argc, char *argv[]) +{ + int i; + char line[1024], *uri; + + /* copy defaults */ + init_config(); + uri = &config.start_uri[0]; + + /* parse command line */ + for (i = 1; i < argc; i++) { + if (argv[i][0] == '-') switch(argv[i][1]) { + case 'H': + usage(); + break; + case 'v': + banner(stdout); + exit(EXIT_SUCCESS); + default: + usage(); + } else { + uri = argv[i]; + } + } + + /* parse uri */ + if (! parse_uri(uri)) { + banner(stderr); + fprintf(stderr, "invalid gopher URI: %s", argv[i]); + exit(EXIT_FAILURE); + } + + /* main loop */ + view_directory(parsed_host, parsed_port, parsed_selector, 0); + for (;;) { + printf("\033[%sm%s:%s%s\033[0m ", config.color_prompt, + current_host, current_port, current_selector); + fflush(stdout); /* to display the prompt */ + if (! read_line(0, line, sizeof(line))) { + puts("QUIT"); + return EXIT_SUCCESS; + } + i = strlen(line); + switch (line[0]) { + case '?': + puts( + "? - help\n" + "* - reload directory\n" + "< - go back in history\n" + ".[LINK] - download the given link\n" + "H - show history\n" + "H[LINK] - jump to the specified history item\n" + "G[URI] - jump to the given gopher URI\n" + "B - show bookmarks\n" + "B[LINK] - jump to the specified bookmark item\n" + "C^d - quit"); + break; + case '<': + pop_history(); + break; + case '*': + view_directory(current_host, current_port, + current_selector, 0); + break; + case '.': + download_link(make_key(line[1], line[2], line[3])); + break; + case 'H': + if (i == 1 || i == 3 || i == 4) view_history(make_key(line[1], line[2], line[3])); + break; + case 'G': + if (parse_uri(&line[1])) view_directory(parsed_host, parsed_port, parsed_selector, 1); + else puts("invalid gopher URI"); + break; + case 'B': + if (i == 1 || i == 3 || i == 4) view_bookmarks(make_key(line[1], line[2], line[3])); + break; + default: + follow_link(make_key(line[0], line[1], line[2])); + break; + } + } + return EXIT_SUCCESS; /* never get's here but stops cc complaining */ +} diff --git a/cgo.rs b/cgo.rs new file mode 100644 index 0000000..23f2b7a --- /dev/null +++ b/cgo.rs @@ -0,0 +1,1417 @@ +#![allow(dead_code, mutable_transmutes, non_camel_case_types, non_snake_case, + non_upper_case_globals, unused_assignments, unused_mut)] +#![register_tool(c2rust)] +#![feature(const_raw_ptr_to_usize_cast, extern_types, main, register_tool)] +extern "C" { + pub type _IO_wide_data; + pub type _IO_codecvt; + pub type _IO_marker; + #[no_mangle] + fn waitpid(__pid: __pid_t, __stat_loc: *mut libc::c_int, + __options: libc::c_int) -> __pid_t; + #[no_mangle] + fn socket(__domain: libc::c_int, __type: libc::c_int, + __protocol: libc::c_int) -> libc::c_int; + #[no_mangle] + fn connect(__fd: libc::c_int, __addr: *const sockaddr, __len: socklen_t) + -> libc::c_int; + #[no_mangle] + fn getaddrinfo(__name: *const libc::c_char, + __service: *const libc::c_char, __req: *const addrinfo, + __pai: *mut *mut addrinfo) -> libc::c_int; + #[no_mangle] + fn freeaddrinfo(__ai: *mut addrinfo); + #[no_mangle] + fn open(__file: *const libc::c_char, __oflag: libc::c_int, _: ...) + -> libc::c_int; + #[no_mangle] + fn close(__fd: libc::c_int) -> libc::c_int; + #[no_mangle] + fn read(__fd: libc::c_int, __buf: *mut libc::c_void, __nbytes: size_t) + -> ssize_t; + #[no_mangle] + fn write(__fd: libc::c_int, __buf: *const libc::c_void, __n: size_t) + -> ssize_t; + #[no_mangle] + fn sleep(__seconds: libc::c_uint) -> libc::c_uint; + #[no_mangle] + fn execvp(__file: *const libc::c_char, __argv: *const *mut libc::c_char) + -> libc::c_int; + #[no_mangle] + fn execlp(__file: *const libc::c_char, __arg: *const libc::c_char, _: ...) + -> libc::c_int; + #[no_mangle] + fn fork() -> __pid_t; + #[no_mangle] + fn unlink(__name: *const libc::c_char) -> libc::c_int; + #[no_mangle] + fn __errno_location() -> *mut libc::c_int; + #[no_mangle] + static mut stdout: *mut FILE; + #[no_mangle] + static mut stderr: *mut FILE; + #[no_mangle] + fn fclose(__stream: *mut FILE) -> libc::c_int; + #[no_mangle] + fn fflush(__stream: *mut FILE) -> libc::c_int; + #[no_mangle] + fn fopen(_: *const libc::c_char, _: *const libc::c_char) -> *mut FILE; + #[no_mangle] + fn fprintf(_: *mut FILE, _: *const libc::c_char, _: ...) -> libc::c_int; + #[no_mangle] + fn printf(_: *const libc::c_char, _: ...) -> libc::c_int; + #[no_mangle] + fn snprintf(_: *mut libc::c_char, _: libc::c_ulong, + _: *const libc::c_char, _: ...) -> libc::c_int; + #[no_mangle] + fn fgetc(__stream: *mut FILE) -> libc::c_int; + #[no_mangle] + fn fputs(__s: *const libc::c_char, __stream: *mut FILE) -> libc::c_int; + #[no_mangle] + fn puts(__s: *const libc::c_char) -> libc::c_int; + #[no_mangle] + fn calloc(_: libc::c_ulong, _: libc::c_ulong) -> *mut libc::c_void; + #[no_mangle] + fn free(__ptr: *mut libc::c_void); + #[no_mangle] + fn exit(_: libc::c_int) -> !; + #[no_mangle] + fn getenv(__name: *const libc::c_char) -> *mut libc::c_char; + #[no_mangle] + fn mkstemp(__template: *mut libc::c_char) -> libc::c_int; + #[no_mangle] + fn memset(_: *mut libc::c_void, _: libc::c_int, _: libc::c_ulong) + -> *mut libc::c_void; + #[no_mangle] + fn strcpy(_: *mut libc::c_char, _: *const libc::c_char) + -> *mut libc::c_char; + #[no_mangle] + fn strcmp(_: *const libc::c_char, _: *const libc::c_char) -> libc::c_int; + #[no_mangle] + fn strncmp(_: *const libc::c_char, _: *const libc::c_char, + _: libc::c_ulong) -> libc::c_int; + #[no_mangle] + fn strdup(_: *const libc::c_char) -> *mut libc::c_char; + #[no_mangle] + fn strrchr(_: *const libc::c_char, _: libc::c_int) -> *mut libc::c_char; + #[no_mangle] + fn strcasecmp(_: *const libc::c_char, _: *const libc::c_char) + -> libc::c_int; + #[no_mangle] + fn strlen(_: *const libc::c_char) -> libc::c_ulong; + #[no_mangle] + fn strerror(_: libc::c_int) -> *mut libc::c_char; +} +pub type __off_t = libc::c_long; +pub type __off64_t = libc::c_long; +pub type __pid_t = libc::c_int; +pub type __ssize_t = libc::c_long; +pub type __socklen_t = libc::c_uint; +pub type pid_t = __pid_t; +pub type ssize_t = __ssize_t; +pub type size_t = libc::c_ulong; +pub type socklen_t = __socklen_t; +pub type __socket_type = libc::c_uint; +pub const SOCK_NONBLOCK: __socket_type = 2048; +pub const SOCK_CLOEXEC: __socket_type = 524288; +pub const SOCK_PACKET: __socket_type = 10; +pub const SOCK_DCCP: __socket_type = 6; +pub const SOCK_SEQPACKET: __socket_type = 5; +pub const SOCK_RDM: __socket_type = 4; +pub const SOCK_RAW: __socket_type = 3; +pub const SOCK_DGRAM: __socket_type = 2; +pub const SOCK_STREAM: __socket_type = 1; +pub type sa_family_t = libc::c_ushort; +#[derive(Copy, Clone)] +#[repr(C)] +pub struct sockaddr { + pub sa_family: sa_family_t, + pub sa_data: [libc::c_char; 14], +} +#[derive(Copy, Clone)] +#[repr(C)] +pub struct addrinfo { + pub ai_flags: libc::c_int, + pub ai_family: libc::c_int, + pub ai_socktype: libc::c_int, + pub ai_protocol: libc::c_int, + pub ai_addrlen: socklen_t, + pub ai_addr: *mut sockaddr, + pub ai_canonname: *mut libc::c_char, + pub ai_next: *mut addrinfo, +} +#[derive(Copy, Clone)] +#[repr(C)] +pub struct _IO_FILE { + pub _flags: libc::c_int, + pub _IO_read_ptr: *mut libc::c_char, + pub _IO_read_end: *mut libc::c_char, + pub _IO_read_base: *mut libc::c_char, + pub _IO_write_base: *mut libc::c_char, + pub _IO_write_ptr: *mut libc::c_char, + pub _IO_write_end: *mut libc::c_char, + pub _IO_buf_base: *mut libc::c_char, + pub _IO_buf_end: *mut libc::c_char, + pub _IO_save_base: *mut libc::c_char, + pub _IO_backup_base: *mut libc::c_char, + pub _IO_save_end: *mut libc::c_char, + pub _markers: *mut _IO_marker, + pub _chain: *mut _IO_FILE, + pub _fileno: libc::c_int, + pub _flags2: libc::c_int, + pub _old_offset: __off_t, + pub _cur_column: libc::c_ushort, + pub _vtable_offset: libc::c_schar, + pub _shortbuf: [libc::c_char; 1], + pub _lock: *mut libc::c_void, + pub _offset: __off64_t, + pub _codecvt: *mut _IO_codecvt, + pub _wide_data: *mut _IO_wide_data, + pub _freeres_list: *mut _IO_FILE, + pub _freeres_buf: *mut libc::c_void, + pub __pad5: size_t, + pub _mode: libc::c_int, + pub _unused2: [libc::c_char; 20], +} +pub type _IO_lock_t = (); +pub type FILE = _IO_FILE; +#[derive(Copy, Clone)] +#[repr(C)] +pub struct link_s { + pub next: *mut link_t, + pub which: libc::c_char, + pub key: libc::c_short, + pub host: *mut libc::c_char, + pub port: *mut libc::c_char, + pub selector: *mut libc::c_char, +} +/* structs */ +pub type link_t = link_s; +#[derive(Copy, Clone)] +#[repr(C)] +pub struct config_s { + pub start_uri: [libc::c_char; 512], + pub cmd_text: [libc::c_char; 512], + pub cmd_image: [libc::c_char; 512], + pub cmd_browser: [libc::c_char; 512], + pub cmd_player: [libc::c_char; 512], + pub color_prompt: [libc::c_char; 512], + pub color_selector: [libc::c_char; 512], + pub verbose: [libc::c_char; 512], +} +pub type config_t = config_s; +#[no_mangle] +pub static mut tmpfilename: [libc::c_char; 256] = [0; 256]; +#[no_mangle] +pub static mut links: *mut link_t = 0 as *const link_t as *mut link_t; +#[no_mangle] +pub static mut history: *mut link_t = 0 as *const link_t as *mut link_t; +#[no_mangle] +pub static mut link_key: libc::c_int = 0; +#[no_mangle] +pub static mut current_host: [libc::c_char; 512] = [0; 512]; +#[no_mangle] +pub static mut current_port: [libc::c_char; 64] = [0; 64]; +#[no_mangle] +pub static mut current_selector: [libc::c_char; 1024] = [0; 1024]; +#[no_mangle] +pub static mut parsed_host: [libc::c_char; 512] = [0; 512]; +#[no_mangle] +pub static mut parsed_port: [libc::c_char; 64] = [0; 64]; +#[no_mangle] +pub static mut parsed_selector: [libc::c_char; 1024] = [0; 1024]; +#[no_mangle] +pub static mut bookmarks: [[libc::c_char; 512]; 20] = [[0; 512]; 20]; +#[no_mangle] +pub static mut config: config_t = + config_t{start_uri: [0; 512], + cmd_text: [0; 512], + cmd_image: [0; 512], + cmd_browser: [0; 512], + cmd_player: [0; 512], + color_prompt: [0; 512], + color_selector: [0; 512], + verbose: [0; 512],}; +/* implementation */ +#[no_mangle] +pub unsafe extern "C" fn usage() { + fputs(b"usage: cgo [-v] [-H] [gopher URI]\n\x00" as *const u8 as + *const libc::c_char, stderr); + exit(0 as libc::c_int); +} +#[no_mangle] +pub unsafe extern "C" fn banner(mut f: *mut FILE) { + fputs(b"cgo 0.6.1 Copyright (c) 2020 Sebastian Steinhauer\n\x00" as + *const u8 as *const libc::c_char, f); +} +#[no_mangle] +pub unsafe extern "C" fn check_option_true(mut option: *const libc::c_char) + -> libc::c_int { + return (strcasecmp(option, + b"false\x00" as *const u8 as *const libc::c_char) != 0 + && + strcasecmp(option, + b"off\x00" as *const u8 as *const libc::c_char) != + 0) as libc::c_int; +} +#[no_mangle] +pub unsafe extern "C" fn parse_config_line(mut line: *const libc::c_char) { + let mut token: [libc::c_char; 1024] = [0; 1024]; + let mut bkey: [libc::c_char; 128] = [0; 128]; + let mut value: *mut libc::c_char = 0 as *mut libc::c_char; + let mut i: libc::c_int = 0; + let mut j: libc::c_int = 0; + while *line as libc::c_int == ' ' as i32 || + *line as libc::c_int == '\t' as i32 { + line = line.offset(1) + } + i = 0 as libc::c_int; + while *line as libc::c_int != 0 && *line as libc::c_int != ' ' as i32 && + *line as libc::c_int != '\t' as i32 { + if (i as libc::c_ulong) < + (::std::mem::size_of::<[libc::c_char; 1024]>() as + libc::c_ulong).wrapping_sub(1 as libc::c_int as + libc::c_ulong) { + let fresh0 = i; + i = i + 1; + token[fresh0 as usize] = *line + } + line = line.offset(1) + } + token[i as usize] = 0 as libc::c_int as libc::c_char; + if strcmp(token.as_mut_ptr(), + b"start_uri\x00" as *const u8 as *const libc::c_char) == 0 { + value = + &mut *config.start_uri.as_mut_ptr().offset(0 as libc::c_int as + isize) as + *mut libc::c_char + } else if strcmp(token.as_mut_ptr(), + b"cmd_text\x00" as *const u8 as *const libc::c_char) == 0 + { + value = + &mut *config.cmd_text.as_mut_ptr().offset(0 as libc::c_int as + isize) as + *mut libc::c_char + } else if strcmp(token.as_mut_ptr(), + b"cmd_browser\x00" as *const u8 as *const libc::c_char) + == 0 { + value = + &mut *config.cmd_browser.as_mut_ptr().offset(0 as libc::c_int as + isize) as + *mut libc::c_char + } else if strcmp(token.as_mut_ptr(), + b"cmd_image\x00" as *const u8 as *const libc::c_char) == + 0 { + value = + &mut *config.cmd_image.as_mut_ptr().offset(0 as libc::c_int as + isize) as + *mut libc::c_char + } else if strcmp(token.as_mut_ptr(), + b"cmd_player\x00" as *const u8 as *const libc::c_char) == + 0 { + value = + &mut *config.cmd_player.as_mut_ptr().offset(0 as libc::c_int as + isize) as + *mut libc::c_char + } else if strcmp(token.as_mut_ptr(), + b"color_prompt\x00" as *const u8 as *const libc::c_char) + == 0 { + value = + &mut *config.color_prompt.as_mut_ptr().offset(0 as libc::c_int as + isize) as + *mut libc::c_char + } else if strcmp(token.as_mut_ptr(), + b"color_selector\x00" as *const u8 as + *const libc::c_char) == 0 { + value = + &mut *config.color_selector.as_mut_ptr().offset(0 as libc::c_int + as isize) as + *mut libc::c_char + } else if strcmp(token.as_mut_ptr(), + b"verbose\x00" as *const u8 as *const libc::c_char) == 0 + { + value = + &mut *config.verbose.as_mut_ptr().offset(0 as libc::c_int as + isize) as + *mut libc::c_char + } else { + j = 0 as libc::c_int; + while j < 20 as libc::c_int { + snprintf(bkey.as_mut_ptr(), + ::std::mem::size_of::<[libc::c_char; 128]>() as + libc::c_ulong, + b"bookmark%d\x00" as *const u8 as *const libc::c_char, + j + 1 as libc::c_int); + if strcmp(token.as_mut_ptr(), bkey.as_mut_ptr()) == 0 { + value = + &mut *(*bookmarks.as_mut_ptr().offset(j as + isize)).as_mut_ptr().offset(0 + as + libc::c_int + as + isize) + as *mut libc::c_char; + break ; + } else { j += 1 } + } + if value.is_null() { return } + } + while *line as libc::c_int == ' ' as i32 || + *line as libc::c_int == '\t' as i32 { + line = line.offset(1) + } + i = 0 as libc::c_int; + while *line != 0 { + if i < 512 as libc::c_int - 1 as libc::c_int { + let fresh1 = i; + i = i + 1; + *value.offset(fresh1 as isize) = *line + } + line = line.offset(1) + } + i -= 1; + while i > 0 as libc::c_int && + (*value.offset(i as isize) as libc::c_int == ' ' as i32 || + *value.offset(i as isize) as libc::c_int == '\t' as i32) { + i -= 1 + } + i += 1; + *value.offset(i as isize) = 0 as libc::c_int as libc::c_char; +} +#[no_mangle] +pub unsafe extern "C" fn load_config(mut filename: *const libc::c_char) { + let mut fp: *mut FILE = 0 as *mut FILE; + let mut ch: libc::c_int = 0; + let mut i: libc::c_int = 0; + let mut line: [libc::c_char; 1024] = [0; 1024]; + fp = fopen(filename, b"r\x00" as *const u8 as *const libc::c_char); + if fp.is_null() { return } + memset(line.as_mut_ptr() as *mut libc::c_void, 0 as libc::c_int, + ::std::mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong); + i = 0 as libc::c_int; + ch = fgetc(fp); + loop { + match ch { + 35 => { + while ch != '\n' as i32 && ch != -(1 as libc::c_int) { + ch = fgetc(fp) + } + } + -1 => { parse_config_line(line.as_mut_ptr()); fclose(fp); return } + 13 => { ch = fgetc(fp) } + 10 => { + parse_config_line(line.as_mut_ptr()); + memset(line.as_mut_ptr() as *mut libc::c_void, + 0 as libc::c_int, + ::std::mem::size_of::<[libc::c_char; 1024]>() as + libc::c_ulong); + i = 0 as libc::c_int; + ch = fgetc(fp) + } + _ => { + if (i as libc::c_ulong) < + (::std::mem::size_of::<[libc::c_char; 1024]>() as + libc::c_ulong).wrapping_sub(1 as libc::c_int as + libc::c_ulong) { + let fresh2 = i; + i = i + 1; + line[fresh2 as usize] = ch as libc::c_char + } + ch = fgetc(fp) + } + } + }; +} +#[no_mangle] +pub unsafe extern "C" fn init_config() { + let mut filename: [libc::c_char; 1024] = [0; 1024]; + let mut home: *const libc::c_char = 0 as *const libc::c_char; + let mut i: libc::c_int = 0; + /* copy defaults */ + snprintf(config.start_uri.as_mut_ptr(), + ::std::mem::size_of::<[libc::c_char; 512]>() as libc::c_ulong, + b"gopher://gopher.floodgap.com:70\x00" as *const u8 as + *const libc::c_char); + snprintf(config.cmd_text.as_mut_ptr(), + ::std::mem::size_of::<[libc::c_char; 512]>() as libc::c_ulong, + b"%s\x00" as *const u8 as *const libc::c_char, + b"less\x00" as *const u8 as *const libc::c_char); + snprintf(config.cmd_image.as_mut_ptr(), + ::std::mem::size_of::<[libc::c_char; 512]>() as libc::c_ulong, + b"%s\x00" as *const u8 as *const libc::c_char, + b"display\x00" as *const u8 as *const libc::c_char); + snprintf(config.cmd_browser.as_mut_ptr(), + ::std::mem::size_of::<[libc::c_char; 512]>() as libc::c_ulong, + b"%s\x00" as *const u8 as *const libc::c_char, + b"firefox\x00" as *const u8 as *const libc::c_char); + snprintf(config.cmd_player.as_mut_ptr(), + ::std::mem::size_of::<[libc::c_char; 512]>() as libc::c_ulong, + b"%s\x00" as *const u8 as *const libc::c_char, + b"mplayer\x00" as *const u8 as *const libc::c_char); + snprintf(config.color_prompt.as_mut_ptr(), + ::std::mem::size_of::<[libc::c_char; 512]>() as libc::c_ulong, + b"%s\x00" as *const u8 as *const libc::c_char, + b"1;34\x00" as *const u8 as *const libc::c_char); + snprintf(config.color_selector.as_mut_ptr(), + ::std::mem::size_of::<[libc::c_char; 512]>() as libc::c_ulong, + b"%s\x00" as *const u8 as *const libc::c_char, + b"1;32\x00" as *const u8 as *const libc::c_char); + snprintf(config.verbose.as_mut_ptr(), + ::std::mem::size_of::<[libc::c_char; 512]>() as libc::c_ulong, + b"%s\x00" as *const u8 as *const libc::c_char, + b"true\x00" as *const u8 as *const libc::c_char); + i = 0 as libc::c_int; + while i < 20 as libc::c_int { + bookmarks[i as usize][0 as libc::c_int as usize] = + 0 as libc::c_int as libc::c_char; + i += 1 + } + /* read configs */ + load_config(b"/etc/cgorc\x00" as *const u8 as + *const libc::c_char); /* ignore incomplete selectors */ + home = + getenv(b"HOME\x00" as *const u8 as + *const libc::c_char); /* not needed for history...just clear them */ + if !home.is_null() { + snprintf(filename.as_mut_ptr(), + ::std::mem::size_of::<[libc::c_char; 1024]>() as + libc::c_ulong, + b"%s%s\x00" as *const u8 as *const libc::c_char, home, + b"/.cgorc\x00" as *const u8 as *const libc::c_char); + load_config(filename.as_mut_ptr()); + }; +} +#[no_mangle] +pub unsafe extern "C" fn dial(mut host: *const libc::c_char, + mut port: *const libc::c_char, + mut selector: *const libc::c_char) + -> libc::c_int { + let mut hints: addrinfo = + addrinfo{ai_flags: 0, + ai_family: 0, + ai_socktype: 0, + ai_protocol: 0, + ai_addrlen: 0, + ai_addr: 0 as *mut sockaddr, + ai_canonname: 0 as *mut libc::c_char, + ai_next: 0 as *mut addrinfo,}; + let mut res: *mut addrinfo = 0 as *mut addrinfo; + let mut r: *mut addrinfo = 0 as *mut addrinfo; + let mut srv: libc::c_int = -(1 as libc::c_int); + let mut l: libc::c_int = 0; + let mut request: [libc::c_char; 512] = [0; 512]; + memset(&mut hints as *mut addrinfo as *mut libc::c_void, 0 as libc::c_int, + ::std::mem::size_of::() as libc::c_ulong); + hints.ai_family = 0 as libc::c_int; + hints.ai_socktype = SOCK_STREAM as libc::c_int; + if getaddrinfo(host, port, &mut hints, &mut res) != 0 as libc::c_int { + fprintf(stderr, + b"error: cannot resolve hostname \'%s:%s\': %s\n\x00" as + *const u8 as *const libc::c_char, host, port, + strerror(*__errno_location())); + return -(1 as libc::c_int) + } + r = res; + while !r.is_null() { + srv = socket((*r).ai_family, (*r).ai_socktype, (*r).ai_protocol); + if !(srv == -(1 as libc::c_int)) { + if connect(srv, (*r).ai_addr, (*r).ai_addrlen) == 0 as libc::c_int + { + break ; + } + close(srv); + } + r = (*r).ai_next + } + freeaddrinfo(res); + if r.is_null() { + fprintf(stderr, + b"error: cannot connect to host \'%s:%s\'\n\x00" as *const u8 + as *const libc::c_char, host, port); + return -(1 as libc::c_int) + } + snprintf(request.as_mut_ptr(), + ::std::mem::size_of::<[libc::c_char; 512]>() as libc::c_ulong, + b"%s\r\n\x00" as *const u8 as *const libc::c_char, selector); + l = strlen(request.as_mut_ptr()) as libc::c_int; + if write(srv, request.as_mut_ptr() as *const libc::c_void, l as size_t) != + l as libc::c_long { + fprintf(stderr, + b"error: cannot complete request\n\x00" as *const u8 as + *const libc::c_char); + close(srv); + return -(1 as libc::c_int) + } + return srv; +} +#[no_mangle] +pub unsafe extern "C" fn read_line(mut fd: libc::c_int, + mut buf: *mut libc::c_char, + mut buf_len: size_t) -> libc::c_int { + let mut i: size_t = 0 as libc::c_int as size_t; + let mut c: libc::c_char = 0 as libc::c_int as libc::c_char; + loop { + if read(fd, &mut c as *mut libc::c_char as *mut libc::c_void, + ::std::mem::size_of::() as libc::c_ulong) as + libc::c_ulong != + ::std::mem::size_of::() as libc::c_ulong { + return 0 as libc::c_int + } + if c as libc::c_int != '\r' as i32 { + let fresh3 = i; + i = i.wrapping_add(1); + *buf.offset(fresh3 as isize) = c + } + if !(c as libc::c_int != '\n' as i32 && i < buf_len) { break ; } + } + *buf.offset(i.wrapping_sub(1 as libc::c_int as libc::c_ulong) as isize) = + '\u{0}' as i32 as libc::c_char; + return 1 as libc::c_int; +} +#[no_mangle] +pub unsafe extern "C" fn download_file(mut host: *const libc::c_char, + mut port: *const libc::c_char, + mut selector: *const libc::c_char, + mut fd: libc::c_int) -> libc::c_int { + let mut srvfd: libc::c_int = 0; + let mut len: libc::c_int = 0; + let mut total: libc::c_ulong = 0 as libc::c_int as libc::c_ulong; + let mut buffer: [libc::c_char; 4096] = [0; 4096]; + if check_option_true(config.verbose.as_mut_ptr()) != 0 { + printf(b"downloading [%s]...\r\x00" as *const u8 as + *const libc::c_char, selector); + } + srvfd = dial(host, port, selector); + if srvfd == -(1 as libc::c_int) { + printf(b"\x1b[2Kerror: downloading [%s] failed\n\x00" as *const u8 as + *const libc::c_char, selector); + close(fd); + return 0 as libc::c_int + } + loop { + len = + read(srvfd, buffer.as_mut_ptr() as *mut libc::c_void, + ::std::mem::size_of::<[libc::c_char; 4096]>() as + libc::c_ulong) as libc::c_int; + if !(len > 0 as libc::c_int) { break ; } + write(fd, buffer.as_mut_ptr() as *const libc::c_void, len as size_t); + total = total.wrapping_add(len as libc::c_ulong); + if check_option_true(config.verbose.as_mut_ptr()) != 0 { + printf(b"downloading [%s] (%ld kb)...\r\x00" as *const u8 as + *const libc::c_char, selector, + total.wrapping_div(1024 as libc::c_int as libc::c_ulong)); + } + } + close(fd); + close(srvfd); + if check_option_true(config.verbose.as_mut_ptr()) != 0 { + printf(b"\x1b[2Kdownloading [%s] complete\n\x00" as *const u8 as + *const libc::c_char, selector); + } + return 1 as libc::c_int; +} +#[no_mangle] +pub unsafe extern "C" fn download_temp(mut host: *const libc::c_char, + mut port: *const libc::c_char, + mut selector: *const libc::c_char) + -> libc::c_int { + let mut tmpfd: libc::c_int = 0; + strcpy(tmpfilename.as_mut_ptr(), + b"/tmp/cgoXXXXXX\x00" as *const u8 as *const libc::c_char); + tmpfd = mkstemp(tmpfilename.as_mut_ptr()); + if tmpfd == -(1 as libc::c_int) { + fputs(b"error: unable to create tmp file\n\x00" as *const u8 as + *const libc::c_char, stderr); + return 0 as libc::c_int + } + if download_file(host, port, selector, tmpfd) == 0 { + unlink(tmpfilename.as_mut_ptr()); + return 0 as libc::c_int + } + return 1 as libc::c_int; +} +#[no_mangle] +pub unsafe extern "C" fn make_key(mut c1: libc::c_char, mut c2: libc::c_char, + mut c3: libc::c_char) -> libc::c_int { + if c1 == 0 || c2 == 0 { return -(1 as libc::c_int) } + if c3 == 0 { + return (c1 as libc::c_int - 'a' as i32) * + ('z' as i32 - 'a' as i32 + 1 as libc::c_int) + + (c2 as libc::c_int - 'a' as i32) + } else { + return (c1 as libc::c_int - 'a' as i32 + 1 as libc::c_int) * + ('z' as i32 - 'a' as i32 + 1 as libc::c_int) * + ('z' as i32 - 'a' as i32 + 1 as libc::c_int) + + (c2 as libc::c_int - 'a' as i32) * + ('z' as i32 - 'a' as i32 + 1 as libc::c_int) + + (c3 as libc::c_int - 'a' as i32) + }; +} +#[no_mangle] +pub unsafe extern "C" fn make_key_str(mut key: libc::c_int, + mut c1: *mut libc::c_char, + mut c2: *mut libc::c_char, + mut c3: *mut libc::c_char) { + if key < + ('z' as i32 - 'a' as i32 + 1 as libc::c_int) * + ('z' as i32 - 'a' as i32 + 1 as libc::c_int) { + *c1 = + ('a' as i32 + key / ('z' as i32 - 'a' as i32 + 1 as libc::c_int)) + as libc::c_char; + *c2 = + ('a' as i32 + key % ('z' as i32 - 'a' as i32 + 1 as libc::c_int)) + as libc::c_char; + *c3 = 0 as libc::c_int as libc::c_char + } else { + *c1 = + ('a' as i32 + + key / + (('z' as i32 - 'a' as i32 + 1 as libc::c_int) * + ('z' as i32 - 'a' as i32 + 1 as libc::c_int)) - + 1 as libc::c_int) as libc::c_char; + *c2 = + ('a' as i32 + + key / ('z' as i32 - 'a' as i32 + 1 as libc::c_int) % + ('z' as i32 - 'a' as i32 + 1 as libc::c_int)) as + libc::c_char; + *c3 = + ('a' as i32 + key % ('z' as i32 - 'a' as i32 + 1 as libc::c_int)) + as libc::c_char + }; +} +#[no_mangle] +pub unsafe extern "C" fn add_link(mut which: libc::c_char, + mut name: *const libc::c_char, + mut host: *const libc::c_char, + mut port: *const libc::c_char, + mut selector: *const libc::c_char) { + let mut link: *mut link_t = 0 as *mut link_t; + let mut a: libc::c_char = 0 as libc::c_int as libc::c_char; + let mut b: libc::c_char = 0 as libc::c_int as libc::c_char; + let mut c: libc::c_char = 0 as libc::c_int as libc::c_char; + if host.is_null() || port.is_null() || selector.is_null() { return } + link = + calloc(1 as libc::c_int as libc::c_ulong, + ::std::mem::size_of::() as libc::c_ulong) as + *mut link_t; + (*link).which = which; + (*link).key = link_key as libc::c_short; + (*link).host = strdup(host); + (*link).port = strdup(port); + (*link).selector = strdup(selector); + if links.is_null() { + (*link).next = 0 as *mut link_t + } else { (*link).next = links } + links = link; + let fresh4 = link_key; + link_key = link_key + 1; + make_key_str(fresh4, &mut a, &mut b, &mut c); + printf(b"\x1b[%sm%c%c%c\x1b[0m \x1b[1m%s\x1b[0m\n\x00" as *const u8 as + *const libc::c_char, config.color_selector.as_mut_ptr(), + a as libc::c_int, b as libc::c_int, c as libc::c_int, name); +} +#[no_mangle] +pub unsafe extern "C" fn clear_links() { + let mut link: *mut link_t = 0 as *mut link_t; + let mut next: *mut link_t = 0 as *mut link_t; + link = links; + while !link.is_null() { + next = (*link).next; + free((*link).host as *mut libc::c_void); + free((*link).port as *mut libc::c_void); + free((*link).selector as *mut libc::c_void); + free(link as *mut libc::c_void); + link = next + } + links = 0 as *mut link_t; + link_key = 0 as libc::c_int; +} +#[no_mangle] +pub unsafe extern "C" fn add_history() { + let mut link: *mut link_t = 0 as *mut link_t; + link = + calloc(1 as libc::c_int as libc::c_ulong, + ::std::mem::size_of::() as libc::c_ulong) as + *mut link_t; + (*link).host = strdup(current_host.as_mut_ptr()); + (*link).port = strdup(current_port.as_mut_ptr()); + (*link).selector = strdup(current_selector.as_mut_ptr()); + (*link).which = 0 as libc::c_int as libc::c_char; + (*link).key = 0 as libc::c_int as libc::c_short; + if history.is_null() { + (*link).next = 0 as *mut link_t + } else { (*link).next = history } + history = link; +} +#[no_mangle] +pub unsafe extern "C" fn handle_directory_line(mut line: *mut libc::c_char) { + let mut i: libc::c_int = 0; + let mut lp: *mut libc::c_char = 0 as *mut libc::c_char; + let mut last: *mut libc::c_char = 0 as *mut libc::c_char; + let mut fields: [*mut libc::c_char; 4] = [0 as *mut libc::c_char; 4]; + /* tokenize */ + i = 0 as libc::c_int; + while i < 4 as libc::c_int { + fields[i as usize] = 0 as *mut libc::c_char; + i += 1 + } + last = &mut *line.offset(1 as libc::c_int as isize) as *mut libc::c_char; + lp = last; + i = 0 as libc::c_int; + while i < 4 as libc::c_int { + if *lp as libc::c_int == '\t' as i32 || + *lp as libc::c_int == '\u{0}' as i32 { + fields[i as usize] = last; + last = lp.offset(1 as libc::c_int as isize); + if *lp as libc::c_int == '\u{0}' as i32 { break ; } + *lp = '\u{0}' as i32 as libc::c_char; + i += 1 + } + lp = lp.offset(1) + } + /* determine listing type */ + match *line.offset(0 as libc::c_int as isize) as libc::c_int { + 105 | 51 => { + printf(b" %s\n\x00" as *const u8 as *const libc::c_char, + fields[0 as libc::c_int as usize]); + } + 46 => { + /* some gopher servers use this */ + puts(b"\x00" as *const u8 as *const libc::c_char); + } + 48 | 49 | 53 | 55 | 56 | 57 | 103 | 73 | 112 | 104 | 115 => { + add_link(*line.offset(0 as libc::c_int as isize), + fields[0 as libc::c_int as usize], + fields[2 as libc::c_int as usize], + fields[3 as libc::c_int as usize], + fields[1 as libc::c_int as usize]); + } + _ => { + printf(b"miss [%c]: %s\n\x00" as *const u8 as *const libc::c_char, + *line.offset(0 as libc::c_int as isize) as libc::c_int, + fields[0 as libc::c_int as usize]); + } + }; +} +#[no_mangle] +pub unsafe extern "C" fn is_valid_directory_entry(mut line: + *const libc::c_char) + -> libc::c_int { + match *line.offset(0 as libc::c_int as isize) as libc::c_int { + 105 | 51 | 46 | 48 | 49 | 53 | 55 | 56 | 57 | 103 | 73 | 112 | 104 | + 115 => { + /* some gopher servers use this */ + return 1 as libc::c_int + } + _ => { return 0 as libc::c_int } + }; +} +#[no_mangle] +pub unsafe extern "C" fn view_directory(mut host: *const libc::c_char, + mut port: *const libc::c_char, + mut selector: *const libc::c_char, + mut make_current: libc::c_int) { + let mut is_dir: libc::c_int = 0; + let mut srvfd: libc::c_int = 0; + let mut i: libc::c_int = 0; + let mut head_read: libc::c_int = 0; + let mut line: [libc::c_char; 1024] = [0; 1024]; + let mut head: [[libc::c_char; 1024]; 5] = [[0; 1024]; 5]; + srvfd = dial(host, port, selector); + if srvfd != -(1 as libc::c_int) { + /* only adapt current prompt when successful */ + /* make history entry */ + if make_current != 0 { add_history(); } + /* don't overwrite the current_* things... */ + if host != current_host.as_mut_ptr() as *const libc::c_char { + snprintf(current_host.as_mut_ptr(), + ::std::mem::size_of::<[libc::c_char; 512]>() as + libc::c_ulong, + b"%s\x00" as *const u8 as *const libc::c_char, + host); /* clear links *AFTER* dialing out!! */ + } /* quit if not successful */ + if port != current_port.as_mut_ptr() as *const libc::c_char { + snprintf(current_port.as_mut_ptr(), + ::std::mem::size_of::<[libc::c_char; 64]>() as + libc::c_ulong, + b"%s\x00" as *const u8 as *const libc::c_char, port); + } + if selector != current_selector.as_mut_ptr() as *const libc::c_char { + snprintf(current_selector.as_mut_ptr(), + ::std::mem::size_of::<[libc::c_char; 1024]>() as + libc::c_ulong, + b"%s\x00" as *const u8 as *const libc::c_char, selector); + } + } + clear_links(); + if srvfd == -(1 as libc::c_int) { return } + head_read = 0 as libc::c_int; + is_dir = 1 as libc::c_int; + while head_read < 5 as libc::c_int && + read_line(srvfd, line.as_mut_ptr(), + ::std::mem::size_of::<[libc::c_char; 1024]>() as + libc::c_ulong) != 0 { + strcpy(head[head_read as usize].as_mut_ptr(), line.as_mut_ptr()); + if is_valid_directory_entry(head[head_read as usize].as_mut_ptr()) == + 0 { + is_dir = 0 as libc::c_int; + break ; + } else { head_read += 1 } + } + if is_dir == 0 { + puts(b"error: Not a directory.\x00" as *const u8 as + *const libc::c_char); + close(srvfd); + return + } + i = 0 as libc::c_int; + while i < head_read { + handle_directory_line(head[i as usize].as_mut_ptr()); + i += 1 + } + while read_line(srvfd, line.as_mut_ptr(), + ::std::mem::size_of::<[libc::c_char; 1024]>() as + libc::c_ulong) != 0 { + handle_directory_line(line.as_mut_ptr()); + } + close(srvfd); +} +#[no_mangle] +pub unsafe extern "C" fn view_file(mut cmd: *const libc::c_char, + mut host: *const libc::c_char, + mut port: *const libc::c_char, + mut selector: *const libc::c_char) { + let mut pid: pid_t = 0; + let mut status: libc::c_int = 0; + let mut i: libc::c_int = 0; + let mut j: libc::c_int = 0; + let mut buffer: [libc::c_char; 1024] = [0; 1024]; + let mut argv: [*mut libc::c_char; 32] = [0 as *mut libc::c_char; 32]; + let mut p: *mut libc::c_char = 0 as *mut libc::c_char; + if check_option_true(config.verbose.as_mut_ptr()) != 0 { + printf(b"h(%s) p(%s) s(%s)\n\x00" as *const u8 as *const libc::c_char, + host, port, selector); + } + if download_temp(host, port, selector) == 0 { return } + /* parsed command line string */ + argv[0 as libc::c_int as usize] = + &mut *buffer.as_mut_ptr().offset(0 as libc::c_int as isize) as + *mut libc::c_char; + p = cmd as *mut libc::c_char; + i = 0 as libc::c_int; + j = 1 as libc::c_int; + while *p as libc::c_int != 0 && + (i as libc::c_ulong) < + (::std::mem::size_of::<[libc::c_char; 1024]>() as + libc::c_ulong).wrapping_sub(1 as libc::c_int as + libc::c_ulong) && + j < 30 as libc::c_int { + if *p as libc::c_int == ' ' as i32 || *p as libc::c_int == '\t' as i32 + { + let fresh5 = i; + i = i + 1; + buffer[fresh5 as usize] = 0 as libc::c_int as libc::c_char; + let fresh6 = j; + j = j + 1; + argv[fresh6 as usize] = + &mut *buffer.as_mut_ptr().offset(i as isize) as + *mut libc::c_char; + while *p as libc::c_int == ' ' as i32 || + *p as libc::c_int == '\t' as i32 { + p = p.offset(1) + } + } else { + let fresh7 = p; + p = p.offset(1); + let fresh8 = i; + i = i + 1; + buffer[fresh8 as usize] = *fresh7 + } + } + buffer[i as usize] = 0 as libc::c_int as libc::c_char; + let fresh9 = j; + j = j + 1; + argv[fresh9 as usize] = tmpfilename.as_mut_ptr(); + argv[j as usize] = 0 as *mut libc::c_char; + /* fork and execute */ + if check_option_true(config.verbose.as_mut_ptr()) != 0 { + printf(b"executing: %s %s\n\x00" as *const u8 as *const libc::c_char, + cmd, + tmpfilename.as_mut_ptr()); /* to wait for browsers etc. that return immediatly */ + } + pid = fork(); + if pid == 0 as libc::c_int { + if execvp(argv[0 as libc::c_int as usize], + argv.as_mut_ptr() as *const *mut libc::c_char) == + -(1 as libc::c_int) { + puts(b"error: execvp() failed!\x00" as *const u8 as + *const libc::c_char); + } + } else if pid == -(1 as libc::c_int) { + puts(b"error: fork() failed\x00" as *const u8 as *const libc::c_char); + } + sleep(1 as libc::c_int as libc::c_uint); + waitpid(pid, &mut status, 0 as libc::c_int); + unlink(tmpfilename.as_mut_ptr()); +} +#[no_mangle] +pub unsafe extern "C" fn view_telnet(mut host: *const libc::c_char, + mut port: *const libc::c_char) { + let mut pid: pid_t = 0; + let mut status: libc::c_int = 0; + printf(b"executing: %s %s %s\n\x00" as *const u8 as *const libc::c_char, + b"telnet\x00" as *const u8 as *const libc::c_char, host, port); + pid = fork(); + if pid == 0 as libc::c_int { + if execlp(b"telnet\x00" as *const u8 as *const libc::c_char, + b"telnet\x00" as *const u8 as *const libc::c_char, host, + port, 0 as *mut libc::c_void) == -(1 as libc::c_int) { + puts(b"error: execlp() failed!\x00" as *const u8 as + *const libc::c_char); + } + } else if pid == -(1 as libc::c_int) { + puts(b"error: fork() failed!\x00" as *const u8 as + *const libc::c_char); + } + waitpid(pid, &mut status, 0 as libc::c_int); + puts(b"(done)\x00" as *const u8 as *const libc::c_char); +} +#[no_mangle] +pub unsafe extern "C" fn view_download(mut host: *const libc::c_char, + mut port: *const libc::c_char, + mut selector: *const libc::c_char) { + let mut fd: libc::c_int = 0; + let mut filename: [libc::c_char; 1024] = [0; 1024]; + let mut line: [libc::c_char; 1024] = [0; 1024]; + snprintf(filename.as_mut_ptr(), + ::std::mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong, + b"%s\x00" as *const u8 as *const libc::c_char, + strrchr(selector, '/' as i32).offset(1 as libc::c_int as isize)); + printf(b"enter filename for download [%s]: \x00" as *const u8 as + *const libc::c_char, filename.as_mut_ptr()); + fflush(stdout); + if read_line(0 as libc::c_int, line.as_mut_ptr(), + ::std::mem::size_of::<[libc::c_char; 1024]>() as + libc::c_ulong) == 0 { + puts(b"download aborted\x00" as *const u8 as *const libc::c_char); + return + } + if strlen(line.as_mut_ptr()) > 0 as libc::c_int as libc::c_ulong { + strcpy(filename.as_mut_ptr(), line.as_mut_ptr()); + } + fd = + open(filename.as_mut_ptr(), 0o100 as libc::c_int | 0o1 as libc::c_int, + 0o400 as libc::c_int | 0o200 as libc::c_int); + if fd == -(1 as libc::c_int) { + printf(b"error: unable to create file [%s]: %s\n\x00" as *const u8 as + *const libc::c_char, filename.as_mut_ptr(), + strerror(*__errno_location())); + return + } + if download_file(host, port, selector, fd) == 0 { + printf(b"error: unable to download [%s]\n\x00" as *const u8 as + *const libc::c_char, selector); + unlink(filename.as_mut_ptr()); + return + }; +} +#[no_mangle] +pub unsafe extern "C" fn view_search(mut host: *const libc::c_char, + mut port: *const libc::c_char, + mut selector: *const libc::c_char) { + let mut search_selector: [libc::c_char; 1024] = [0; 1024]; + let mut line: [libc::c_char; 1024] = [0; 1024]; + printf(b"enter search string: \x00" as *const u8 as *const libc::c_char); + fflush(stdout); + if read_line(0 as libc::c_int, line.as_mut_ptr(), + ::std::mem::size_of::<[libc::c_char; 1024]>() as + libc::c_ulong) == 0 { + puts(b"search aborted\x00" as *const u8 as *const libc::c_char); + return + } + snprintf(search_selector.as_mut_ptr(), + ::std::mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong, + b"%s\t%s\x00" as *const u8 as *const libc::c_char, selector, + line.as_mut_ptr()); + view_directory(host, port, search_selector.as_mut_ptr(), + 1 as libc::c_int); +} +#[no_mangle] +pub unsafe extern "C" fn view_history(mut key: libc::c_int) { + let mut history_key: libc::c_int = 0 as libc::c_int; + let mut a: libc::c_char = 0; + let mut b: libc::c_char = 0; + let mut c: libc::c_char = 0; + let mut link: *mut link_t = 0 as *mut link_t; + if history.is_null() { + puts(b"(empty history)\x00" as *const u8 as *const libc::c_char); + return + } + if key < 0 as libc::c_int { + puts(b"(history)\x00" as *const u8 as *const libc::c_char); + link = history; + while !link.is_null() { + let fresh10 = history_key; + history_key = history_key + 1; + make_key_str(fresh10, &mut a, &mut b, &mut c); + printf(b"\x1b[%sm%c%c%c\x1b[0m \x1b[1m%s:%s/1%s\x1b[0m\n\x00" as + *const u8 as *const libc::c_char, + b"1;32\x00" as *const u8 as *const libc::c_char, + a as libc::c_int, b as libc::c_int, c as libc::c_int, + (*link).host, (*link).port, (*link).selector); + link = (*link).next + } + } else { + /* traverse history list */ + link = history; + while !link.is_null() { + if history_key == key { + view_directory((*link).host, (*link).port, (*link).selector, + 0 as libc::c_int); + return + } + link = (*link).next; + history_key += 1 + } + puts(b"history item not found\x00" as *const u8 as + *const libc::c_char); + }; +} +#[no_mangle] +pub unsafe extern "C" fn view_bookmarks(mut key: libc::c_int) { + let mut i: libc::c_int = 0; + let mut a: libc::c_char = 0; + let mut b: libc::c_char = 0; + let mut c: libc::c_char = 0; + if key < 0 as libc::c_int { + puts(b"(bookmarks)\x00" as *const u8 as *const libc::c_char); + i = 0 as libc::c_int; + while i < 20 as libc::c_int { + if bookmarks[i as usize][0 as libc::c_int as usize] != 0 { + make_key_str(i, &mut a, &mut b, &mut c); + printf(b"\x1b[%sm%c%c%c\x1b[0m \x1b[1m%s\x1b[0m\n\x00" as + *const u8 as *const libc::c_char, + b"1;32\x00" as *const u8 as *const libc::c_char, + a as libc::c_int, b as libc::c_int, c as libc::c_int, + &mut *(*bookmarks.as_mut_ptr().offset(i as + isize)).as_mut_ptr().offset(0 + as + libc::c_int + as + isize) + as *mut libc::c_char); + } + i += 1 + } + } else { + i = 0 as libc::c_int; + while i < 20 as libc::c_int { + if bookmarks[i as usize][0 as libc::c_int as usize] as libc::c_int + != 0 && i == key { + if parse_uri(&mut *(*bookmarks.as_mut_ptr().offset(i as + isize)).as_mut_ptr().offset(0 + as + libc::c_int + as + isize)) + != 0 { + view_directory(parsed_host.as_mut_ptr(), + parsed_port.as_mut_ptr(), + parsed_selector.as_mut_ptr(), + 0 as libc::c_int); + } else { + printf(b"invalid gopher URI: %s\x00" as *const u8 as + *const libc::c_char, + &mut *(*bookmarks.as_mut_ptr().offset(i as + isize)).as_mut_ptr().offset(0 + as + libc::c_int + as + isize) + as *mut libc::c_char); + } + return + } + i += 1 + } + }; +} +#[no_mangle] +pub unsafe extern "C" fn pop_history() { + let mut next: *mut link_t = 0 as *mut link_t; + if history.is_null() { + puts(b"(empty history)\x00" as *const u8 as *const libc::c_char); + return + } + /* reload page from history (and don't count as history) */ + view_directory((*history).host, (*history).port, (*history).selector, + 0 as libc::c_int); + /* history is history... :) */ + next = (*history).next; + free((*history).host as *mut libc::c_void); + free((*history).port as *mut libc::c_void); + free((*history).selector as *mut libc::c_void); + free(history as *mut libc::c_void); + history = next; +} +#[no_mangle] +pub unsafe extern "C" fn follow_link(mut key: libc::c_int) -> libc::c_int { + let mut link: *mut link_t = 0 as *mut link_t; + link = links; + while !link.is_null() { + if (*link).key as libc::c_int != key { + link = (*link).next + } else { + match (*link).which as libc::c_int { + 48 => { + view_file(&mut *config.cmd_text.as_mut_ptr().offset(0 as + libc::c_int + as + isize), + (*link).host, (*link).port, (*link).selector); + } + 49 => { + view_directory((*link).host, (*link).port, + (*link).selector, 1 as libc::c_int); + } + 55 => { + view_search((*link).host, (*link).port, (*link).selector); + } + 53 | 57 => { + view_download((*link).host, (*link).port, + (*link).selector); + } + 56 => { view_telnet((*link).host, (*link).port); } + 103 | 73 | 112 => { + view_file(&mut *config.cmd_image.as_mut_ptr().offset(0 as + libc::c_int + as + isize), + (*link).host, (*link).port, (*link).selector); + } + 104 => { + view_file(&mut *config.cmd_browser.as_mut_ptr().offset(0 + as + libc::c_int + as + isize), + (*link).host, (*link).port, (*link).selector); + } + 115 => { + view_file(&mut *config.cmd_player.as_mut_ptr().offset(0 as + libc::c_int + as + isize), + (*link).host, (*link).port, (*link).selector); + } + _ => { + printf(b"missing handler [%c]\n\x00" as *const u8 as + *const libc::c_char, + (*link).which as libc::c_int); + } + } + return 1 as libc::c_int + } + /* return the array is broken after view! */ + } + return 0 as libc::c_int; +} +#[no_mangle] +pub unsafe extern "C" fn download_link(mut key: libc::c_int) { + let mut link: *mut link_t = 0 as *mut link_t; + link = links; + while !link.is_null() { + if (*link).key as libc::c_int != key { + link = (*link).next + } else { + view_download((*link).host, (*link).port, (*link).selector); + return + } + } + puts(b"link not found\x00" as *const u8 as *const libc::c_char); +} +/* function prototypes */ +#[no_mangle] +pub unsafe extern "C" fn parse_uri(mut uri: *const libc::c_char) + -> libc::c_int { + let mut i: libc::c_int = 0; + /* strip gopher:// */ + if strncmp(uri, b"gopher://\x00" as *const u8 as *const libc::c_char, + 9 as libc::c_int as libc::c_ulong) == 0 { + uri = uri.offset(9 as libc::c_int as isize) + } + /* parse host */ + i = 0 as libc::c_int; + while *uri as libc::c_int != 0 && *uri as libc::c_int != ':' as i32 && + *uri as libc::c_int != '/' as i32 { + if *uri as libc::c_int != ' ' as i32 && + (i as libc::c_ulong) < + (::std::mem::size_of::<[libc::c_char; 512]>() as + libc::c_ulong).wrapping_sub(1 as libc::c_int as + libc::c_ulong) { + let fresh11 = i; + i = i + 1; + parsed_host[fresh11 as usize] = *uri + } + uri = uri.offset(1) + } + if i > 0 as libc::c_int { + parsed_host[i as usize] = 0 as libc::c_int as libc::c_char + } else { return 0 as libc::c_int } + /* parse port */ + if *uri as libc::c_int == ':' as i32 { + uri = uri.offset(1); + i = 0 as libc::c_int; + while *uri as libc::c_int != 0 && *uri as libc::c_int != '/' as i32 { + if *uri as libc::c_int != ' ' as i32 && + (i as libc::c_ulong) < + (::std::mem::size_of::<[libc::c_char; 64]>() as + libc::c_ulong).wrapping_sub(1 as libc::c_int as + libc::c_ulong) { + let fresh12 = i; + i = i + 1; + parsed_port[fresh12 as usize] = *uri + } + uri = uri.offset(1) + } + parsed_port[i as usize] = 0 as libc::c_int as libc::c_char + } else { + snprintf(parsed_port.as_mut_ptr(), + ::std::mem::size_of::<[libc::c_char; 64]>() as libc::c_ulong, + b"%d\x00" as *const u8 as *const libc::c_char, + 70 as libc::c_int); + } + /* parse selector (ignore slash and selector type) */ + if *uri != 0 { uri = uri.offset(1) } + if *uri != 0 { uri = uri.offset(1) } + i = 0 as libc::c_int; + while *uri as libc::c_int != 0 && + (i as libc::c_ulong) < + (::std::mem::size_of::<[libc::c_char; 1024]>() as + libc::c_ulong).wrapping_sub(1 as libc::c_int as + libc::c_ulong) { + parsed_selector[i as usize] = *uri; + uri = uri.offset(1); + i += 1 + } + parsed_selector[i as usize] = '\u{0}' as i32 as libc::c_char; + return 1 as libc::c_int; +} +unsafe fn main_0(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char) + -> libc::c_int { + let mut i: libc::c_int = 0; + let mut line: [libc::c_char; 1024] = [0; 1024]; + let mut uri: *mut libc::c_char = 0 as *mut libc::c_char; + /* copy defaults */ + init_config(); + uri = + &mut *config.start_uri.as_mut_ptr().offset(0 as libc::c_int as isize) + as *mut libc::c_char; + /* parse command line */ + i = 1 as libc::c_int; + while i < argc { + if *(*argv.offset(i as isize)).offset(0 as libc::c_int as isize) as + libc::c_int == '-' as i32 { + match *(*argv.offset(i as + isize)).offset(1 as libc::c_int as isize) + as libc::c_int { + 72 => { usage(); } + 118 => { banner(stdout); exit(0 as libc::c_int); } + _ => { usage(); } + } + } else { uri = *argv.offset(i as isize) } + i += 1 + } + /* parse uri */ + if parse_uri(uri) == 0 { + banner(stderr); + fprintf(stderr, + b"invalid gopher URI: %s\x00" as *const u8 as + *const libc::c_char, *argv.offset(i as isize)); + exit(1 as libc::c_int); + } + /* main loop */ + view_directory(parsed_host.as_mut_ptr(), parsed_port.as_mut_ptr(), + parsed_selector.as_mut_ptr(), + 0 as libc::c_int); /* to display the prompt */ + loop { + printf(b"\x1b[%sm%s:%s%s\x1b[0m \x00" as *const u8 as + *const libc::c_char, config.color_prompt.as_mut_ptr(), + current_host.as_mut_ptr(), current_port.as_mut_ptr(), + current_selector.as_mut_ptr()); + fflush(stdout); + if read_line(0 as libc::c_int, line.as_mut_ptr(), + ::std::mem::size_of::<[libc::c_char; 1024]>() as + libc::c_ulong) == 0 { + puts(b"QUIT\x00" as *const u8 as *const libc::c_char); + return 0 as libc::c_int + } + i = strlen(line.as_mut_ptr()) as libc::c_int; + match line[0 as libc::c_int as usize] as libc::c_int { + 63 => { + puts(b"? - help\n* - reload directory\n< - go back in history\n.[LINK] - download the given link\nH - show history\nH[LINK] - jump to the specified history item\nG[URI] - jump to the given gopher URI\nB - show bookmarks\nB[LINK] - jump to the specified bookmark item\nC^d - quit\x00" + as *const u8 as *const libc::c_char); + } + 60 => { pop_history(); } + 42 => { + view_directory(current_host.as_mut_ptr(), + current_port.as_mut_ptr(), + current_selector.as_mut_ptr(), + 0 as libc::c_int); + } + 46 => { + download_link(make_key(line[1 as libc::c_int as usize], + line[2 as libc::c_int as usize], + line[3 as libc::c_int as usize])); + } + 72 => { + if i == 1 as libc::c_int || i == 3 as libc::c_int || + i == 4 as libc::c_int { + view_history(make_key(line[1 as libc::c_int as usize], + line[2 as libc::c_int as usize], + line[3 as libc::c_int as usize])); + } + } + 71 => { + if parse_uri(&mut *line.as_mut_ptr().offset(1 as libc::c_int + as isize)) != + 0 { + view_directory(parsed_host.as_mut_ptr(), + parsed_port.as_mut_ptr(), + parsed_selector.as_mut_ptr(), + 1 as libc::c_int); + } else { + puts(b"invalid gopher URI\x00" as *const u8 as + *const libc::c_char); + } + } + 66 => { + if i == 1 as libc::c_int || i == 3 as libc::c_int || + i == 4 as libc::c_int { + view_bookmarks(make_key(line[1 as libc::c_int as usize], + line[2 as libc::c_int as usize], + line[3 as libc::c_int as usize])); + } + } + _ => { + follow_link(make_key(line[0 as libc::c_int as usize], + line[1 as libc::c_int as usize], + line[2 as libc::c_int as usize])); + } + } + }; + /* never get's here but stops cc complaining */ +} +#[main] +pub fn main() { + let mut args: Vec<*mut libc::c_char> = Vec::new(); + for arg in ::std::env::args() { + args.push(::std::ffi::CString::new(arg).expect("Failed to convert argument into CString.").into_raw()); + }; + args.push(::std::ptr::null_mut()); + unsafe { + ::std::process::exit(main_0((args.len() - 1) as libc::c_int, + args.as_mut_ptr() as + *mut *mut libc::c_char) as i32) + } +} diff --git a/compile_commands.json b/compile_commands.json new file mode 100644 index 0000000..969ee11 --- /dev/null +++ b/compile_commands.json @@ -0,0 +1,11 @@ +[ + { + "arguments": [ + "cc", + "-c", + "cgo.c" + ], + "directory": "/root/rgp", + "file": "cgo.c" + } +] \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..1ed61f0 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,1625 @@ +#![allow( + dead_code, + mutable_transmutes, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unused_assignments, + unused_mut +)] +#![register_tool(c2rust)] +#![feature(const_raw_ptr_to_usize_cast, extern_types, main, register_tool)] + +extern crate libc; +#[macro_use] extern crate lazy_static; + +use std::env; +use std::process::exit; +use std::thread::sleep; +use std::time::Duration; +use std::ffi::{CStr, CString}; +use std::mem; +use std::ptr; + +extern "C" { + pub type _IO_wide_data; + pub type _IO_codecvt; + pub type _IO_marker; + #[no_mangle] + fn waitpid( + __pid: __pid_t, + __stat_loc: *mut libc::c_int, + __options: libc::c_int, + ) -> __pid_t; + #[no_mangle] + fn socket( + __domain: libc::c_int, + __type: libc::c_int, + __protocol: libc::c_int, + ) -> libc::c_int; + #[no_mangle] + fn connect( + __fd: libc::c_int, + __addr: *const sockaddr, + __len: socklen_t, + ) -> libc::c_int; + #[no_mangle] + fn getaddrinfo( + __name: *const libc::c_char, + __service: *const libc::c_char, + __req: *const addrinfo, + __pai: *mut *mut addrinfo, + ) -> libc::c_int; + #[no_mangle] + fn freeaddrinfo(__ai: *mut addrinfo); + #[no_mangle] + fn open(__file: *const libc::c_char, __oflag: libc::c_int, _: ...) -> libc::c_int; + #[no_mangle] + fn close(__fd: libc::c_int) -> libc::c_int; + #[no_mangle] + fn read(__fd: libc::c_int, __buf: *mut libc::c_void, __nbytes: size_t) -> ssize_t; + #[no_mangle] + fn write(__fd: libc::c_int, __buf: *const libc::c_void, __n: size_t) -> ssize_t; + #[no_mangle] + fn execvp( + __file: *const libc::c_char, + __argv: *const *mut libc::c_char, + ) -> libc::c_int; + #[no_mangle] + fn execlp( + __file: *const libc::c_char, + __arg: *const libc::c_char, + _: ... + ) -> libc::c_int; + #[no_mangle] + fn fork() -> __pid_t; + #[no_mangle] + fn unlink(__name: *const libc::c_char) -> libc::c_int; + #[no_mangle] + fn __errno_location() -> *mut libc::c_int; + #[no_mangle] + static mut stderr: *mut FILE; + #[no_mangle] + fn fclose(__stream: *mut FILE) -> libc::c_int; + #[no_mangle] + fn fopen(_: *const libc::c_char, _: *const libc::c_char) -> *mut FILE; + #[no_mangle] + fn fprintf(_: *mut FILE, _: *const libc::c_char, _: ...) -> libc::c_int; + #[no_mangle] + fn printf(_: *const libc::c_char, _: ...) -> libc::c_int; + #[no_mangle] + fn snprintf( + _: *mut libc::c_char, + _: libc::c_ulong, + _: *const libc::c_char, + _: ... + ) -> libc::c_int; + #[no_mangle] + fn fgetc(__stream: *mut FILE) -> libc::c_int; + #[no_mangle] + fn fputs(__s: *const libc::c_char, __stream: *mut FILE) -> libc::c_int; + #[no_mangle] + fn puts(__s: *const libc::c_char) -> libc::c_int; + #[no_mangle] + fn calloc(_: libc::c_ulong, _: libc::c_ulong) -> *mut libc::c_void; + #[no_mangle] + fn free(__ptr: *mut libc::c_void); + #[no_mangle] + fn getenv(__name: *const libc::c_char) -> *mut libc::c_char; + #[no_mangle] + fn mkstemp(__template: *mut libc::c_char) -> libc::c_int; + #[no_mangle] + fn memset( + _: *mut libc::c_void, + _: libc::c_int, + _: libc::c_ulong, + ) -> *mut libc::c_void; + #[no_mangle] + fn strcpy(_: *mut libc::c_char, _: *const libc::c_char) -> *mut libc::c_char; + #[no_mangle] + fn strcmp(_: *const libc::c_char, _: *const libc::c_char) -> libc::c_int; + #[no_mangle] + fn strncmp( + _: *const libc::c_char, + _: *const libc::c_char, + _: libc::c_ulong, + ) -> libc::c_int; + #[no_mangle] + fn strdup(_: *const libc::c_char) -> *mut libc::c_char; + #[no_mangle] + fn strrchr(_: *const libc::c_char, _: libc::c_int) -> *mut libc::c_char; + #[no_mangle] + fn strcasecmp(_: *const libc::c_char, _: *const libc::c_char) -> libc::c_int; + #[no_mangle] + fn strlen(_: *const libc::c_char) -> libc::c_ulong; + #[no_mangle] + fn strerror(_: libc::c_int) -> *mut libc::c_char; +} +pub type __off_t = libc::c_long; +pub type __off64_t = libc::c_long; +pub type __pid_t = libc::c_int; +pub type __ssize_t = libc::c_long; +pub type __socklen_t = libc::c_uint; +pub type pid_t = __pid_t; +pub type ssize_t = __ssize_t; +pub type size_t = libc::c_ulong; +pub type socklen_t = __socklen_t; +pub type __socket_type = libc::c_uint; +pub const SOCK_NONBLOCK: __socket_type = 2048; +pub const SOCK_CLOEXEC: __socket_type = 524288; +pub const SOCK_PACKET: __socket_type = 10; +pub const SOCK_DCCP: __socket_type = 6; +pub const SOCK_SEQPACKET: __socket_type = 5; +pub const SOCK_RDM: __socket_type = 4; +pub const SOCK_RAW: __socket_type = 3; +pub const SOCK_DGRAM: __socket_type = 2; +pub const SOCK_STREAM: __socket_type = 1; +pub type sa_family_t = libc::c_ushort; +#[derive(Copy, Clone)] +#[repr(C)] +pub struct sockaddr { + pub sa_family: sa_family_t, + pub sa_data: [libc::c_char; 14], +} +#[derive(Copy, Clone)] +#[repr(C)] +pub struct addrinfo { + pub ai_flags: libc::c_int, + pub ai_family: libc::c_int, + pub ai_socktype: libc::c_int, + pub ai_protocol: libc::c_int, + pub ai_addrlen: socklen_t, + pub ai_addr: *mut sockaddr, + pub ai_canonname: *mut libc::c_char, + pub ai_next: *mut addrinfo, +} +#[derive(Copy, Clone)] +#[repr(C)] +pub struct _IO_FILE { + pub _flags: libc::c_int, + pub _IO_read_ptr: *mut libc::c_char, + pub _IO_read_end: *mut libc::c_char, + pub _IO_read_base: *mut libc::c_char, + pub _IO_write_base: *mut libc::c_char, + pub _IO_write_ptr: *mut libc::c_char, + pub _IO_write_end: *mut libc::c_char, + pub _IO_buf_base: *mut libc::c_char, + pub _IO_buf_end: *mut libc::c_char, + pub _IO_save_base: *mut libc::c_char, + pub _IO_backup_base: *mut libc::c_char, + pub _IO_save_end: *mut libc::c_char, + pub _markers: *mut _IO_marker, + pub _chain: *mut _IO_FILE, + pub _fileno: libc::c_int, + pub _flags2: libc::c_int, + pub _old_offset: __off_t, + pub _cur_column: libc::c_ushort, + pub _vtable_offset: libc::c_schar, + pub _shortbuf: [libc::c_char; 1], + pub _lock: *mut libc::c_void, + pub _offset: __off64_t, + pub _codecvt: *mut _IO_codecvt, + pub _wide_data: *mut _IO_wide_data, + pub _freeres_list: *mut _IO_FILE, + pub _freeres_buf: *mut libc::c_void, + pub __pad5: size_t, + pub _mode: libc::c_int, + pub _unused2: [libc::c_char; 20], +} +pub type _IO_lock_t = (); +pub type FILE = _IO_FILE; + +/* structs */ +#[derive(Copy, Clone)] +pub struct Link { + pub next: *mut Link, + pub which: libc::c_char, + pub key: libc::c_short, + pub host: *mut libc::c_char, + pub port: *mut libc::c_char, + pub selector: *mut libc::c_char, +} +#[derive(Clone)] +pub struct Config { + pub start_uri: String, //[libc::c_char; 512], + pub cmd_text: String, //[libc::c_char; 512], + pub cmd_image: String, //[libc::c_char; 512], + pub cmd_browser: String, //[libc::c_char; 512], + pub cmd_player: String, //[libc::c_char; 512], + pub color_prompt: String, //[libc::c_char; 512], + pub color_selector: String, //[libc::c_char; 512], + pub verbose: String, //[libc::c_char; 512], +} + +pub static mut tmpfilename: [libc::c_char; 256] = [0; 256]; +pub static mut links: *mut Link = 0 as *const Link as *mut Link; +pub static mut history: *mut Link = 0 as *const Link as *mut Link; +pub static mut link_key: libc::c_int = 0; +pub static mut current_host: [libc::c_char; 512] = [0; 512]; +pub static mut current_port: [libc::c_char; 64] = [0; 64]; +pub static mut current_selector: [libc::c_char; 1024] = [0; 1024]; +pub static mut parsed_host: [libc::c_char; 512] = [0; 512]; +pub static mut parsed_port: [libc::c_char; 64] = [0; 64]; +pub static mut parsed_selector: [libc::c_char; 1024] = [0; 1024]; +pub static mut bookmarks: [[libc::c_char; 512]; 20] = [[0; 512]; 20]; + +lazy_static! { + pub static ref config: Config = Config { + start_uri: String::from("gopher://gopher.floodgap.com:70"), + cmd_text: String::from("less"), + cmd_image: String::from("display"), + cmd_browser: String::from("firefox"), + cmd_player: String::from("mplayer"), + color_prompt: String::from("1;34"), + color_selector: String::from("1;32"), + verbose: String::from("true"), + }; +} + +/* implementation */ +pub fn usage() { + eprintln!("usage: cgo [-v] [-H] [gopher URI]"); + exit(0); +} +#[no_mangle] +pub fn banner(to_error: bool) { + if to_error { + eprintln!("cgo 0.6.1 Copyright (c) 2020 Sebastian Steinhauer"); + } else { + println!("cgo 0.6.1 Copyright (c) 2020 Sebastian Steinhauer"); + } +} +#[no_mangle] +pub unsafe extern "C" fn check_option_true( + mut option: *const libc::c_char, +) -> libc::c_int { + return (strcasecmp(option, b"false\x00" as *const u8 as *const libc::c_char) != 0 + && strcasecmp(option, b"off\x00" as *const u8 as *const libc::c_char) != 0) + as libc::c_int; +} +#[no_mangle] +pub unsafe extern "C" fn parse_config_line(mut line: *const libc::c_char) { + let mut token: [libc::c_char; 1024] = [0; 1024]; + let mut bkey: [libc::c_char; 128] = [0; 128]; + let mut value: *mut libc::c_char = 0 as *mut libc::c_char; + let mut i: libc::c_int = 0; + let mut j: libc::c_int = 0; + while *line as libc::c_int == ' ' as i32 || *line as libc::c_int == '\t' as i32 { + line = line.offset(1) + } + i = 0 as libc::c_int; + while *line as libc::c_int != 0 + && *line as libc::c_int != ' ' as i32 + && *line as libc::c_int != '\t' as i32 + { + if (i as libc::c_ulong) + < (mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong) + .wrapping_sub(1 as libc::c_int as libc::c_ulong) + { + let fresh0 = i; + i = i + 1; + token[fresh0 as usize] = *line + } + line = line.offset(1) + } + token[i as usize] = 0 as libc::c_int as libc::c_char; + if strcmp(token.as_mut_ptr(), b"start_uri\x00" as *const u8 as *const libc::c_char) + == 0 + { + value = CString::new(config.start_uri.clone()).unwrap().into_raw(); + } else if strcmp( + token.as_mut_ptr(), + b"cmd_text\x00" as *const u8 as *const libc::c_char, + ) == 0 + { + value = CString::new(config.cmd_text.clone()).unwrap().into_raw(); + } else if strcmp( + token.as_mut_ptr(), + b"cmd_browser\x00" as *const u8 as *const libc::c_char, + ) == 0 + { + value = CString::new(config.cmd_browser.clone()).unwrap().into_raw(); + } else if strcmp( + token.as_mut_ptr(), + b"cmd_image\x00" as *const u8 as *const libc::c_char, + ) == 0 + { + value = CString::new(config.cmd_image.clone()).unwrap().into_raw(); + } else if strcmp( + token.as_mut_ptr(), + b"cmd_player\x00" as *const u8 as *const libc::c_char, + ) == 0 + { + value = CString::new(config.cmd_player.clone()).unwrap().into_raw(); + } else if strcmp( + token.as_mut_ptr(), + b"color_prompt\x00" as *const u8 as *const libc::c_char, + ) == 0 + { + value = CString::new(config.color_prompt.clone()).unwrap().into_raw(); + } else if strcmp( + token.as_mut_ptr(), + b"color_selector\x00" as *const u8 as *const libc::c_char, + ) == 0 + { + value = CString::new(config.color_selector.clone()).unwrap().into_raw(); + } else if strcmp( + token.as_mut_ptr(), + b"verbose\x00" as *const u8 as *const libc::c_char, + ) == 0 + { + value = CString::new(config.verbose.clone()).unwrap().into_raw(); + } else { + j = 0 as libc::c_int; + while j < 20 as libc::c_int { + snprintf( + bkey.as_mut_ptr(), + mem::size_of::<[libc::c_char; 128]>() as libc::c_ulong, + b"bookmark%d\x00" as *const u8 as *const libc::c_char, + j + 1 as libc::c_int, + ); + if strcmp(token.as_mut_ptr(), bkey.as_mut_ptr()) == 0 { + value = &mut *(*bookmarks.as_mut_ptr().offset(j as isize)) + .as_mut_ptr() + .offset(0 as libc::c_int as isize) as *mut libc::c_char; + break; + } else { + j += 1 + } + } + if value.is_null() { + return; + } + } + while *line as libc::c_int == ' ' as i32 || *line as libc::c_int == '\t' as i32 { + line = line.offset(1) + } + i = 0 as libc::c_int; + while *line != 0 { + if i < 512 as libc::c_int - 1 as libc::c_int { + let fresh1 = i; + i = i + 1; + *value.offset(fresh1 as isize) = *line + } + line = line.offset(1) + } + i -= 1; + while i > 0 as libc::c_int + && (*value.offset(i as isize) as libc::c_int == ' ' as i32 + || *value.offset(i as isize) as libc::c_int == '\t' as i32) + { + i -= 1 + } + i += 1; + *value.offset(i as isize) = 0 as libc::c_int as libc::c_char; +} +#[no_mangle] +pub unsafe extern "C" fn load_config(mut filename: *const libc::c_char) { + let mut fp: *mut FILE = 0 as *mut FILE; + let mut ch: libc::c_int = 0; + let mut i: libc::c_int = 0; + let mut line: [libc::c_char; 1024] = [0; 1024]; + fp = fopen(filename, b"r\x00" as *const u8 as *const libc::c_char); + if fp.is_null() { + return; + } + memset( + line.as_mut_ptr() as *mut libc::c_void, + 0 as libc::c_int, + mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong, + ); + i = 0 as libc::c_int; + ch = fgetc(fp); + loop { + match ch { + 35 => + while ch != '\n' as i32 && ch != -(1 as libc::c_int) { + ch = fgetc(fp) + }, + -1 => { + parse_config_line(line.as_mut_ptr()); + fclose(fp); + return; + } + 13 => ch = fgetc(fp), + 10 => { + parse_config_line(line.as_mut_ptr()); + memset( + line.as_mut_ptr() as *mut libc::c_void, + 0 as libc::c_int, + mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong, + ); + i = 0 as libc::c_int; + ch = fgetc(fp) + } + _ => { + if (i as libc::c_ulong) + < (mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong) + .wrapping_sub(1 as libc::c_int as libc::c_ulong) + { + let fresh2 = i; + i = i + 1; + line[fresh2 as usize] = ch as libc::c_char + } + ch = fgetc(fp) + } + } + } +} +pub unsafe extern "C" fn init_config() { + let mut filename: [libc::c_char; 1024] = [0; 1024]; + let mut home: *const libc::c_char = 0 as *const libc::c_char; + let mut i: libc::c_int = 0; + /* copy defaults */ + snprintf( + CString::new(config.start_uri.clone()).unwrap().into_raw(), + mem::size_of::<[libc::c_char; 512]>() as libc::c_ulong, + b"gopher://gopher.floodgap.com:70\x00" as *const u8 as *const libc::c_char, + ); + snprintf( + CString::new(config.cmd_text.clone()).unwrap().into_raw(), + mem::size_of::<[libc::c_char; 512]>() as libc::c_ulong, + b"%s\x00" as *const u8 as *const libc::c_char, + b"less\x00" as *const u8 as *const libc::c_char, + ); + snprintf( + CString::new(config.cmd_image.clone()).unwrap().into_raw(), + mem::size_of::<[libc::c_char; 512]>() as libc::c_ulong, + b"%s\x00" as *const u8 as *const libc::c_char, + b"display\x00" as *const u8 as *const libc::c_char, + ); + snprintf( + CString::new(config.cmd_browser.clone()).unwrap().into_raw(), + mem::size_of::<[libc::c_char; 512]>() as libc::c_ulong, + b"%s\x00" as *const u8 as *const libc::c_char, + b"firefox\x00" as *const u8 as *const libc::c_char, + ); + snprintf( + CString::new(config.cmd_player.clone()).unwrap().into_raw(), + mem::size_of::<[libc::c_char; 512]>() as libc::c_ulong, + b"%s\x00" as *const u8 as *const libc::c_char, + b"mplayer\x00" as *const u8 as *const libc::c_char, + ); + snprintf( + CString::new(config.color_prompt.clone()).unwrap().into_raw(), + mem::size_of::<[libc::c_char; 512]>() as libc::c_ulong, + b"%s\x00" as *const u8 as *const libc::c_char, + b"1;34\x00" as *const u8 as *const libc::c_char, + ); + snprintf( + CString::new(config.color_selector.clone()).unwrap().into_raw(), + mem::size_of::<[libc::c_char; 512]>() as libc::c_ulong, + b"%s\x00" as *const u8 as *const libc::c_char, + b"1;32\x00" as *const u8 as *const libc::c_char, + ); + snprintf( + CString::new(config.verbose.clone()).unwrap().into_raw(), + mem::size_of::<[libc::c_char; 512]>() as libc::c_ulong, + b"%s\x00" as *const u8 as *const libc::c_char, + b"true\x00" as *const u8 as *const libc::c_char, + ); + i = 0 as libc::c_int; + while i < 20 as libc::c_int { + bookmarks[i as usize][0 as libc::c_int as usize] = + 0 as libc::c_int as libc::c_char; + i += 1 + } + /* read configs */ + load_config(b"/etc/cgorc\x00" as *const u8 as *const libc::c_char); /* ignore incomplete selectors */ + home = getenv(b"HOME\x00" as *const u8 as *const libc::c_char); /* not needed for history...just clear them */ + if !home.is_null() { + snprintf( + filename.as_mut_ptr(), + mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong, + b"%s%s\x00" as *const u8 as *const libc::c_char, + home, + b"/.cgorc\x00" as *const u8 as *const libc::c_char, + ); + load_config(filename.as_mut_ptr()); + }; +} +pub unsafe extern "C" fn dial( + mut host: *const libc::c_char, + mut port: *const libc::c_char, + mut selector: *const libc::c_char, +) -> libc::c_int { + let mut hints: addrinfo = addrinfo { + ai_flags: 0, + ai_family: 0, + ai_socktype: 0, + ai_protocol: 0, + ai_addrlen: 0, + ai_addr: 0 as *mut sockaddr, + ai_canonname: 0 as *mut libc::c_char, + ai_next: 0 as *mut addrinfo, + }; + let mut res: *mut addrinfo = 0 as *mut addrinfo; + let mut r: *mut addrinfo = 0 as *mut addrinfo; + let mut srv: libc::c_int = -(1 as libc::c_int); + let mut l: libc::c_int = 0; + let mut request: [libc::c_char; 512] = [0; 512]; + memset( + &mut hints as *mut addrinfo as *mut libc::c_void, + 0 as libc::c_int, + mem::size_of::() as libc::c_ulong, + ); + hints.ai_family = 0 as libc::c_int; + hints.ai_socktype = SOCK_STREAM as libc::c_int; + if getaddrinfo(host, port, &mut hints, &mut res) != 0 as libc::c_int { + eprintln!( + "error: cannot resolve hostname '{}:{}': {}", + CStr::from_ptr(host).to_str().unwrap(), + CStr::from_ptr(port).to_str().unwrap(), + CStr::from_ptr(strerror(*__errno_location())).to_str().unwrap() + ); + return -(1 as libc::c_int); + } + r = res; + while !r.is_null() { + srv = socket((*r).ai_family, (*r).ai_socktype, (*r).ai_protocol); + if !(srv == -(1 as libc::c_int)) { + if connect(srv, (*r).ai_addr, (*r).ai_addrlen) == 0 as libc::c_int { + break; + } + close(srv); + } + r = (*r).ai_next + } + freeaddrinfo(res); + if r.is_null() { + fprintf( + stderr, + b"error: cannot connect to host \'%s:%s\'\n\x00" as *const u8 + as *const libc::c_char, + host, + port, + ); + return -(1 as libc::c_int); + } + snprintf( + request.as_mut_ptr(), + mem::size_of::<[libc::c_char; 512]>() as libc::c_ulong, + b"%s\r\n\x00" as *const u8 as *const libc::c_char, + selector, + ); + l = strlen(request.as_mut_ptr()) as libc::c_int; + if write(srv, request.as_mut_ptr() as *const libc::c_void, l as size_t) + != l as libc::c_long + { + fprintf( + stderr, + b"error: cannot complete request\n\x00" as *const u8 as *const libc::c_char, + ); + close(srv); + return -(1 as libc::c_int); + } + return srv; +} +pub unsafe extern "C" fn read_line( + mut fd: libc::c_int, + mut buf: *mut libc::c_char, + mut buf_len: size_t, +) -> libc::c_int { + let mut i: size_t = 0 as libc::c_int as size_t; + let mut c: libc::c_char = 0 as libc::c_int as libc::c_char; + loop { + if read( + fd, + &mut c as *mut libc::c_char as *mut libc::c_void, + mem::size_of::() as libc::c_ulong, + ) as libc::c_ulong + != mem::size_of::() as libc::c_ulong + { + return 0 as libc::c_int; + } + if c as libc::c_int != '\r' as i32 { + let fresh3 = i; + i = i.wrapping_add(1); + *buf.offset(fresh3 as isize) = c + } + if !(c as libc::c_int != '\n' as i32 && i < buf_len) { + break; + } + } + *buf.offset(i.wrapping_sub(1 as libc::c_int as libc::c_ulong) as isize) = + '\u{0}' as i32 as libc::c_char; + return 1 as libc::c_int; +} +pub unsafe extern "C" fn download_file( + mut host: *const libc::c_char, + mut port: *const libc::c_char, + mut selector: *const libc::c_char, + mut fd: libc::c_int, +) -> libc::c_int { + let mut srvfd: libc::c_int = 0; + let mut len: libc::c_int = 0; + let mut total: libc::c_ulong = 0 as libc::c_int as libc::c_ulong; + let mut buffer: [libc::c_char; 4096] = [0; 4096]; + if check_option_true(CString::new(config.verbose.clone()).unwrap().into_raw()) != 0 { + printf( + b"downloading [%s]...\r\x00" as *const u8 as *const libc::c_char, + selector, + ); + } + srvfd = dial(host, port, selector); + if srvfd == -(1 as libc::c_int) { + printf( + b"\x1b[2Kerror: downloading [%s] failed\n\x00" as *const u8 + as *const libc::c_char, + selector, + ); + close(fd); + return 0 as libc::c_int; + } + loop { + len = read( + srvfd, + buffer.as_mut_ptr() as *mut libc::c_void, + mem::size_of::<[libc::c_char; 4096]>() as libc::c_ulong, + ) as libc::c_int; + if !(len > 0 as libc::c_int) { + break; + } + write(fd, buffer.as_mut_ptr() as *const libc::c_void, len as size_t); + total = total.wrapping_add(len as libc::c_ulong); + if check_option_true(CString::new(config.verbose.clone()).unwrap().into_raw()) != 0 { + printf( + b"downloading [%s] (%ld kb)...\r\x00" as *const u8 as *const libc::c_char, + selector, + total.wrapping_div(1024 as libc::c_int as libc::c_ulong), + ); + } + } + close(fd); + close(srvfd); + if check_option_true(CString::new(config.verbose.clone()).unwrap().into_raw()) != 0 { + printf( + b"\x1b[2Kdownloading [%s] complete\n\x00" as *const u8 as *const libc::c_char, + selector, + ); + } + return 1 as libc::c_int; +} +pub unsafe extern "C" fn download_temp( + mut host: *const libc::c_char, + mut port: *const libc::c_char, + mut selector: *const libc::c_char, +) -> libc::c_int { + let mut tmpfd: libc::c_int = 0; + strcpy( + tmpfilename.as_mut_ptr(), + b"/tmp/cgoXXXXXX\x00" as *const u8 as *const libc::c_char, + ); + tmpfd = mkstemp(tmpfilename.as_mut_ptr()); + if tmpfd == -(1 as libc::c_int) { + fputs( + b"error: unable to create tmp file\n\x00" as *const u8 as *const libc::c_char, + stderr, + ); + return 0 as libc::c_int; + } + if download_file(host, port, selector, tmpfd) == 0 { + unlink(tmpfilename.as_mut_ptr()); + return 0 as libc::c_int; + } + return 1 as libc::c_int; +} +pub unsafe extern "C" fn make_key( + mut c1: libc::c_char, + mut c2: libc::c_char, + mut c3: libc::c_char, +) -> libc::c_int { + if c1 == 0 || c2 == 0 { + return -(1 as libc::c_int); + } + if c3 == 0 { + return (c1 as libc::c_int - 'a' as i32) + * ('z' as i32 - 'a' as i32 + 1 as libc::c_int) + + (c2 as libc::c_int - 'a' as i32); + } else { + return (c1 as libc::c_int - 'a' as i32 + 1 as libc::c_int) + * ('z' as i32 - 'a' as i32 + 1 as libc::c_int) + * ('z' as i32 - 'a' as i32 + 1 as libc::c_int) + + (c2 as libc::c_int - 'a' as i32) + * ('z' as i32 - 'a' as i32 + 1 as libc::c_int) + + (c3 as libc::c_int - 'a' as i32); + }; +} +pub unsafe extern "C" fn make_key_str( + mut key: libc::c_int, + mut c1: *mut libc::c_char, + mut c2: *mut libc::c_char, + mut c3: *mut libc::c_char, +) { + if key + < ('z' as i32 - 'a' as i32 + 1 as libc::c_int) + * ('z' as i32 - 'a' as i32 + 1 as libc::c_int) + { + *c1 = ('a' as i32 + key / ('z' as i32 - 'a' as i32 + 1 as libc::c_int)) + as libc::c_char; + *c2 = ('a' as i32 + key % ('z' as i32 - 'a' as i32 + 1 as libc::c_int)) + as libc::c_char; + *c3 = 0 as libc::c_int as libc::c_char + } else { + *c1 = ('a' as i32 + + key + / (('z' as i32 - 'a' as i32 + 1 as libc::c_int) + * ('z' as i32 - 'a' as i32 + 1 as libc::c_int)) + - 1 as libc::c_int) as libc::c_char; + *c2 = ('a' as i32 + + key / ('z' as i32 - 'a' as i32 + 1 as libc::c_int) + % ('z' as i32 - 'a' as i32 + 1 as libc::c_int)) as libc::c_char; + *c3 = ('a' as i32 + key % ('z' as i32 - 'a' as i32 + 1 as libc::c_int)) + as libc::c_char + }; +} +pub unsafe extern "C" fn add_link( + mut which: libc::c_char, + mut name: *const libc::c_char, + mut host: *const libc::c_char, + mut port: *const libc::c_char, + mut selector: *const libc::c_char, +) { + let mut link: *mut Link = 0 as *mut Link; + let mut a: libc::c_char = 0 as libc::c_int as libc::c_char; + let mut b: libc::c_char = 0 as libc::c_int as libc::c_char; + let mut c: libc::c_char = 0 as libc::c_int as libc::c_char; + if host.is_null() || port.is_null() || selector.is_null() { + return; + } + link = calloc( + 1 as libc::c_int as libc::c_ulong, + mem::size_of::() as libc::c_ulong, + ) as *mut Link; + (*link).which = which; + (*link).key = link_key as libc::c_short; + (*link).host = strdup(host); + (*link).port = strdup(port); + (*link).selector = strdup(selector); + if links.is_null() { + (*link).next = 0 as *mut Link + } else { + (*link).next = links + } + links = link; + let fresh4 = link_key; + link_key = link_key + 1; + make_key_str(fresh4, &mut a, &mut b, &mut c); + printf( + b"\x1b[%sm%c%c%c\x1b[0m \x1b[1m%s\x1b[0m\n\x00" as *const u8 + as *const libc::c_char, + CString::new(config.color_selector.clone()).unwrap().into_raw(), + a as libc::c_int, + b as libc::c_int, + c as libc::c_int, + name, + ); +} +pub unsafe extern "C" fn clear_links() { + let mut link: *mut Link = 0 as *mut Link; + let mut next: *mut Link = 0 as *mut Link; + link = links; + while !link.is_null() { + next = (*link).next; + free((*link).host as *mut libc::c_void); + free((*link).port as *mut libc::c_void); + free((*link).selector as *mut libc::c_void); + free(link as *mut libc::c_void); + link = next + } + links = 0 as *mut Link; + link_key = 0 as libc::c_int; +} +pub unsafe extern "C" fn add_history() { + let mut link: *mut Link = 0 as *mut Link; + link = calloc( + 1 as libc::c_int as libc::c_ulong, + mem::size_of::() as libc::c_ulong, + ) as *mut Link; + (*link).host = strdup(current_host.as_mut_ptr()); + (*link).port = strdup(current_port.as_mut_ptr()); + (*link).selector = strdup(current_selector.as_mut_ptr()); + (*link).which = 0 as libc::c_int as libc::c_char; + (*link).key = 0 as libc::c_int as libc::c_short; + if history.is_null() { + (*link).next = 0 as *mut Link + } else { + (*link).next = history + } + history = link; +} +pub unsafe extern "C" fn handle_directory_line(mut line: *mut libc::c_char) { + let mut i: libc::c_int = 0; + let mut lp: *mut libc::c_char = 0 as *mut libc::c_char; + let mut last: *mut libc::c_char = 0 as *mut libc::c_char; + let mut fields: [*mut libc::c_char; 4] = [0 as *mut libc::c_char; 4]; + /* tokenize */ + i = 0 as libc::c_int; + while i < 4 as libc::c_int { + fields[i as usize] = 0 as *mut libc::c_char; + i += 1 + } + last = &mut *line.offset(1 as libc::c_int as isize) as *mut libc::c_char; + lp = last; + i = 0 as libc::c_int; + while i < 4 as libc::c_int { + if *lp as libc::c_int == '\t' as i32 || *lp as libc::c_int == '\u{0}' as i32 { + fields[i as usize] = last; + last = lp.offset(1 as libc::c_int as isize); + if *lp as libc::c_int == '\u{0}' as i32 { + break; + } + *lp = '\u{0}' as i32 as libc::c_char; + i += 1 + } + lp = lp.offset(1) + } + /* determine listing type */ + match *line.offset(0 as libc::c_int as isize) as libc::c_int { + 105 | 51 => { + printf( + b" %s\n\x00" as *const u8 as *const libc::c_char, + fields[0 as libc::c_int as usize], + ); + } + 46 => { + /* some gopher servers use this */ + puts(b"\x00" as *const u8 as *const libc::c_char); + } + 48 | 49 | 53 | 55 | 56 | 57 | 103 | 73 | 112 | 104 | 115 => { + add_link( + *line.offset(0 as libc::c_int as isize), + fields[0 as libc::c_int as usize], + fields[2 as libc::c_int as usize], + fields[3 as libc::c_int as usize], + fields[1 as libc::c_int as usize], + ); + } + _ => { + printf( + b"miss [%c]: %s\n\x00" as *const u8 as *const libc::c_char, + *line.offset(0 as libc::c_int as isize) as libc::c_int, + fields[0 as libc::c_int as usize], + ); + } + }; +} +pub unsafe extern "C" fn is_valid_directory_entry( + mut line: *const libc::c_char, +) -> libc::c_int { + match *line.offset(0 as libc::c_int as isize) as libc::c_int { + 105 | 51 | 46 | 48 | 49 | 53 | 55 | 56 | 57 | 103 | 73 | 112 | 104 | 115 => { + /* some gopher servers use this */ + return 1 as libc::c_int; + } + _ => return 0 as libc::c_int, + }; +} +pub unsafe extern "C" fn view_directory( + mut host: *const libc::c_char, + mut port: *const libc::c_char, + mut selector: *const libc::c_char, + mut make_current: libc::c_int, +) { + let mut is_dir: libc::c_int = 0; + let mut srvfd: libc::c_int = 0; + let mut i: libc::c_int = 0; + let mut head_read: libc::c_int = 0; + let mut line: [libc::c_char; 1024] = [0; 1024]; + let mut head: [[libc::c_char; 1024]; 5] = [[0; 1024]; 5]; + srvfd = dial(host, port, selector); + if srvfd != -(1 as libc::c_int) { + /* only adapt current prompt when successful */ + /* make history entry */ + if make_current != 0 { + add_history(); + } + /* don't overwrite the current_* things... */ + if host != current_host.as_mut_ptr() as *const libc::c_char { + snprintf( + current_host.as_mut_ptr(), + mem::size_of::<[libc::c_char; 512]>() as libc::c_ulong, + b"%s\x00" as *const u8 as *const libc::c_char, + host, + ); /* clear links *AFTER* dialing out!! */ + } /* quit if not successful */ + if port != current_port.as_mut_ptr() as *const libc::c_char { + snprintf( + current_port.as_mut_ptr(), + mem::size_of::<[libc::c_char; 64]>() as libc::c_ulong, + b"%s\x00" as *const u8 as *const libc::c_char, + port, + ); + } + if selector != current_selector.as_mut_ptr() as *const libc::c_char { + snprintf( + current_selector.as_mut_ptr(), + mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong, + b"%s\x00" as *const u8 as *const libc::c_char, + selector, + ); + } + } + clear_links(); + if srvfd == -(1 as libc::c_int) { + return; + } + head_read = 0 as libc::c_int; + is_dir = 1 as libc::c_int; + while head_read < 5 as libc::c_int + && read_line( + srvfd, + line.as_mut_ptr(), + mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong, + ) != 0 + { + strcpy(head[head_read as usize].as_mut_ptr(), line.as_mut_ptr()); + if is_valid_directory_entry(head[head_read as usize].as_mut_ptr()) == 0 { + is_dir = 0 as libc::c_int; + break; + } else { + head_read += 1 + } + } + if is_dir == 0 { + puts(b"error: Not a directory.\x00" as *const u8 as *const libc::c_char); + close(srvfd); + return; + } + i = 0 as libc::c_int; + while i < head_read { + handle_directory_line(head[i as usize].as_mut_ptr()); + i += 1 + } + while read_line( + srvfd, + line.as_mut_ptr(), + mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong, + ) != 0 + { + handle_directory_line(line.as_mut_ptr()); + } + close(srvfd); +} +pub unsafe extern "C" fn view_file( + mut cmd: *const libc::c_char, + mut host: *const libc::c_char, + mut port: *const libc::c_char, + mut selector: *const libc::c_char, +) { + let mut pid: pid_t = 0; + let mut status: libc::c_int = 0; + let mut i: libc::c_int = 0; + let mut j: libc::c_int = 0; + let mut buffer: [libc::c_char; 1024] = [0; 1024]; + let mut argv: [*mut libc::c_char; 32] = [0 as *mut libc::c_char; 32]; + let mut p: *mut libc::c_char = 0 as *mut libc::c_char; + if check_option_true(CString::new(config.verbose.clone()).unwrap().into_raw()) != 0 { + printf( + b"h(%s) p(%s) s(%s)\n\x00" as *const u8 as *const libc::c_char, + host, + port, + selector, + ); + } + if download_temp(host, port, selector) == 0 { + return; + } + /* parsed command line string */ + argv[0 as libc::c_int as usize] = + &mut *buffer.as_mut_ptr().offset(0 as libc::c_int as isize) as *mut libc::c_char; + p = cmd as *mut libc::c_char; + i = 0 as libc::c_int; + j = 1 as libc::c_int; + while *p as libc::c_int != 0 + && (i as libc::c_ulong) + < (mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong) + .wrapping_sub(1 as libc::c_int as libc::c_ulong) + && j < 30 as libc::c_int + { + if *p as libc::c_int == ' ' as i32 || *p as libc::c_int == '\t' as i32 { + let fresh5 = i; + i = i + 1; + buffer[fresh5 as usize] = 0 as libc::c_int as libc::c_char; + let fresh6 = j; + j = j + 1; + argv[fresh6 as usize] = + &mut *buffer.as_mut_ptr().offset(i as isize) as *mut libc::c_char; + while *p as libc::c_int == ' ' as i32 || *p as libc::c_int == '\t' as i32 { + p = p.offset(1) + } + } else { + let fresh7 = p; + p = p.offset(1); + let fresh8 = i; + i = i + 1; + buffer[fresh8 as usize] = *fresh7 + } + } + buffer[i as usize] = 0 as libc::c_int as libc::c_char; + let fresh9 = j; + j = j + 1; + argv[fresh9 as usize] = tmpfilename.as_mut_ptr(); + argv[j as usize] = 0 as *mut libc::c_char; + /* fork and execute */ + if check_option_true(CString::new(config.verbose.clone()).unwrap().into_raw()) != 0 { + printf( + b"executing: %s %s\n\x00" as *const u8 as *const libc::c_char, + cmd, + tmpfilename.as_mut_ptr(), + ); /* to wait for browsers etc. that return immediatly */ + } + pid = fork(); + if pid == 0 as libc::c_int { + if execvp( + argv[0 as libc::c_int as usize], + argv.as_mut_ptr() as *const *mut libc::c_char, + ) == -(1 as libc::c_int) + { + puts(b"error: execvp() failed!\x00" as *const u8 as *const libc::c_char); + } + } else if pid == -(1 as libc::c_int) { + puts(b"error: fork() failed\x00" as *const u8 as *const libc::c_char); + } + sleep(Duration::from_secs(1)); + waitpid(pid, &mut status, 0 as libc::c_int); + unlink(tmpfilename.as_mut_ptr()); +} +pub unsafe extern "C" fn view_telnet( + mut host: *const libc::c_char, + mut port: *const libc::c_char, +) { + let mut pid: pid_t = 0; + let mut status: libc::c_int = 0; + printf( + b"executing: %s %s %s\n\x00" as *const u8 as *const libc::c_char, + b"telnet\x00" as *const u8 as *const libc::c_char, + host, + port, + ); + pid = fork(); + if pid == 0 as libc::c_int { + if execlp( + b"telnet\x00" as *const u8 as *const libc::c_char, + b"telnet\x00" as *const u8 as *const libc::c_char, + host, + port, + 0 as *mut libc::c_void, + ) == -(1 as libc::c_int) + { + puts(b"error: execlp() failed!\x00" as *const u8 as *const libc::c_char); + } + } else if pid == -(1 as libc::c_int) { + puts(b"error: fork() failed!\x00" as *const u8 as *const libc::c_char); + } + waitpid(pid, &mut status, 0 as libc::c_int); + puts(b"(done)\x00" as *const u8 as *const libc::c_char); +} +pub unsafe extern "C" fn view_download( + mut host: *const libc::c_char, + mut port: *const libc::c_char, + mut selector: *const libc::c_char, +) { + let mut fd: libc::c_int = 0; + let mut filename: [libc::c_char; 1024] = [0; 1024]; + let mut line: [libc::c_char; 1024] = [0; 1024]; + snprintf( + filename.as_mut_ptr(), + mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong, + b"%s\x00" as *const u8 as *const libc::c_char, + strrchr(selector, '/' as i32).offset(1 as libc::c_int as isize), + ); + printf( + b"enter filename for download [%s]: \x00" as *const u8 as *const libc::c_char, + filename.as_mut_ptr(), + ); + if read_line( + 0 as libc::c_int, + line.as_mut_ptr(), + mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong, + ) == 0 + { + puts(b"download aborted\x00" as *const u8 as *const libc::c_char); + return; + } + if strlen(line.as_mut_ptr()) > 0 as libc::c_int as libc::c_ulong { + strcpy(filename.as_mut_ptr(), line.as_mut_ptr()); + } + fd = open( + filename.as_mut_ptr(), + 0o100 as libc::c_int | 0o1 as libc::c_int, + 0o400 as libc::c_int | 0o200 as libc::c_int, + ); + if fd == -(1 as libc::c_int) { + printf( + b"error: unable to create file [%s]: %s\n\x00" as *const u8 + as *const libc::c_char, + filename.as_mut_ptr(), + strerror(*__errno_location()), + ); + return; + } + if download_file(host, port, selector, fd) == 0 { + printf( + b"error: unable to download [%s]\n\x00" as *const u8 as *const libc::c_char, + selector, + ); + unlink(filename.as_mut_ptr()); + return; + }; +} +pub unsafe extern "C" fn view_search( + mut host: *const libc::c_char, + mut port: *const libc::c_char, + mut selector: *const libc::c_char, +) { + let mut search_selector: [libc::c_char; 1024] = [0; 1024]; + let mut line: [libc::c_char; 1024] = [0; 1024]; + printf(b"enter search string: \x00" as *const u8 as *const libc::c_char); + if read_line( + 0 as libc::c_int, + line.as_mut_ptr(), + mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong, + ) == 0 + { + puts(b"search aborted\x00" as *const u8 as *const libc::c_char); + return; + } + snprintf( + search_selector.as_mut_ptr(), + mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong, + b"%s\t%s\x00" as *const u8 as *const libc::c_char, + selector, + line.as_mut_ptr(), + ); + view_directory(host, port, search_selector.as_mut_ptr(), 1 as libc::c_int); +} +pub unsafe extern "C" fn view_history(mut key: libc::c_int) { + let mut history_key: libc::c_int = 0 as libc::c_int; + let mut a: libc::c_char = 0; + let mut b: libc::c_char = 0; + let mut c: libc::c_char = 0; + let mut link: *mut Link = 0 as *mut Link; + if history.is_null() { + puts(b"(empty history)\x00" as *const u8 as *const libc::c_char); + return; + } + if key < 0 as libc::c_int { + puts(b"(history)\x00" as *const u8 as *const libc::c_char); + link = history; + while !link.is_null() { + let fresh10 = history_key; + history_key = history_key + 1; + make_key_str(fresh10, &mut a, &mut b, &mut c); + printf( + b"\x1b[%sm%c%c%c\x1b[0m \x1b[1m%s:%s/1%s\x1b[0m\n\x00" as *const u8 + as *const libc::c_char, + b"1;32\x00" as *const u8 as *const libc::c_char, + a as libc::c_int, + b as libc::c_int, + c as libc::c_int, + (*link).host, + (*link).port, + (*link).selector, + ); + link = (*link).next + } + } else { + /* traverse history list */ + link = history; + while !link.is_null() { + if history_key == key { + view_directory( + (*link).host, + (*link).port, + (*link).selector, + 0 as libc::c_int, + ); + return; + } + link = (*link).next; + history_key += 1 + } + puts(b"history item not found\x00" as *const u8 as *const libc::c_char); + }; +} +pub unsafe extern "C" fn view_bookmarks(mut key: libc::c_int) { + let mut i: libc::c_int = 0; + let mut a: libc::c_char = 0; + let mut b: libc::c_char = 0; + let mut c: libc::c_char = 0; + if key < 0 as libc::c_int { + puts(b"(bookmarks)\x00" as *const u8 as *const libc::c_char); + i = 0 as libc::c_int; + while i < 20 as libc::c_int { + if bookmarks[i as usize][0 as libc::c_int as usize] != 0 { + make_key_str(i, &mut a, &mut b, &mut c); + printf( + b"\x1b[%sm%c%c%c\x1b[0m \x1b[1m%s\x1b[0m\n\x00" as *const u8 + as *const libc::c_char, + b"1;32\x00" as *const u8 as *const libc::c_char, + a as libc::c_int, + b as libc::c_int, + c as libc::c_int, + &mut *(*bookmarks.as_mut_ptr().offset(i as isize)) + .as_mut_ptr() + .offset(0 as libc::c_int as isize) as *mut libc::c_char, + ); + } + i += 1 + } + } else { + i = 0 as libc::c_int; + while i < 20 as libc::c_int { + if bookmarks[i as usize][0 as libc::c_int as usize] as libc::c_int != 0 + && i == key + { + if parse_uri( + &mut *(*bookmarks.as_mut_ptr().offset(i as isize)) + .as_mut_ptr() + .offset(0 as libc::c_int as isize), + ) != 0 + { + view_directory( + parsed_host.as_mut_ptr(), + parsed_port.as_mut_ptr(), + parsed_selector.as_mut_ptr(), + 0 as libc::c_int, + ); + } else { + printf( + b"invalid gopher URI: %s\x00" as *const u8 as *const libc::c_char, + &mut *(*bookmarks.as_mut_ptr().offset(i as isize)) + .as_mut_ptr() + .offset(0 as libc::c_int as isize) as *mut libc::c_char, + ); + } + return; + } + i += 1 + } + }; +} +pub unsafe extern "C" fn pop_history() { + let mut next: *mut Link = 0 as *mut Link; + if history.is_null() { + puts(b"(empty history)\x00" as *const u8 as *const libc::c_char); + return; + } + /* reload page from history (and don't count as history) */ + view_directory( + (*history).host, + (*history).port, + (*history).selector, + 0 as libc::c_int, + ); + /* history is history... :) */ + next = (*history).next; + free((*history).host as *mut libc::c_void); + free((*history).port as *mut libc::c_void); + free((*history).selector as *mut libc::c_void); + free(history as *mut libc::c_void); + history = next; +} +pub unsafe extern "C" fn follow_link(mut key: libc::c_int) -> libc::c_int { + let mut link: *mut Link = 0 as *mut Link; + link = links; + while !link.is_null() { + if (*link).key as libc::c_int != key { + link = (*link).next + } else { + match (*link).which as libc::c_int { + 48 => { + view_file( + CString::new(config.cmd_text.clone()) + .unwrap() + .into_raw(), + (*link).host, + (*link).port, + (*link).selector, + ); + } + 49 => { + view_directory( + (*link).host, + (*link).port, + (*link).selector, + 1 as libc::c_int, + ); + } + 55 => { + view_search((*link).host, (*link).port, (*link).selector); + } + 53 | 57 => { + view_download((*link).host, (*link).port, (*link).selector); + } + 56 => { + view_telnet((*link).host, (*link).port); + } + 103 | 73 | 112 => { + view_file( + CString::new(config.cmd_image.clone()) + .unwrap() + .into_raw(), + (*link).host, + (*link).port, + (*link).selector, + ); + } + 104 => { + view_file( + CString::new(config.cmd_browser.clone()) + .unwrap() + .into_raw(), + (*link).host, + (*link).port, + (*link).selector, + ); + } + 115 => { + view_file( + CString::new(config.cmd_player.clone()) + .unwrap() + .into_raw(), + (*link).host, + (*link).port, + (*link).selector, + ); + } + _ => { + printf( + b"missing handler [%c]\n\x00" as *const u8 as *const libc::c_char, + (*link).which as libc::c_int, + ); + } + } + return 1 as libc::c_int; + } + /* return the array is broken after view! */ + } + return 0 as libc::c_int; +} +pub unsafe extern "C" fn download_link(mut key: libc::c_int) { + let mut link: *mut Link = 0 as *mut Link; + link = links; + while !link.is_null() { + if (*link).key as libc::c_int != key { + link = (*link).next + } else { + view_download((*link).host, (*link).port, (*link).selector); + return; + } + } + puts(b"link not found\x00" as *const u8 as *const libc::c_char); +} +/* function prototypes */ +pub unsafe extern "C" fn parse_uri(mut uri: *const libc::c_char) -> libc::c_int { + let mut i: libc::c_int = 0; + /* strip gopher:// */ + if strncmp( + uri, + b"gopher://\x00" as *const u8 as *const libc::c_char, + 9 as libc::c_int as libc::c_ulong, + ) == 0 + { + uri = uri.offset(9 as libc::c_int as isize) + } + /* parse host */ + i = 0 as libc::c_int; + while *uri as libc::c_int != 0 + && *uri as libc::c_int != ':' as i32 + && *uri as libc::c_int != '/' as i32 + { + if *uri as libc::c_int != ' ' as i32 + && (i as libc::c_ulong) + < (mem::size_of::<[libc::c_char; 512]>() as libc::c_ulong) + .wrapping_sub(1 as libc::c_int as libc::c_ulong) + { + let fresh11 = i; + i = i + 1; + parsed_host[fresh11 as usize] = *uri + } + uri = uri.offset(1) + } + if i > 0 as libc::c_int { + parsed_host[i as usize] = 0 as libc::c_int as libc::c_char + } else { + return 0 as libc::c_int; + } + /* parse port */ + if *uri as libc::c_int == ':' as i32 { + uri = uri.offset(1); + i = 0 as libc::c_int; + while *uri as libc::c_int != 0 && *uri as libc::c_int != '/' as i32 { + if *uri as libc::c_int != ' ' as i32 + && (i as libc::c_ulong) + < (mem::size_of::<[libc::c_char; 64]>() as libc::c_ulong) + .wrapping_sub(1 as libc::c_int as libc::c_ulong) + { + let fresh12 = i; + i = i + 1; + parsed_port[fresh12 as usize] = *uri + } + uri = uri.offset(1) + } + parsed_port[i as usize] = 0 as libc::c_int as libc::c_char + } else { + snprintf( + parsed_port.as_mut_ptr(), + mem::size_of::<[libc::c_char; 64]>() as libc::c_ulong, + b"%d\x00" as *const u8 as *const libc::c_char, + 70 as libc::c_int, + ); + } + /* parse selector (ignore slash and selector type) */ + if *uri != 0 { + uri = uri.offset(1) + } + if *uri != 0 { + uri = uri.offset(1) + } + i = 0 as libc::c_int; + while *uri as libc::c_int != 0 + && (i as libc::c_ulong) + < (mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong) + .wrapping_sub(1 as libc::c_int as libc::c_ulong) + { + parsed_selector[i as usize] = *uri; + uri = uri.offset(1); + i += 1 + } + parsed_selector[i as usize] = '\u{0}' as i32 as libc::c_char; + return 1 as libc::c_int; +} +unsafe fn main_0(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char) -> libc::c_int { + let mut i: libc::c_int = 0; + let mut line: [libc::c_char; 1024] = [0; 1024]; + let mut uri: *mut libc::c_char = 0 as *mut libc::c_char; + /* copy defaults */ + init_config(); + uri = CString::new(config.start_uri.clone()).unwrap().into_raw(); + /* parse command line */ + i = 1 as libc::c_int; + while i < argc { + if *(*argv.offset(i as isize)).offset(0 as libc::c_int as isize) as libc::c_int + == '-' as i32 + { + match *(*argv.offset(i as isize)).offset(1 as libc::c_int as isize) + as libc::c_int + { + 72 => { + usage(); + } + 118 => { + banner(true); + exit(0); + } + _ => { + usage(); + } + } + } else { + uri = *argv.offset(i as isize) + } + i += 1 + } + /* parse uri */ + if parse_uri(uri) == 0 { + banner(false); + eprintln!( + "invalid gopher URI: {}", + CStr::from_ptr(*argv.offset(i as isize)).to_str().unwrap() + ); + exit(1); + } + /* main loop */ + view_directory( + parsed_host.as_mut_ptr(), + parsed_port.as_mut_ptr(), + parsed_selector.as_mut_ptr(), + 0 as libc::c_int, + ); /* to display the prompt */ + loop { + printf( + b"\x1b[%sm%s:%s%s\x1b[0m \x00" as *const u8 as *const libc::c_char, + CString::new(config.color_prompt.clone()).unwrap().into_raw(), + current_host.as_mut_ptr(), + current_port.as_mut_ptr(), + current_selector.as_mut_ptr(), + ); + if read_line( + 0 as libc::c_int, + line.as_mut_ptr(), + mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong, + ) == 0 + { + puts(b"QUIT\x00" as *const u8 as *const libc::c_char); + return 0 as libc::c_int; + } + i = strlen(line.as_mut_ptr()) as libc::c_int; + match line[0 as libc::c_int as usize] as libc::c_int { + 63 => { + puts(b"? - help\n* - reload directory\n< - go back in history\n.[LINK] - download the given link\nH - show history\nH[LINK] - jump to the specified history item\nG[URI] - jump to the given gopher URI\nB - show bookmarks\nB[LINK] - jump to the specified bookmark item\nC^d - quit\x00" + as *const u8 as *const libc::c_char); + } + 60 => { + pop_history(); + } + 42 => { + view_directory( + current_host.as_mut_ptr(), + current_port.as_mut_ptr(), + current_selector.as_mut_ptr(), + 0 as libc::c_int, + ); + } + 46 => { + download_link(make_key( + line[1 as libc::c_int as usize], + line[2 as libc::c_int as usize], + line[3 as libc::c_int as usize], + )); + } + 72 => { + if i == 1 as libc::c_int || i == 3 as libc::c_int || i == 4 as libc::c_int + { + view_history(make_key( + line[1 as libc::c_int as usize], + line[2 as libc::c_int as usize], + line[3 as libc::c_int as usize], + )); + } + } + 71 => { + if parse_uri(&mut *line.as_mut_ptr().offset(1 as libc::c_int as isize)) + != 0 + { + view_directory( + parsed_host.as_mut_ptr(), + parsed_port.as_mut_ptr(), + parsed_selector.as_mut_ptr(), + 1 as libc::c_int, + ); + } else { + puts(b"invalid gopher URI\x00" as *const u8 as *const libc::c_char); + } + } + 66 => { + if i == 1 as libc::c_int || i == 3 as libc::c_int || i == 4 as libc::c_int + { + view_bookmarks(make_key( + line[1 as libc::c_int as usize], + line[2 as libc::c_int as usize], + line[3 as libc::c_int as usize], + )); + } + } + _ => { + follow_link(make_key( + line[0 as libc::c_int as usize], + line[1 as libc::c_int as usize], + line[2 as libc::c_int as usize], + )); + } + } + } + /* never get's here but stops cc complaining */ +} +#[main] +pub fn main() { + let mut args: Vec<*mut libc::c_char> = Vec::new(); + for arg in env::args() { + args.push( + CString::new(arg) + .expect("Failed to convert argument into CString.") + .into_raw(), + ); + } + args.push(ptr::null_mut()); + unsafe { + exit(main_0( + (args.len() - 1) as libc::c_int, + args.as_mut_ptr() as *mut *mut libc::c_char, + ) as i32) + } +}