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 "mmap_file.h"
|
||||||
#include "read_certs.h"
|
#include "read_certs.h"
|
||||||
#include "gemini.h"
|
#include "gemini.h"
|
||||||
|
#include "pager.h"
|
||||||
|
|
||||||
#define GEMINI_REQUEST 1024
|
#define GEMINI_REQUEST 1024
|
||||||
|
|
||||||
|
@ -18,6 +19,7 @@ static void usage(bool fail)
|
||||||
"Usage: gemi [options] <url>\n"
|
"Usage: gemi [options] <url>\n"
|
||||||
"Options:\n"
|
"Options:\n"
|
||||||
" -b: browse mode (experimental)\n"
|
" -b: browse mode (experimental)\n"
|
||||||
|
" -p: use pager: value of PAGER (default is less)\n"
|
||||||
" -n: don't strip header\n"
|
" -n: don't strip header\n"
|
||||||
" -d: debug\n"
|
" -d: debug\n"
|
||||||
" -h: show this dialog\n"
|
" -h: show this dialog\n"
|
||||||
|
@ -30,15 +32,19 @@ static void usage(bool fail)
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
int rv = EXIT_FAILURE;
|
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 redirections = 0, redirections_pos = 0;
|
||||||
|
|
||||||
int c;
|
int c;
|
||||||
while ((c = getopt(argc, argv, "+bndhr:")) != -1) {
|
while ((c = getopt(argc, argv, "+bpndhr:")) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'b':
|
case 'b':
|
||||||
browse = true;
|
browse = true;
|
||||||
break;
|
break;
|
||||||
|
case 'p':
|
||||||
|
pager = true;
|
||||||
|
break;
|
||||||
case 'n':
|
case 'n':
|
||||||
no_strip = true;
|
no_strip = true;
|
||||||
break;
|
break;
|
||||||
|
@ -123,12 +129,20 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
signal(SIGPIPE, SIG_IGN);
|
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;
|
struct mmap_file output;
|
||||||
if (browse) {
|
if (browse) {
|
||||||
output = create_mmap_from_file(NULL, PROT_MEM);
|
output = create_mmap_from_file(NULL, PROT_MEM);
|
||||||
} else {
|
} else {
|
||||||
output = create_mmap_from_FILE(stdout, "w");
|
output = create_mmap_from_FILE(output_stream, "w");
|
||||||
}
|
}
|
||||||
if (ERROR_MMAP(output)) {
|
if (ERROR_MMAP(output)) {
|
||||||
return rv;
|
return rv;
|
||||||
|
@ -151,6 +165,16 @@ int main(int argc, char **argv)
|
||||||
goto early_out;
|
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) {
|
if (redirect_link) {
|
||||||
redirections += 1;
|
redirections += 1;
|
||||||
// redirect link was stored in callback
|
// redirect link was stored in callback
|
||||||
|
@ -182,10 +206,6 @@ int main(int argc, char **argv)
|
||||||
execvp(progpath, new_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) {
|
if (browse) {
|
||||||
struct gemini_link_node *head = NULL;
|
struct gemini_link_node *head = NULL;
|
||||||
int n = get_links_from_gmi((char *)output.data, &head);
|
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
|
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 = 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)
|
LIBSOBJS = $(BASEENCODEOBJS) $(PURROBJS)
|
||||||
|
|
||||||
HEADERS = purr.h mmap_file.h read_certs.h gemini.h
|
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