📄 pg.c
字号:
/* * pg - A clone of the System V CRT paging utility. * * Copyright (c) 2000-2001 Gunnar Ritter. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. [deleted] * 4. Neither the name of Gunnar Ritter nor the names of his contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. *//* Sccsid @(#)pg.c 1.44 (gritter) 2/8/02 - modified for util-linux *//* * #define _XOPEN_SOURCE 500L * * Adding this define gives us the correct prototypes for fseeko, ftello, * but (for some glibc versions) conflicting prototype for wcwidth. * So, avoid defining _XOPEN_SOURCE, and give prototypes for fseeko, ftello * by hand. */#include <sys/types.h>#include <sys/wait.h>#include <sys/stat.h>#ifndef TIOCGWINSZ#include <sys/ioctl.h>#endif#include <sys/termios.h>#include <fcntl.h>#include <regex.h>#include <stdio.h>#include <string.h>#include <stdlib.h>#include <limits.h>#include <ctype.h>#include <errno.h>#include <unistd.h>#include <signal.h>#include <setjmp.h>#include <locale.h>#include <nl_types.h>#include <libgen.h>#if NCH#include <ncurses.h>#else#include <curses.h>#endif#include <term.h>#include "nls.h"#include "widechar.h"#include "../defines.h" /* for HAVE_fseeko */#define READBUF LINE_MAX /* size of input buffer */#define CMDBUF 255 /* size of command buffer */#define TABSIZE 8 /* spaces consumed by tab character *//* * Avoid the message "`var' might be clobbered by `longjmp' or `vfork'" */#define CLOBBGRD(a) (void)(&(a));#define cuc(c) ((c) & 0377)enum { FORWARD = 1, BACKWARD = 2 }; /* search direction */enum { TOP, MIDDLE, BOTTOM }; /* position of matching line *//* * States for syntax-aware command line editor. */enum { COUNT, SIGN, CMD_FIN, SEARCH, SEARCH_FIN, ADDON_FIN, STRING, INVALID};/* * Current command */struct { char cmdline[CMDBUF]; size_t cmdlen; int count; int key; char pattern[CMDBUF]; char addon;} cmd;/* * Position of file arguments on argv[] to main() */struct { int first; int current; int last;} files;void (*oldint)(int); /* old SIGINT handler */void (*oldquit)(int); /* old SIGQUIT handler */void (*oldterm)(int); /* old SIGTERM handler */char *tty; /* result of ttyname(1) */char *progname; /* program name */unsigned ontty; /* whether running on tty device */unsigned exitstatus; /* exit status */int pagelen = 23; /* lines on a single screen page */int ttycols = 79; /* screen columns (starting at 0) */struct termios otio; /* old termios settings */int tinfostat = -1; /* terminfo routines initialized */int searchdisplay = TOP; /* matching line position */regex_t re; /* regular expression to search for */int remembered; /* have a remembered search string */int cflag; /* clear screen before each page */int eflag; /* suppress (EOF) */int fflag; /* do not split lines */int nflag; /* no newline for commands required */int rflag; /* "restricted" pg */int sflag; /* use standout mode */char *pstring = ":"; /* prompt string */char *searchfor; /* search pattern from argv[] */int havepagelen; /* page length is manually defined */long startline; /* start line from argv[] */int nextfile = 1; /* files to advance */jmp_buf jmpenv; /* jump from signal handlers */int canjump; /* jmpenv is valid */wchar_t wbuf[READBUF]; /* used in several widechar routines */const char *copyright ="@(#)pg 1.44 2/8/02. Copyright (c) 2000-2001 Gunnar Ritter. ";const char *helpscreen = "All rights reserved.\n\-------------------------------------------------------\n\ h this screen\n\ q or Q quit program\n\ <newline> next page\n\ f skip a page forward\n\ d or ^D next halfpage\n\ l next line\n\ $ last page\n\ /regex/ search forward for regex\n\ ?regex? or ^regex^ search backward for regex\n\ . or ^L redraw screen\n\ w or z set page size and go to next page\n\ s filename save current file to filename\n\ !command shell escape\n\ p go to previous file\n\ n go to next file\n\\n\Many commands accept preceding numbers, for example:\n\+1<newline> (next page); -1<newline> (previous page); 1<newline> (first page).\n\\n\See pg(1) for more information.\n\-------------------------------------------------------\n";#ifdef HAVE_fseeko#if defined (_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS) == 64 extern int fseeko64(FILE *f, off_t off, int whence); extern off_t ftello64(FILE *f); #define my_fseeko fseeko64 #define my_ftello ftello64#else extern int fseeko(FILE *f, off_t off, int whence); extern off_t ftello(FILE *f); #define my_fseeko fseeko #define my_ftello ftello#endif#else static int my_fseeko(FILE *f, off_t off, int whence) { return fseek(f, (long) off, whence); } static off_t my_ftello(FILE *f) { return (off_t) ftell(f); }#endif#ifdef USE_SIGSET /* never defined *//* sigset and sigrelse are obsolete - use when POSIX stuff is unavailable */#define my_sigset sigset#define my_sigrelse sigrelse#elsestatic int my_sigrelse(int sig) { sigset_t sigs; if (sigemptyset(&sigs) || sigaddset(&sigs, sig)) return -1; return sigprocmask(SIG_UNBLOCK, &sigs, NULL);}typedef void (*my_sighandler_t)(int);static my_sighandler_t my_sigset(int sig, my_sighandler_t disp) { struct sigaction act, oact; act.sa_handler = disp; if (sigemptyset(&act.sa_mask)) return SIG_ERR; act.sa_flags = 0; if (sigaction(sig, &act, &oact)) return SIG_ERR; if (my_sigrelse(sig)) return SIG_ERR; return oact.sa_handler;}#endif/* * Quit pg. */static voidquit(int status){ exit(status < 0100 ? status : 077);}/* * Memory allocator including check. */static char *smalloc(size_t s){ char *m = (char *)malloc(s); if (m == NULL) { write(2, "Out of memory\n", 14); quit(++exitstatus); } return m;}/* * Usage message and similar routines. */static voidusage(void){ fprintf(stderr, _("%s: Usage: %s [-number] [-p string] [-cefnrs] " "[+line] [+/pattern/] [files]\n"), progname, progname); quit(2);}static voidneedarg(char *s){ fprintf(stderr, _("%s: option requires an argument -- %s\n"), progname, s); usage();}static voidinvopt(char *s){ fprintf(stderr, _("%s: illegal option -- %s\n"), progname, s); usage();}#ifdef ENABLE_WIDECHAR/* * A mbstowcs()-alike function that transparently handles invalid sequences. */static size_txmbstowcs(wchar_t *pwcs, const char *s, size_t nwcs){ size_t n = nwcs; int c; mbtowc(pwcs, NULL, MB_CUR_MAX); while (*s && n) { if ((c = mbtowc(pwcs, s, MB_CUR_MAX)) < 0) { s++; *pwcs = L'?'; } else s += c; pwcs++; n--; } if (n) *pwcs = L'\0'; mbtowc(pwcs, NULL, MB_CUR_MAX); return nwcs - n;}#endif/* * Helper function for tputs(). */static intoutcap(int i){ char c = i; return write(1, &c, 1);}/* * Write messages to terminal. */static voidmesg(char *message){ if (ontty == 0) return; if (*message != '\n' && sflag) vidputs(A_STANDOUT, outcap); write(1, message, strlen(message)); if (*message != '\n' && sflag) vidputs(A_NORMAL, outcap);}/* * Get the window size. */static voidgetwinsize(void){ static int initialized, envlines, envcols, deflines, defcols;#ifdef TIOCGWINSZ struct winsize winsz; int badioctl;#endif char *p; if (initialized == 0) { if ((p = getenv("LINES")) != NULL && *p != '\0') if ((envlines = atoi(p)) < 0) envlines = 0; if ((p = getenv("COLUMNS")) != NULL && *p != '\0') if ((envcols = atoi(p)) < 0) envcols = 0; /* terminfo values. */ if (tinfostat != 1 || columns == 0) defcols = 24; else defcols = columns; if (tinfostat != 1 || lines == 0) deflines = 80; else deflines = lines; initialized = 1; }#ifdef TIOCGWINSZ badioctl = ioctl(1, TIOCGWINSZ, &winsz);#endif if (envcols) ttycols = envcols - 1;#ifdef TIOCGWINSZ else if (!badioctl) ttycols = winsz.ws_col - 1;#endif else ttycols = defcols - 1; if (havepagelen == 0) { if (envlines) pagelen = envlines - 1;#ifdef TIOCGWINSZ else if (!badioctl) pagelen = winsz.ws_row - 1;#endif else pagelen = deflines - 1; }}/* * Message if skipping parts of files. */static voidskip(int direction){ if (direction > 0) mesg(_("...skipping forward\n")); else mesg(_("...skipping backward\n"));}/* * Signal handler while reading from input file. */static voidsighandler(int signum){ if (canjump && (signum == SIGINT || signum == SIGQUIT)) longjmp(jmpenv, signum); tcsetattr(1, TCSADRAIN, &otio); quit(exitstatus);}/* * Check whether the requested file was specified on the command line. */static intcheckf(void){ if (files.current + nextfile >= files.last) { mesg(_("No next file")); return 1; } if (files.current + nextfile < files.first) { mesg(_("No previous file")); return 1; } return 0;}#ifdef ENABLE_WIDECHAR/* * Return the last character that will fit on the line at col columns * in case MB_CUR_MAX > 1. */static char *endline_for_mb(unsigned col, char *s){ unsigned pos = 0; wchar_t *p = wbuf; wchar_t *end; size_t wl; char *t = s; if ((wl = xmbstowcs(wbuf, t, sizeof wbuf - 1)) == (size_t)-1) return s + 1; wbuf[wl] = L'\0'; while (*p != L'\0') { switch (*p) { /* * Cursor left. */ case L'\b': if (pos > 0) pos--; break; /* * No cursor movement. */ case L'\a': break; /* * Special. */ case L'\r': pos = 0; break; case L'\n': end = p + 1; goto ended; /* * Cursor right. */ case L'\t': pos += TABSIZE - (pos % TABSIZE); break; default: pos += wcwidth(*p); } if (pos > col) { if (*p == L'\t') p++; else if (pos > col + 1) /* * wcwidth() found a character that * has multiple columns. What happens * now? Assume the terminal will print * the entire character onto the next * row. */ p--; if (*++p == L'\n') p++; end = p; goto ended; } p++; } end = p; ended: *end = L'\0'; p = wbuf; if ((pos = wcstombs(NULL, p, READBUF)) == -1) return s + 1; return s + pos;}#endif/* * Return the last character that will fit on the line at col columns. */static char *endline(unsigned col, char *s){ unsigned pos = 0; char *t = s;#ifdef ENABLE_WIDECHAR if (MB_CUR_MAX > 1) return endline_for_mb(col, s);#endif while (*s != '\0') { switch (*s) { /* * Cursor left. */ case '\b': if (pos > 0) pos--; break; /* * No cursor movement. */ case '\a': break; /* * Special. */ case '\r': pos = 0; break; case '\n': t = s + 1; goto cend; /* * Cursor right. */ case '\t': pos += TABSIZE - (pos % TABSIZE); break; default: pos++; } if (pos > col) { if (*s == '\t') s++; if (*++s == '\n') s++; t = s; goto cend; } s++; } t = s; cend: return t;}/* * Clear the current line on the terminal's screen. */static voidcline(void){ char *buf = (char *)smalloc(ttycols + 2); memset(buf, ' ', ttycols + 2); buf[0] = '\r'; buf[ttycols + 1] = '\r'; write(1, buf, ttycols + 2); free(buf);}/* * Evaluate a command character's semantics. */static intgetstate(int c){ switch (c) { case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '0': case '\0': return COUNT; case '-': case '+': return SIGN; case 'l': case 'd': case '\004': case 'f': case 'z': case '.': case '\014': case '$': case 'n': case 'p': case 'w': case 'h': case 'q': case 'Q': return CMD_FIN; case '/': case '?': case '^': return SEARCH;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -