mirror of https://github.com/ericonr/purr-c.git
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:
parent
436357ccad
commit
ef02e2d5ef
36
gemi.c
36
gemi.c
|
@ -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);
|
||||
|
|
2
makefile
2
makefile
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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_
|
Loading…
Reference in New Issue