Add pager implementation, use in gemi.

This also required a small change in where gemi outputs the page's
contents, to avoid requiring multiple wait_for_pager() calls in the
code. This also allows it to show the redirect page's contents.
This commit is contained in:
Érico Rolim 2020-10-01 03:12:02 -03:00
parent 436357ccad
commit ef02e2d5ef
4 changed files with 111 additions and 9 deletions

36
gemi.c
View File

@ -8,6 +8,7 @@
#include "mmap_file.h"
#include "read_certs.h"
#include "gemini.h"
#include "pager.h"
#define GEMINI_REQUEST 1024
@ -18,6 +19,7 @@ static void usage(bool fail)
"Usage: gemi [options] <url>\n"
"Options:\n"
" -b: browse mode (experimental)\n"
" -p: use pager: value of PAGER (default is less)\n"
" -n: don't strip header\n"
" -d: debug\n"
" -h: show this dialog\n"
@ -30,15 +32,19 @@ static void usage(bool fail)
int main(int argc, char **argv)
{
int rv = EXIT_FAILURE;
bool debug = false, no_strip = false, browse = false;
bool browse = false, pager = false;
bool debug = false, no_strip = false;
int redirections = 0, redirections_pos = 0;
int c;
while ((c = getopt(argc, argv, "+bndhr:")) != -1) {
while ((c = getopt(argc, argv, "+bpndhr:")) != -1) {
switch (c) {
case 'b':
browse = true;
break;
case 'p':
pager = true;
break;
case 'n':
no_strip = true;
break;
@ -123,12 +129,20 @@ int main(int argc, char **argv)
signal(SIGPIPE, SIG_IGN);
// writes directly into stdout
FILE *output_stream = stdout;
struct pager_proc pager_info;
if (pager) {
if (launch_pager(&pager_info) < 0) {
return rv;
}
output_stream = pager_info.file;
}
struct mmap_file output;
if (browse) {
output = create_mmap_from_file(NULL, PROT_MEM);
} else {
output = create_mmap_from_FILE(stdout, "w");
output = create_mmap_from_FILE(output_stream, "w");
}
if (ERROR_MMAP(output)) {
return rv;
@ -151,6 +165,16 @@ int main(int argc, char **argv)
goto early_out;
}
// generic way of outputting data:
// - if using FILE backend, offset is 0 and nothing happens
// - if using memory backend, offset is used
// data output should happen _before_ dealing with links
fwrite(output.data, 1, output.offset, output_stream);
// pager must always be closed before exec'ing into self
// if redirect_link exists, should also kill pager
// XXX: add some flag to not kill pager when performing redirection?
if (pager) wait_for_pager(pager_info, (bool)redirect_link);
if (redirect_link) {
redirections += 1;
// redirect link was stored in callback
@ -182,10 +206,6 @@ int main(int argc, char **argv)
execvp(progpath, new_argv);
}
// generic way of outputting data:
// - if using FILE backend, offset is 0 and nothing happens
// - if using memory backend, offset is used
fwrite(output.data, 1, output.offset, stdout);
if (browse) {
struct gemini_link_node *head = NULL;
int n = get_links_from_gmi((char *)output.data, &head);

View File

@ -12,7 +12,7 @@ INC += -Iextern
BASEENCODEOBJS = extern/libbaseencode/base64.o extern/libbaseencode/base32.o
PURROBJS = socket.o urls.o files.o comm.o formats.o encrypt.o mmap_file.o
PURROBJS += read_certs.o gemini.o
PURROBJS += read_certs.o gemini.o pager.o
LIBSOBJS = $(BASEENCODEOBJS) $(PURROBJS)
HEADERS = purr.h mmap_file.h read_certs.h gemini.h

65
pager.c Normal file
View File

@ -0,0 +1,65 @@
#define _POSIX_C_SOURCE 200112L /* fdopen */
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <sys/wait.h>
#include "pager.h"
int launch_pager(struct pager_proc *p)
{
int rv = -1;
int pipes[2];
if (pipe(pipes) < 0) {
perror("pipe()");
return rv;
}
char *pager = getenv("PAGER");
if (pager == NULL || *pager == 0) {
pager = "less";
}
char *const pager_cmd[] = {pager, NULL};
p->pid = fork();
if (p->pid < 0) {
perror("fork()");
return rv;
}
if (p->pid == 0) {
close(pipes[1]);
dup2(pipes[0], STDIN_FILENO);
if (execvp(pager, pager_cmd) < 0) {
perror("execvp()");
return rv;
}
} else {
close(pipes[0]);
p->file = fdopen(pipes[1], "w");
if (p->file == NULL) {
perror("fdopen()");
return rv;
}
rv = 0;
}
return rv;
}
int wait_for_pager(struct pager_proc p, bool should_kill)
{
int status = 0;
fclose(p.file);
if (should_kill) {
sleep(1);
kill(p.pid, SIGTERM);
}
waitpid(p.pid, &status, 0);
return status;
}

17
pager.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef __PAGER_H_
#define __PAGER_H_
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <sys/types.h>
struct pager_proc {
FILE *file;
pid_t pid;
};
int launch_pager(struct pager_proc *);
int wait_for_pager(struct pager_proc, bool);
#endif // __PAGER_H_