linux/tools/lib/subcmd/pager.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <sys/select.h>
   3#include <stdlib.h>
   4#include <stdio.h>
   5#include <string.h>
   6#include <signal.h>
   7#include <sys/ioctl.h>
   8#include "pager.h"
   9#include "run-command.h"
  10#include "sigchain.h"
  11#include "subcmd-config.h"
  12
  13/*
  14 * This is split up from the rest of git so that we can do
  15 * something different on Windows.
  16 */
  17
  18static int spawned_pager;
  19static int pager_columns;
  20
  21void pager_init(const char *pager_env)
  22{
  23        subcmd_config.pager_env = pager_env;
  24}
  25
  26static void pager_preexec(void)
  27{
  28        /*
  29         * Work around bug in "less" by not starting it until we
  30         * have real input
  31         */
  32        fd_set in;
  33        fd_set exception;
  34
  35        FD_ZERO(&in);
  36        FD_ZERO(&exception);
  37        FD_SET(0, &in);
  38        FD_SET(0, &exception);
  39        select(1, &in, NULL, &exception, NULL);
  40
  41        setenv("LESS", "FRSX", 0);
  42}
  43
  44static const char *pager_argv[] = { "sh", "-c", NULL, NULL };
  45static struct child_process pager_process;
  46
  47static void wait_for_pager(void)
  48{
  49        fflush(stdout);
  50        fflush(stderr);
  51        /* signal EOF to pager */
  52        close(1);
  53        close(2);
  54        finish_command(&pager_process);
  55}
  56
  57static void wait_for_pager_signal(int signo)
  58{
  59        wait_for_pager();
  60        sigchain_pop(signo);
  61        raise(signo);
  62}
  63
  64void setup_pager(void)
  65{
  66        const char *pager = getenv(subcmd_config.pager_env);
  67        struct winsize sz;
  68
  69        if (!isatty(1))
  70                return;
  71        if (ioctl(1, TIOCGWINSZ, &sz) == 0)
  72                pager_columns = sz.ws_col;
  73        if (!pager)
  74                pager = getenv("PAGER");
  75        if (!(pager || access("/usr/bin/pager", X_OK)))
  76                pager = "/usr/bin/pager";
  77        if (!(pager || access("/usr/bin/less", X_OK)))
  78                pager = "/usr/bin/less";
  79        if (!pager)
  80                pager = "cat";
  81        if (!*pager || !strcmp(pager, "cat"))
  82                return;
  83
  84        spawned_pager = 1; /* means we are emitting to terminal */
  85
  86        /* spawn the pager */
  87        pager_argv[2] = pager;
  88        pager_process.argv = pager_argv;
  89        pager_process.in = -1;
  90        pager_process.preexec_cb = pager_preexec;
  91
  92        if (start_command(&pager_process))
  93                return;
  94
  95        /* original process continues, but writes to the pipe */
  96        dup2(pager_process.in, 1);
  97        if (isatty(2))
  98                dup2(pager_process.in, 2);
  99        close(pager_process.in);
 100
 101        /* this makes sure that the parent terminates after the pager */
 102        sigchain_push_common(wait_for_pager_signal);
 103        atexit(wait_for_pager);
 104}
 105
 106int pager_in_use(void)
 107{
 108        return spawned_pager;
 109}
 110
 111int pager_get_columns(void)
 112{
 113        char *s;
 114
 115        s = getenv("COLUMNS");
 116        if (s)
 117                return atoi(s);
 118
 119        return (pager_columns ? pager_columns : 80) - 2;
 120}
 121