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 const char *forced_pager;
  27
  28void force_pager(const char *pager)
  29{
  30        forced_pager = pager;
  31}
  32
  33static void pager_preexec(void)
  34{
  35        /*
  36         * Work around bug in "less" by not starting it until we
  37         * have real input
  38         */
  39        fd_set in;
  40        fd_set exception;
  41
  42        FD_ZERO(&in);
  43        FD_ZERO(&exception);
  44        FD_SET(0, &in);
  45        FD_SET(0, &exception);
  46        select(1, &in, NULL, &exception, NULL);
  47
  48        setenv("LESS", "FRSX", 0);
  49}
  50
  51static const char *pager_argv[] = { "sh", "-c", NULL, NULL };
  52static struct child_process pager_process;
  53
  54static void wait_for_pager(void)
  55{
  56        fflush(stdout);
  57        fflush(stderr);
  58        /* signal EOF to pager */
  59        close(1);
  60        close(2);
  61        finish_command(&pager_process);
  62}
  63
  64static void wait_for_pager_signal(int signo)
  65{
  66        wait_for_pager();
  67        sigchain_pop(signo);
  68        raise(signo);
  69}
  70
  71void setup_pager(void)
  72{
  73        const char *pager = getenv(subcmd_config.pager_env);
  74        struct winsize sz;
  75
  76        if (forced_pager)
  77                pager = forced_pager;
  78        if (!isatty(1) && !forced_pager)
  79                return;
  80        if (ioctl(1, TIOCGWINSZ, &sz) == 0)
  81                pager_columns = sz.ws_col;
  82        if (!pager)
  83                pager = getenv("PAGER");
  84        if (!(pager || access("/usr/bin/pager", X_OK)))
  85                pager = "/usr/bin/pager";
  86        if (!(pager || access("/usr/bin/less", X_OK)))
  87                pager = "/usr/bin/less";
  88        if (!pager)
  89                pager = "cat";
  90        if (!*pager || !strcmp(pager, "cat"))
  91                return;
  92
  93        spawned_pager = 1; /* means we are emitting to terminal */
  94
  95        /* spawn the pager */
  96        pager_argv[2] = pager;
  97        pager_process.argv = pager_argv;
  98        pager_process.in = -1;
  99        pager_process.preexec_cb = pager_preexec;
 100
 101        if (start_command(&pager_process))
 102                return;
 103
 104        /* original process continues, but writes to the pipe */
 105        dup2(pager_process.in, 1);
 106        if (isatty(2))
 107                dup2(pager_process.in, 2);
 108        close(pager_process.in);
 109
 110        /* this makes sure that the parent terminates after the pager */
 111        sigchain_push_common(wait_for_pager_signal);
 112        atexit(wait_for_pager);
 113}
 114
 115int pager_in_use(void)
 116{
 117        return spawned_pager;
 118}
 119
 120int pager_get_columns(void)
 121{
 122        char *s;
 123
 124        s = getenv("COLUMNS");
 125        if (s)
 126                return atoi(s);
 127
 128        return (pager_columns ? pager_columns : 80) - 2;
 129}
 130