#include #include #include #include #include const char towrite[] = "gemini://tilde.club/\r\n"; typedef struct ll { void *data; struct ll *next; struct ll *prev; /* optional */ } ll; char *tls_incremental_line_read(struct tls *context) { char buffer; int bufsize = 40; int current = 0; int read; char *initial = malloc(bufsize); for(;;) { if(!(read = tls_read(context, &buffer, 1))) { free(initial); return NULL; } switch(buffer) { case '\n': case '\r': case '\0': initial[current] = '\0'; return initial; } initial[current++] = buffer; if(current == bufsize - 2) { initial = realloc(initial, bufsize *= 2); } } } struct tls_config *tls_defconfig(void) { struct tls_config *r = tls_config_new(); tls_config_insecure_noverifycert(r); tls_config_insecure_noverifyname(r); return r; } struct tls *dial(char *addr, char *port) { struct tls_config *t = tls_defconfig(); struct tls *client = tls_client(); tls_configure(client, t); tls_connect(client, addr, port); tls_config_free(t); return client; } void ncurses_init(void) { initscr(); keypad(stdscr, TRUE); nonl(); cbreak(); noecho(); if(has_colors()) { init_pair(COLOR_BLACK, COLOR_BLACK, COLOR_BLACK); init_pair(COLOR_GREEN, COLOR_GREEN, COLOR_BLACK); init_pair(COLOR_RED, COLOR_RED, COLOR_BLACK); init_pair(COLOR_CYAN, COLOR_CYAN, COLOR_BLACK); init_pair(COLOR_WHITE, COLOR_WHITE, COLOR_BLACK); init_pair(COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLACK); init_pair(COLOR_BLUE, COLOR_BLUE, COLOR_BLACK); init_pair(COLOR_YELLOW, COLOR_YELLOW, COLOR_BLACK); } } void ncurses_cleanup(void) { endwin(); } void manager(void *newdata, ll **ptr, ll **head) { if(*ptr == NULL) { *ptr = malloc(sizeof **ptr); *head = *ptr; (*ptr)->prev = NULL; } else { (*ptr)->next = malloc(sizeof **ptr); (*ptr)->next->prev = *ptr; *ptr = (*ptr)->next; } (*ptr)->next = NULL; (*ptr)->data = newdata; } int is_link(char *c) { return c[0] == '=' && c[1] == '>'; } ll *scroll_ll(int direction, ll *current) { if(direction) { if(current->next) { return current->next; } } else { if(current->prev) { return current->prev; } } return current; } void render(int yinit, int ymax, ll *data) { ll *ptr2 = data; erase(); while(ptr2) { mvprintw(yinit++, 0, "%s", ptr2->data); ptr2 = ptr2->next; if(yinit == ymax) break; } } ll *fetch_and_render(struct tls *ctx, int yinit, int ymax, char *url) { tls_write(ctx, url, strlen(url)); tls_write(ctx, "\r\n", 2); ll *doc = NULL, *dochead = NULL, *links = NULL, *linkhead = NULL; char *c; int to_screen = 0; while(c = tls_incremental_line_read(ctx)) { manager(c, &doc, &dochead); if(is_link(c)) manager(c, &links, &linkhead); if(!to_screen) { mvwprintw(stdscr, yinit++, 0, "%s", c); } else if(to_screen == 1) { refresh(); to_screen++; } if(yinit == ymax) to_screen = 1; } ll *document = malloc(sizeof *document); ll *rlinks = malloc(sizeof *rlinks); document->data = dochead; document->next = rlinks; document->prev = NULL; rlinks->data = linkhead; rlinks->next = NULL; rlinks->prev = document; return document; } int main(void) { struct tls *ctx = dial("tilde.club", "1965"); int tx, ty; getmaxyx(stdscr, ty, tx); char getchb; ll *ptr = NULL, *head = NULL; tls_write(ctx, &towrite, sizeof towrite); ncurses_init(); ll *docdata = fetch_and_render(ctx, 1, ty, "gemini://tilde.club/"); ptr = docdata->data; while(ptr) { getchb = getch(); if(getchb == 'k') { ptr = scroll_ll(TRUE, ptr); } else if(getchb == 'q') { ptr = NULL; } else { ptr = scroll_ll(FALSE, ptr); } render(1, ty, ptr); } ncurses_cleanup(); return 0; }