diff --git a/.travis.yml b/.travis.yml index 68d910d..df89971 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,4 @@ +language: generic sudo: required dist: trusty @@ -28,5 +29,12 @@ script: - lintian --profile debian -i --fail-on-warnings -EvIL +pedantic ../vimpager*.changes - make all - make test + - | + set -e + sudo make install + [[ "$(which vimpager)" == /usr/local/bin/vimpager ]] + [[ "$(which vimcat)" == /usr/local/bin/vimcat ]] + vimpager -v + vimcat -v # vim: sw=4 diff --git a/Makefile b/Makefile index 10e2278..5a632f1 100644 --- a/Makefile +++ b/Makefile @@ -42,19 +42,27 @@ all: ${PROGRAMS:=-vertag-stamp} standalone/vimpager standalone/vimcat docs @git describe >$<-version.txt 2>/dev/null \ || sed -n '/^[0-9][0-9.]* [0-9-]*:$$/{s/ .*//;p;q;}' ChangeLog_$<.yml >$<-version.txt -standalone/vimpager: vimpager vimpager-version.txt ${SRC:=.uu} inc/* Makefile +standalone/vimpager: vimpager vimpager-version.txt ${SRC:=.uu} inc/bundled_scripts.sh inc/do_uudecode.sh inc/prologue.sh inc/vimpager_functions.sh Makefile @echo building $@ @${MKPATH} ${@D} @sed \ - -e '/^ *\. .*inc\/prologue.sh"$$/{' \ + -e '/^\. .*inc\/prologue.sh"$$/{' \ -e 'r inc/prologue.sh' \ -e d \ -e '}' \ + -e '/^\. .*inc\/vimpager_functions.sh"$$/{' \ + -e 'r inc/vimpager_functions.sh' \ + -e d \ + -e '}' \ + -e '/^\. .*inc\/common_functions.sh"$$/{' \ + -e 'r inc/common_functions.sh' \ + -e d \ + -e '}' \ -e 's/^\( *\)# EXTRACT BUNDLED SCRIPTS HERE$$/\1extract_bundled_scripts/' \ - -e 's|^version=.*|version="'"`cat vimpager-version.txt`"' (standalone, shell=\$$(command -v \$$POSIX_SHELL))"|' \ - -e 's!^\( *\)runtime=.*$$!\1runtime='\''\$$tmp/runtime'\''!' \ - -e 's!^\( *\)vimcat=.*$$!\1vimcat='\''\$$runtime/bin/vimcat'\''!' \ - -e 's!^\( *\)system_vimpagerrc=.*$$!\1system_vimpagerrc='\'\''!' \ + -e 's!^version=.*!version="'"`cat vimpager-version.txt`"' (standalone, shell=\$$(command -v \$$POSIX_SHELL))"!' \ + -e 's!^runtime=.*$$!runtime='\''\$$tmp/runtime'\''!' \ + -e 's!^vimcat=.*$$!vimcat='\''\$$runtime/bin/vimcat'\''!' \ + -e 's!^system_vimpagerrc=.*$$!system_vimpagerrc='\'\''!' \ -e '/^# INCLUDE BUNDLED SCRIPTS HERE$$/{ q; }' \ vimpager > $@ @cat inc/do_uudecode.sh >> $@ @@ -63,7 +71,7 @@ standalone/vimpager: vimpager vimpager-version.txt ${SRC:=.uu} inc/* Makefile @sed -n '/^# END OF BUNDLED SCRIPTS$$/,$$p' vimpager >> $@ @chmod +x $@ -standalone/vimcat: vimcat autoload/vimcat.vim vimcat-version.txt inc/prologue.sh Makefile +standalone/vimcat: vimcat autoload/vimcat.vim inc/prologue.sh inc/vimcat_functions.sh inc/common_functions.sh Makefile vimcat-version.txt @echo building $@ @${MKPATH} ${@D} @nlinit=`echo 'nl="'; echo '"'`; eval "$$nlinit"; \ @@ -79,6 +87,14 @@ standalone/vimcat: vimcat autoload/vimcat.vim vimcat-version.txt inc/prologue.sh -e 'r inc/prologue.sh' \ -e d \ -e '}' \ + -e '/^\. .*inc\/vimcat_functions.sh"$$/{' \ + -e 'r inc/vimcat_functions.sh' \ + -e d \ + -e '}' \ + -e '/^\. .*inc\/common_functions.sh"$$/{' \ + -e 'r inc/common_functions.sh' \ + -e d \ + -e '}' \ vimcat > $@ @cp $@ $@.work @awk '/^[ ]*(if|for|while)/ { print $$1 }' $@ \ @@ -89,16 +105,24 @@ standalone/vimcat: vimcat autoload/vimcat.vim vimcat-version.txt inc/prologue.sh @sed -e 's/vimcat#\([^ ]*\)(/\1(/g' autoload/vimcat.vim >> $@ @chmod +x $@ -vimcat.uu: vimcat vimcat-version.txt +vimcat.uu: vimcat vimcat-version.txt inc/vimcat_functions.sh inc/common_functions.sh inc/prologue.sh Makefile @echo uuencoding vimcat @echo 'vimcat_script() {' > $@ @printf "\t(cat <<'EOF') | do_uudecode > bin/vimcat\n" >> $@ @sed \ -e 's|^version=.*|version="'"`cat vimcat-version.txt`"' (bundled, shell=\$$(command -v \$$POSIX_SHELL))"|' \ - -e '/^ *\. .*inc\/prologue.sh"$$/{' \ + -e '/^\. .*inc\/prologue.sh"$$/{' \ -e 'r inc/prologue.sh' \ -e d \ -e '}' \ + -e '/^\. .*inc\/vimcat_functions.sh"$$/{' \ + -e 'r inc/vimcat_functions.sh' \ + -e d \ + -e '}' \ + -e '/^\. .*inc\/common_functions.sh"$$/{' \ + -e 'r inc/common_functions.sh' \ + -e d \ + -e '}' \ vimcat > $@.work @uuencode $@.work vimcat >> $@ @echo EOF >> $@ @@ -173,9 +197,11 @@ install: docs vimpager.configured vimcat.configured echo ${INSTALLCONF} vimpagerrc "$${SYSCONFDIR}/vimpagerrc"; \ ${INSTALLCONF} vimpagerrc "$${SYSCONFDIR}/vimpagerrc" -%.configured: % %-version.txt +%.configured: % %-version.txt inc/%_functions.sh inc/common_functions.sh @echo configuring $< @POSIX_SHELL="`scripts/find_shell`"; \ + BASE='${@F}'; \ + BASE=$${BASE%.configured}; \ if [ '${PREFIX}' = /usr ]; then \ vimpagerrc=/etc/vimpagerrc; \ else \ @@ -184,11 +210,19 @@ install: docs vimpager.configured vimcat.configured sed -e '1{ s|.*|#!'"$$POSIX_SHELL"'|; }' \ -e 's|\$$POSIX_SHELL|'"$$POSIX_SHELL|" \ -e '/^ *\. .*inc\/prologue.sh"$$/d' \ - -e 's|^version=.*|version="'"`cat $<-version.txt`"' (configured, shell='"$$POSIX_SHELL"')"|' \ + -e 's|^version=.*|version="'"`cat $$BASE-version.txt`"' (configured, shell='"$$POSIX_SHELL"')"|' \ -e '/^# FIND REAL PARENT DIRECTORY$$/,/^# END OF FIND REAL PARENT DIRECTORY$$/d' \ - -e 's!^\( *\)runtime=.*!\1runtime='\''${PREFIX}/share/vimpager'\''!' \ - -e 's!^\( *\)vimcat=.*!\1vimcat='\''${PREFIX}/bin/vimcat'\''!' \ - -e 's!^\( *\)system_vimpagerrc=.*!\1system_vimpagerrc='\'"$$vimpagerrc"\''!' \ + -e 's!^runtime=.*!runtime='\''${PREFIX}/share/vimpager'\''!' \ + -e 's!^vimcat=.*!vimcat='\''${PREFIX}/bin/vimcat'\''!' \ + -e 's!^system_vimpagerrc=.*!system_vimpagerrc='\'"$$vimpagerrc"\''!' \ + -e '/^\. .*inc\/'"$$BASE"'_functions.sh"$$/{' \ + -e "r inc/$${BASE}_functions.sh" \ + -e d \ + -e '}' \ + -e '/^\. .*inc\/common_functions.sh"$$/{' \ + -e 'r inc/common_functions.sh' \ + -e d \ + -e '}' \ $< > $@ @chmod +x $@ diff --git a/inc/common_functions.sh b/inc/common_functions.sh new file mode 100644 index 0000000..1d8c80c --- /dev/null +++ b/inc/common_functions.sh @@ -0,0 +1,75 @@ +# Function definitions common for vimpager and vimcat. + +squeeze_blank_lines() { + sed '/^[ ]*$/{ + N + /^[ ]*\n[ ]*$/D + }' +} + +create_tmp_directory() { + if ! mkdir $mkdir_options "$tmp"; then + echo "ERROR: Could not create temporary directory $tmp" >&2 + rm -rf "$tmp" 2>/dev/null # rm -rf "" shows error on OpenBSD + exit 1 + fi +} + +set_system_vars() { + case "$(uname -s)" in + Linux) linux=1;; + SunOS) solaris=1;; + Darwin) osx=1 bsd=1;; + CYGWIN*) cygwin=1 win32=1;; + MINGW*) msys=1 win32=1;; + MSYS*) msys=1 win32=1;; + OpenBSD) openbsd=1 bsd=1;; + FreeBSD) freebsd=1 bsd=1;; + NetBSD) netbsd=1 bsd=1;; + *) bsd=1;; + esac +} + +install_trap() { + trap 'quit 1' PIPE HUP INT QUIT ILL TRAP KILL BUS TERM +} + +check_for_cygpath() { + # special handling to rewrite cygwin/msys paths to windows POSIX paths + if [ -n "$win32" ] && command -v cygpath >/dev/null; then + _have_cygpath=1 + fi +} + +resolve_path() { + if [ -n "$_have_cygpath" ]; then + cygpath --unix "$1" + else + echo "$1" + fi +} + +find_tmp_directory() { + # Find and create the temporary directory used by vimpager. Set the $tmp and $mkdir_options variable. + mkdir_options='-m 700' + # Default to /tmp + tmp=/tmp + + if [ -n "$win32" ]; then + # Use the real TEMP directory on windows in case we are + # using a native vim/gvim + # TEMP can be /tmp sometimes too + tmp=$(resolve_path "$TEMP") + if [ -n "$msys" -a -n "$temp" ]; then + # MSYS2 is a little tricky, we're gonna stick to the user's private temp + tmp=$(resolve_path "$temp") + fi + # chmod doesn't work here, even in /tmp sometimes + mkdir_options= + fi + + # Add a final component to the path, all other temp files should be placed in this directory. + tmp=$tmp/${prog}_$$ +} + +# vim: sw=4 et tw=0: diff --git a/inc/vimcat_functions.sh b/inc/vimcat_functions.sh new file mode 100644 index 0000000..889a17c --- /dev/null +++ b/inc/vimcat_functions.sh @@ -0,0 +1,290 @@ +# Function definitions for vimcat. + +# the name of this program +prog=vimcat + +quit() { + ( + kill "$vim_pid" >/dev/null 2>&1 + do_sleep 100 + kill -9 "$vim_pid" >/dev/null 2>&1 + + kill "$pipeline_pid" >/dev/null 2>&1 + do_sleep 100 + kill -9 "$pipeline_pid" >/dev/null 2>&1 + + kill "$tail_pid" >/dev/null 2>&1 + do_sleep 100 + kill -9 "$tail_pid" >/dev/null 2>&1 + + cd "${tmp%/*}" 2>/dev/null # some systems cannot remove CWD + + rm -rf "$tmp" 2>/dev/null # rm -rf "" shows error on OpenBSD + ) & + exit "${1:-0}" +} + +do_sleep() { + _ms=${1:-100} + "$vim" -NEsnR -i NONE -u NONE +"sleep $_ms m" +q >/dev/null 2>&1 +} + +usage() { + cat <<'EOF' +Usage: vimcat [OPTION]... [FILE | -]... +Display FILE(s) in the terminal with (n)vim syntax highlighting using ANSI escape codes. + +With no FILE, or when FILE is -, read standard input. + + -h, --help, --usage This help screen. + -v, --version Show version information and exit. + -n Print with line numbers. + -s Squeeze multiple blank lines into one. + -o FILE | - Output ANSI highlighted text to FILE or standard output. + --cmd COMMAND Run (n)vim COMMAND before initialization. + -c COMMAND Run (n)vim COMMAND after initialization. + -u FILE Use FILE as the vimrc. + -x  Give debugging output on stderr. + +Examples: + vimcat program.py # output program.py with highlighting to terminal + +Project homepage: <http://github.com/rkitover/vimpager> +and documentation: <https://github.com/rkitover/vimpager/blob/master/markdown/vimcat.md> +or available locally via: man vimcat +EOF +} + +write_chunks() { + cd "$chunks_dir" + rm -f -- * + split -b 4096 - + touch PIPELINE_DONE +} + +start_pipeline() { + if [ -n "$pipeline" ]; then + pipeline="$pipeline | write_chunks" + else + pipeline=write_chunks + fi + cat -- "$pipeline_start" | (eval "$pipeline" <&3 & echo $! > "$tmp/pipeline_pid") 3<&0 + pipeline_pid=$(cat "$tmp/pipeline_pid") +} + +start_highlight_job() { + # INSERT VIMCAT_DEBUG PREPARATION HERE + set -- -NE -i NONE -n \ + --cmd "set runtimepath^=$runtime" \ + --cmd "call vimcat#Init({ 'rc': '$vimcatrc' })" \ + --cmd visual \ + ${extra_cmd:+--cmd "$extra_cmd"} \ + ${extra_c:+-c "$extra_c"} \ + -c "call vimcat#Run(\"$dest_file\", ${line_numbers:-0}, \"$chunks_dir\", \"$pipeline_start\")" + + [ -n "$vimcatrc" ] && set -- "$@" -u "$vimcatrc" + + if [ "${VIMCAT_DEBUG:-0}" -eq 0 ]; then + ("$vim" "$@" /dev/null 2>&1; touch "$tmp/vim_done") & + vim_pid=$! + else + "$vim" "$@" /dev/null; then + vim=vim + elif command -v nvim >/dev/null; then + vim=nvim + else + echo "$0: neither vim nor nvim found, vim or nvim is required for vimcat" >&2 + exit 1 + fi +} + +parse_command_line_options_1() { + # if no args and no stdin, display usage + if [ $# -eq 0 -a -t 0 ]; then + usage + quit 0 + fi + + # check for -h before main option parsing, this is much faster + for arg in "$@"; do + case "$arg" in + "-h"|"--help"|"-help"|"--usage"|"-usage") + usage + quit 0 + ;; + "-v"|"--version"|"-version") + echo "vimcat $version" + quit 0 + ;; + "-x") + set -x + ;; + esac + done +} + +create_fifo() { + tmp_file_in=$tmp/vimcat_in.txt + out_fifo=$tmp/vimcat_out.fifo + + if [ -n "$solaris" -o -n "$win32" ]; then + # the fifo streaming doesn't work on windows and solaris + touch "$out_fifo" + else + mkfifo "$out_fifo" + fi +} + +main() { + # check for arguments + while [ $# -gt 0 ] ; do + case "$1" in + "-c") + shift + if [ -z "$extra_c" ]; then + extra_c=$1 + else + extra_c="$extra_c | $1" + fi + shift + ;; + "--cmd") + shift + if [ -z "$extra_cmd" ]; then + extra_cmd=$1 + else + extra_cmd="$extra_cmd | $1" + fi + shift + ;; + "-u") + shift + vimcatrc=$1 + shift + ;; + "-o") + shift + output_file=$1 + shift + ;; + "-s") + shift + squeeze_blank_lines=1 + ;; + "-n") + shift + line_numbers=1 + ;; + "-x") + # xtrace should already be set by the first option parsing + shift + ;; + "--") + shift + break + ;; + -) + break + ;; + -*) + echo "$0: bad option '$1', see --help for usage." >&2 + quit 1 + ;; + *) + break + ;; + esac + done + + # Just pass through if not on a tty, unless -o was given + if [ -z "$output_file" ]; then + if [ ! -t 1 ]; then + exec cat "$@" + fi + fi + + if [ -z "$vimcatrc" ]; then + if [ -f ~/.vimcatrc ]; then + vimcatrc=~/.vimcatrc + else + vimcatrc= + fi + fi + + if [ $# -eq 0 ]; then + set -- - + fi + + if [ -n "$output_file" -a $# -gt 1 ]; then + echo "$0: -o can only be used with one input file or stdin." >&2 + quit 1 + fi + + chunks_dir=$tmp/chunks + mkdir "$chunks_dir" + + i=1 + for file in "$@" + do + if [ $# -ge 2 ]; then + if [ $i -gt 1 ]; then + printf '\n' + fi + printf "==> %s <==\n\n" "$file" + fi + + pipeline= + pipeline_start=$file + + if [ "${squeeze_blank_lines:-0}" -eq 1 ]; then + pipeline=squeeze_blank_lines + fi + + exit_code=0 + + # Check that the file is readable + if [ "$file" != - ]; then + if [ ! -r "$file" ]; then + echo "$0: Cannot read file: $file" >&2 + exit_code=1 + fi + + [ ! -s "$file" ] && continue + fi + + if [ -z "$output_file" -o "$output_file" = "-" ]; then + dest_file=$out_fifo + + tail -f "$out_fifo" & + tail_pid=$! + else + dest_file=$output_file + printf '' > "$dest_file" + fi + + start_highlight_job + start_pipeline + while [ ! -f "$tmp/vim_done" ]; do + do_sleep 50 + done + + if [ -n "$tail_pid" ]; then + # if it's not a fifo where this doesn't work, tail needs some time to catch up + [ ! -p "$out_fifo" ] && do_sleep 1100 + + kill $tail_pid >/dev/null 2>&1 + fi + + i=$((i + 1)) + done + + quit $exit_code +} + +# vim: sw=4 et tw=0: diff --git a/inc/vimpager_functions.sh b/inc/vimpager_functions.sh new file mode 100644 index 0000000..b92d72a --- /dev/null +++ b/inc/vimpager_functions.sh @@ -0,0 +1,862 @@ +# Function definitions for vimpager. + +# the name of this program +prog=vimpager + +# this is compatible with osed +ANSI_RE='\[[;?]*[0-9.;]*[A-Za-z]' + +main() { + # if no args and no stdin, display usage + if [ $# -eq 0 -a -t 0 ]; then + usage + quit 0 + fi + + # Parse the command line options as early as possible. These variables might be set because of command line options and are thus initialized. + vim_options= + vimrc= + extra_c= + extra_cmd= + line_numbers=0 + + # Check for certain parameters to pass on to vim (or conceivably do something else) + # Couldn't use getopt or getopts as neither supports options prepended with + + while [ $# -gt 0 ] ; do + case "$1" in + -h|--help|-help|--usage|-usage) + usage + quit 0 + ;; + -v|--version|-version) + echo "vimpager $version" + quit 0 + ;; + +G|+) + vim_options="$vim_options +"; + shift + ;; + -N|--LINE-NUMBERS) + line_numbers=1 + shift + ;; + -c) + shift + extra_c="${extra_c:+$extra_c | }$1" + shift + ;; + --cmd) + shift + extra_cmd="${extra_cmd:+$extra_cmd | }$1" + shift + ;; + -u) + shift + vimrc=$1 + shift + ;; + -s) # Ubuntu man passes this option to /usr/bin/pager + shift + squeeze_blank_lines=1 + ;; + --) + shift + break + ;; + -x) + trace=1 + shift + set -x + ;; + -) + break + ;; + -*) + echo "$0: bad option '$1', see --help for usage." >&2 + quit 1 + ;; + *) + break + ;; + esac + done + + find_tmp_directory + + create_tmp_directory + + install_trap + + detect_term_size + + expand_config_vars + + find_vim_executable + + find_vimpagerrc_files + + read_vim_settings + + parse_pstree + + # if no args, assume stdin + if [ $# -eq 0 ]; then + set -- - + # turn off man/perldoc support for > 1 arg + elif [ $# -gt 1 ]; then + is_man= + is_perldoc= + is_doc= + force_strip_ansi= + fi + + file_idx=1 + + for file in "$@"; do + if [ "$file" = - ]; then + filename=stdin + else + if [ ! -r "$file" ]; then + echo "$0: cannot read file '$file'" >&2 + quit 1 + fi + + filename=$(resolve_path "$file") + fi + + set_key orig_file_names $file_idx "$filename" + + # $file still holds the orginal file name. $filename will be + # the encoded version of $file. $tempfile is the path under + # $tmp if the file is to be opend from there instead of the + # original location. If $tempfile is empty the file is to be + # opened as $file. + filename=$(encode_filename "$filename") + tempfile= + + case "$(echo "$file" | tr 'A-Z' 'a-z')" in + *.gz) + filename=${filename%.??} + tempfile=$tmp/$filename + gunzip -c -- "$file" > "$tempfile" + + ;; + *.bz2) + filename=${filename%.??2} + tempfile=$tmp/$filename + bunzip2 -c -- "$file" > "$tempfile" + ;; + *.xz) + filename=${filename%.??} + tempfile=$tmp/$filename + xzcat -c -- "$file" > "$tempfile" + ;; + *.z) + filename=${filename%.?} + tempfile=$tmp/$filename + uncompress -c -- "$file" > "$tempfile" + ;; + *) + if [ "$file" = - ]; then + tempfile=$tmp/$filename + cat -- "$file" > "$tempfile" + fi + ;; + esac + + # check for ANSI codes and strip if not using ansiesc + if head -100 "${tempfile:-$file}" | grep -Eq "$ANSI_RE"; then + if [ -z "$ansiesc_available" -o -n "$force_strip_ansi" ]; then + ansi_filter "${tempfile:-$file}" > "$tmp/$filename.work" + tempfile=$tmp/$filename + mv -f -- "$tempfile.work" "$tempfile" + else + echo 'call vimpager_utils#DoAnsiEsc()' >> "$tmp/$file_idx.vim" + set_key ansi_files "$file_idx" yes + fi + fi + + # squeeze blank lines if option was specified, Ubuntu man with /usr/bin/pager does this + if [ "${squeeze_blank_lines:-0}" -eq 1 ]; then + squeeze_blank_lines < "${tempfile:-$file}" > "$tmp/$filename.work" + tempfile=$tmp/$filename + mv -f -- "$tempfile.work" "$tempfile" + fi + + # dumb man detection when the pstree heuristic fails + if [ -z "$is_doc" ] && head -12 "${tempfile:-$file}" | grep -Eq '^N(.)?A(.)?M(.)?E(.)?[ \t]*$'; then + is_man=1 + is_doc=1 + fi + + # if it's a man page, remove starting blank lines, or the C syntax highlighting fails + # and write out ft command for vim + if [ -n "$is_doc" ]; then + ansi_filter "${tempfile:-$file}" | overstrike_filter | awk ' + BEGIN { skipblank=1 } + /^[ ]*$/ { if (!skipblank) print } + /[^ ]/ { skipblank=0; print } + ' > "$tmp/$filename.work" + tempfile=$tmp/$filename + mv -f -- "$tempfile.work" "$tempfile" + + if [ -n "$is_man" ]; then + echo 'set ft=man' >> "$tmp/$file_idx.vim" + elif [ -n "$is_perldoc" ]; then + echo 'set ft=perldoc' >> "$tmp/$file_idx.vim" + fi + fi + + # if file is zero length, or one blank line (cygwin), and is only arg, exit + if [ ! -s "${tempfile:-$file}" \ + -a $# -eq 1 \ + -o "$(cat "${tempfile:-$file}")" = "" \ + -a "$(wc -l < "${tempfile:-$file}")" -eq 1 ]; then + + quit 0 + fi + + set_key files $file_idx "$(resolve_path "${tempfile:-$file}")" + + file_idx=$((file_idx + 1)) + done + + file_count=$# + + set -- + i=1 + while [ $i -le $file_count ]; do + set -- "$@" "$(get_key files $i)" + i=$((i + 1)) + done + + if [ "${no_pass_thru:-0}" -ne 1 ] && fits_on_screen "$@"; then + cat_files=1 + fi + + page_files "$@" + + quit $? +} + +detect_term_size() { + # Detect the terminal size and set the variables $cols and $lines. If necessary $no_pass_thru is set. + if command -v tput >/dev/null; then + # this is the only way it works on some versions of Cygwin + + # 2>/dev/null makes tput not work, so don't do that + # we are just going to hope that tput errors don't happen + tput cols > "$tmp/cols" + tput lines > "$tmp/lines" + + cols=$(cat "$tmp/cols") + lines=$(cat "$tmp/lines") + + rm -f -- "$tmp/cols" "$tmp/lines" + fi + + # msys has no tput, this doesn't work on Cygwin by the way + if [ -z "$cols" ] && command -v bash >/dev/null; then + cols=$(bash -i -c 'echo $COLUMNS') + lines=$(bash -i -c 'echo $LINES') + fi + + # If we are unable to detect lines/columns, maximize + # the window. + if [ -z "$cols" ]; then + cols=999 + lines=999 + no_pass_thru=1 # force loading vimpager + fi +} + +find_vimpagerrc_files() { + # This function will find the system and the user vimpagerrc file and set the variables $system_vimrc and $vimrc. + + # determine location of rc file + i=1 + OLDIFS=$IFS + IFS=' +' + for var in $(IFS=$OLDIFS; "$tvim" -NEnR -i NONE ${vimrc:+-u "$vimrc"} +'call writefile(["", "VAL:" . $VIM, "VAL:" . $MYVIMRC], "/dev/stderr")' +q &1 >/dev/null); do + case "$var" in + VAL:*) + case $i in + 1) + vim_dir=${var#VAL:} + ;; + 2) + user_vimrc=${var#VAL:} + user_vimrc_dir=${user_vimrc%/*} + break + ;; + esac + i=$((i + 1)) + ;; + esac + done + IFS=$OLDIFS + + # find system vimrc + system_vimrc=$("$tvim" --version | sed -n '/system vimrc file: "/{ + s|\$VIM|'"$vim_dir"'| + s/.*: "\([^"]*\).*/\1/p + q + }') + + # find the users vimpagerrc + if [ -n "$vimrc" ]; then + # The vimrc file was given on the command line. + : + elif [ -n "$VIMPAGER_RC" ]; then + vimrc=$VIMPAGER_RC + # check for vimpagerrc in same dir as vimrc in case it is set in VIMINIT + elif [ -n "$user_vimrc_dir" -a -r "$user_vimrc_dir/.vimpagerrc" ]; then + vimrc=$user_vimrc_dir/.vimpagerrc + elif [ -n "$user_vimrc_dir" -a -r "$user_vimrc_dir/_vimpagerrc" ]; then + vimrc=$user_vimrc_dir/_vimpagerrc + elif [ -n "$user_vimrc_dir" -a -r "$user_vimrc_dir/vimpagerrc" ]; then + vimrc=$user_vimrc_dir/vimpagerrc + # check standard paths, according to :h initialization + elif [ -r ~/.vimpagerrc ]; then + vimrc=~/.vimpagerrc + elif [ -r ~/.vim/vimpagerrc ]; then + vimrc=~/.vim/vimpagerrc + elif [ -r ~/_vimpagerrc ]; then + vimrc=~/_vimpagerrc + elif [ -r ~/vimfiles/vimpagerrc ]; then + vimrc=~/vimfiles/vimpagerrc + elif [ -r "$vim_dir/_vimpagerrc" ]; then + vimrc=$vim_dir/_vimpagerrc + # try the user's ~/.vimrc + elif [ -n "$user_vimrc" ]; then + : + # if no user vimrc, then check for a global /etc/vimpagerrc + elif [ -n "$system_vimpagerrc" -a -f "$system_vimpagerrc" ]; then + : + # check a couple of common places for the standalone version + elif [ -f /usr/local/etc/vimpagerrc ]; then + vimrc=/usr/local/etc/vimpagerrc + elif [ -f /etc/vimpagerrc ]; then + vimrc=/etc/vimpagerrc + fi +} + +read_vim_settings() { + # Read settings from the vimpagerrc file and possibly set $use_gvim, $ansiesc_available, $disable_x11 and $no_pass_thru. + i=1 + OLDIFS=$IFS + IFS=' +' + for var in $(IFS=$OLDIFS; "$tvim" -NEnR ${vimrc:+-u "$vimrc"} -i NONE --cmd 'let g:vimpager = { "enabled": 1 }' +' + if !exists("g:vimpager.gvim") + if !exists("g:vimpager_use_gvim") + let g:vimpager.gvim = 0 + else + let g:vimpager.gvim = g:vimpager_use_gvim + endif + endif + + if !exists("g:vimpager.X11") + if !exists("g:vimpager_disable_x11") + let g:vimpager.X11 = 1 + else + let g:vimpager.X11 = !g:vimpager_disable_x11 + endif + endif + + if !exists("g:vimpager.passthrough") + if !exists("g:vimpager_passthrough") + let g:vimpager.passthrough = 1 + else + let g:vimpager.passthrough = g:vimpager_passthrough + endif + endif + + let g:use_ansiesc = 0 + + if has("conceal") && (!exists("g:vimpager.ansiesc") || g:vimpager.ansiesc == 1) && (!exists("g:vimpager_disable_ansiesc") || g:vimpager_disable_ansiesc == 0) + let g:use_ansiesc = 1 + endif + + call writefile([""] + map([g:vimpager.gvim, g:vimpager.X11, g:vimpager.passthrough, g:use_ansiesc], "\"VAL:\".v:val"), "/dev/stderr") + quit + ' &1 >/dev/null); do + case "$var" in + VAL:*) + case $i in + 1) + [ "${var#VAL:}" -eq 1 ] && use_gvim=1 + ;; + 2) + [ "${var#VAL:}" -eq 0 ] && disable_x11=1 + ;; + 3) + [ "${var#VAL:}" -eq 0 ] && no_pass_thru=1 + ;; + 4) + [ "${var#VAL:}" -ne 0 ] && ansiesc_available=1 + break + ;; + esac + i=$((i + 1)) + ;; + esac + done + IFS=$OLDIFS +} + +find_vim_executable() { + # Find the vim executable to use. Set $vim_cmd and $gui. + if [ -n "$win32" ]; then + # msys/cygwin may be using a native vim, and if we're not in a real + # console the native vim will not work, so we have to use gvim. + + if [ "x$TERM" != "xdumb" -a "x$TERM" != "xcygwin" -a "x$TERM" != "x" ]; then + if command -v vim >/dev/null | grep -Eq '^/(cygdrive/)?[a-z]/'; then + use_gvim=1 + fi + fi + fi + + tvim=vim + + if [ -n "$EDITOR" -a -z "$VIMPAGER_VIM" ]; then + case "${EDITOR##*/}" in + *vim*) + export VIMPAGER_VIM=$EDITOR + ;; + esac + fi + + if [ -n "$VIMPAGER_VIM" ]; then + case "${VIMPAGER_VIM##*/}" in + vim*) + tvim=$VIMPAGER_VIM + ;; + nvim*) + tvim=$VIMPAGER_VIM + ;; + gvim*|mvim*) + use_gvim=1 + gvim=$VIMPAGER_VIM + ;; + esac + fi + + if [ -n "$use_gvim" ]; then + # determine if this is an ssh session and/or $DISPLAY is set + if [ -n "$osx" ]; then + if [ -z "$SSH_CONNECTION" ] && command -v mvim >/dev/null; then + vim_cmd=${gvim:-mvim} + gui=1 + else + vim_cmd=$tvim + fi + elif [ -n "$cygwin" ]; then + if command -v gvim >/dev/null; then + if [ -n "$SSH_CONNECTION" ]; then + vim_cmd=$tvim + # The Cygwin gvim uses X + elif win32_native gvim; then + if [ -z "$DISPLAY" ]; then + vim_cmd=$tvim + else + vim_cmd=${gvim:-gvim} + gui=1 + fi + else + vim_cmd=${gvim:-gvim} + gui=1 + fi + else + vim_cmd=$tvim + fi + elif [ -n "$msys" ]; then + if [ -z "$SSH_CONNECTION" ] && command -v gvim >/dev/null; then + vim_cmd=${gvim:-gvim} + gui=1 + else + vim_cmd=$tvim + fi + elif [ -z "$DISPLAY" ]; then + vim_cmd=$tvim + else + if command -v gvim >/dev/null; then + vim_cmd=${gvim:-gvim} + gui=1 + else + vim_cmd=$tvim + fi + fi + else + vim_cmd=${vim_cmd:-${tvim:-vim}} + fi + + if [ ! -n "$gui" -a -n "$disable_x11" ]; then + vim_cmd="$vim_cmd -X" + fi +} + +parse_pstree() { + # Parse the process tree and set $is_man, $is_doc, $is_perldoc, $force_strip_ansi and $extra_cmd. + ptree=$(do_ptree) + + # Check if called from man, perldoc or pydoc + if echo "$ptree" | grep -Eq '([ \t]+|/)(man|[Pp]y(thon|doc)[0-9.]*|[Rr](uby|i)[0-9.]*)([ \t]|$)'; then + is_man=1 + is_doc=1 + force_strip_ansi=1 + elif echo "$ptree" | grep -Eq '([ \t]+|/)perl(doc)?([0-9.]*)?([ \t]|$)'; then + is_perldoc=1 + is_doc=1 + force_strip_ansi=1 + fi + + extra_cmd="${extra_cmd:+$extra_cmd | }let g:vimpager.ptree=[$(echo "$ptree" | awk '{ print "\"" $2 "\"" }' | tr '\n' ',')] | call remove(g:vimpager.ptree, -1) | let g:vimpager_ptree = g:vimpager.ptree" +} + +expand_config_vars() { + eval runtime=\"$runtime\" + eval vimcat=\"$vimcat\" + eval system_vimpagerrc=\"$system_vimpagerrc\" +} + +quit() { + rm -f gvim.exe.stackdump # for a cygwin bug + cd "${tmp%/*}" 2>/dev/null # some systems cannot remove CWD + rm -rf "$tmp" 2>/dev/null # rm -rf "" shows error on OpenBSD + exit "${1:-0}" +} + +usage() { + cat <<'EOF' +Usage: vimpager [OPTION]... [FILE | -]... +Display FILE(s) in (n)vim with a pager emulating less. + +With no FILE, or when FILE is -, read standard input. + + -h, --help, --usage Show this help screen and exit. + -v, --version Show version information and exit. + +G, + Go to the end of the file. + -N, --LINE-NUMBERS Show line numbers. + -s Squeeze multiple blank lines into one. + --cmd COMMAND Run (n)vim COMMAND before initialization. + -c COMMAND Run (n)vim COMMAND after initialization. + -u FILE Use FILE as the vimrc. + -x  Give debugging output on stderr. + +Examples: + vimpager program.py # view program.py in the pager + PAGER=vimpager man 3 sprintf # view man page for sprintf(3) + +Project homepage and documentation: <http://github.com/rkitover/vimpager> +or available locally via: man vimpager +Press ',h' for a summary of keystrokes in the program. +EOF +} + +awk() { + command "$_awk" "$@" +} + +sed() { + command "$_sed" "$@" +} + +grep() { + case "$1" in + -Eq) + # we only check for -Eq when it's the only option + case "$2" in + -*) + command "$_grep" "$@" + ;; + *) + if [ "${_have_grep_E_q:-0}" -eq 1 ]; then + command "$_grep" "$@" + else + shift + awk_grep_E_q "$@" + fi + ;; + esac + ;; + *) + command "$_grep" "$@" + ;; + esac +} + +awk_grep_E_q() { + _pat=$(printf '%s' "$1" | sed -e 's!/!\\/!g') + shift + awk ' + BEGIN { exit_val = 1 } + /'"$_pat"'/ { exit_val = 0; exit(exit_val) } + END { exit(exit_val) } + ' "$@" +} + +head() { + _lines= + case "$1" in + -[0-9]*) + _lines=${1#-} + shift + esac + + if [ -z "$_lines" ]; then + command "$_head" "$@" + elif [ "$_head_syntax" = "new" ]; then + command "$_head" -n $_lines -- "$@" + elif [ -z "$_head_no_double_dash" ]; then + command "$_head" -$_lines -- "$@" + else + command "$_head" -$_lines "$@" + fi +} + +# We are escaping only slashes (because they are special in file paths) and +# percent (because it is our escape char). This makes it possible to encode +# the path to the original file in the basename of the temporary file. +encode_filename() { + echo "$@" | sed -e 's|%|%25|g' -e 's|/|%2F|g' +} + +# emulate arrays +set_key() { + eval "$1_$2=\"$3\"" +} + +get_key() { + eval "echo \"\${$1_$2}\"" +} + +# this actually runs vim or gvim, or vimcat, or just cats the file +page_files() { + # EXTRACT BUNDLED SCRIPTS HERE + + if [ -n "$cat_files" ]; then + i=1 + for cur_file in "$@"; do + orig_file=$(get_key orig_file_names $i) + + if [ $# -gt 1 ]; then + if [ $i -gt 1 ]; then + printf '\n' + fi + printf '==> %s <==\n\n' "$orig_file" + fi + + if [ -n "$(get_key ansi_files $i)" ]; then + cat "$cur_file" + _exit_status=$? + else + "$POSIX_SHELL" ${trace:+-x} "$vimcat" ${vimrc:+-u "$vimrc"} \ + --cmd "set rtp^=$runtime | let vimpager={ 'enabled': 1 }" \ + ${extra_cmd:+--cmd "$extra_cmd"} \ + -c 'silent! source '"$tmp/$i"'.vim' \ + ${extra_c:+-c "$extra_c"} \ + "$cur_file" /dev/null +} + +win32_native() { + if [ "x$(get_key _win32_native "$1")" = x1 ]; then + return 0 + else + if [ -n "$msys" -o -n "$cygwin" ]; then + if command -v "$1" > /dev/null | grep -Eq '^/(cygdrive/)?[a-z]/'; then + set_key _win32_native "$1" 1 + return 0 + else + set_key _win32_native "$1" 0 + return 1 + fi + else + set_key _win32_native "$1" 0 + return 1 + fi + fi + return 1 +} + +ansi_filter() { + sed -e 's/'"$ANSI_RE"'//g' "$@" +} + +# Even /bin/sed on Solaris handles UTF-8 characters correctly, so we can safely +# use sed for this. +overstrike_filter() { + sed 's/.//g' "$@" +} + +fits_on_screen() { + [ $# -eq 0 ] && set -- - + + # First remove overstrikes and ANSI codes with sed + ansi_filter "$@" | overstrike_filter | \ + awk ' + { + if (NR == 1) { + lines = total_lines - 2 - (num_files - 1) * file_sep_lines + + if (num_files - 1) + lines -= first_file_sep_lines + + total_cols += 0 # coerce to number + } + + col = 0 + + for (pos = 1; pos <= length($0); pos++) { + c = substr($0, pos, 1) + + # handle tabs + if (c == "\t") + col += 8 - (col % 8) + else + col++ + + if (col > total_cols) { + if (!--lines) exit(1) + col = 1 + } + } + + if (!--lines) exit(1) + } + ' num_files=$# total_lines=$lines total_cols=$cols file_sep_lines=3 first_file_sep_lines=2 - +} + +select_awk_executable() { + if command -v gawk >/dev/null; then + _awk=gawk + elif command -v nawk >/dev/null; then + _awk=nawk + elif command -v mawk >/dev/null; then + _awk=mawk + elif [ -x /usr/xpg4/bin/awk ]; then + _awk=/usr/xpg4/bin/awk + elif command -v awk >/dev/null; then + _awk=awk + else + echo "ERROR: No awk found!" >&2 + quit 1 + fi +} + +select_sed_executable() { + if command -v gsed >/dev/null; then + _sed=gsed + elif [ -x /usr/xpg4/bin/sed ]; then + _sed=/usr/xpg4/bin/sed + elif command -v sed >/dev/null; then + _sed=sed + else + echo "ERROR: No sed found!" >&2 + quit 1 + fi +} + +select_grep_executable() { + if command -v ggrep >/dev/null; then + _grep=ggrep + elif [ -x /usr/xpg4/bin/grep ]; then + _grep=/usr/xpg4/bin/grep + elif command -v grep >/dev/null; then + _grep=grep + else + echo "ERROR: No grep found!" >&2 + quit 1 + fi +} + +check_for_grep_q() { + # check that grep -Eq works + if [ -z "$(echo foo | "$_grep" -Eq foo >/dev/null 2>&1)" -a $? -eq 0 ]; then + _have_grep_E_q=1 + fi +} + +select_head_executable() { + if command -v ghead >/dev/null; then + _head=ghead + else + _head=head + fi +} + +select_head_syntax() { + if [ "$(echo xx | head -n 1 2>/dev/null)" = "xx" ]; then + _head_syntax=new + else + if ! head -1 -- "$0" >/dev/null 2>&1; then + _head_no_double_dash=1 + fi + fi +} + +# vim: sw=4 et tw=0: diff --git a/test/common-functions.bats b/test/common-functions.bats new file mode 100644 index 0000000..c2b0819 --- /dev/null +++ b/test/common-functions.bats @@ -0,0 +1,51 @@ +#!/usr/bin/env bats + +load helpers + +. "$src/inc/common_functions.sh" + +@test "squeeze_blank_lines" { + tmp_function () { + ( + echo The first line is normal + echo The second as well + echo # an empty line + echo Another text line + echo # empty line + echo # empty line + echo third line with text + echo more text + echo ' ' # some blanks + echo ' ' # some tabs + echo last line + ) | squeeze_blank_lines + } + run tmp_function + status_ok + [[ "$(wc -l <<<"$output")" -eq 9 ]] + [[ "${#lines[@]}" -eq 7 ]] + [[ "${lines[0]}" = 'The first line is normal' ]] + [[ "${lines[1]}" = 'The second as well' ]] + [[ "${lines[2]}" = 'Another text line' ]] + [[ "${lines[3]}" = 'third line with text' ]] + [[ "${lines[4]}" = 'more text' ]] + [[ "${lines[5]}" = ' ' ]] + [[ "${lines[6]}" = 'last line' ]] +} + +@test 'find_tmp_directory sets $tmp' { + [[ -z "$tmp" ]] + find_tmp_directory + [[ "$tmp" = /* ]] +} + +@test "set_system_vars sets at least one system variable" { + [[ -z "$linux" && -z "$solaris" && -z "$osx" && -z "$bsd" && \ + -z "$cygwin" && -z "$win32" && -z "$msys" && -z "$openbsd" && \ + -z "$freebsd" && -z "$netbsd" ]] + set_system_vars + [[ "$linux" -eq 1 || "$solaris" -eq 1 || "$osx" -eq 1 || "$bsd" -eq 1 || \ + "$cygwin" -eq 1 || "$win32" -eq 1 || "$msys" -eq 1 || \ + "$openbsd" -eq 1 || "$freebsd" -eq 1 || "$netbsd" -eq 1 ]] +} +# vim: filetype=sh et sw=4 sts=4 : diff --git a/test/helpers.bash b/test/helpers.bash index c7d05c5..dcb039b 100644 --- a/test/helpers.bash +++ b/test/helpers.bash @@ -1,6 +1,7 @@ #!/usr/bin/env bash fixtures=$BATS_TEST_DIRNAME/fixtures +src=${BATS_TEST_DIRNAME%/*} status_ok() { [ "$status" -eq 0 ] diff --git a/test/vimcat-functions.bats b/test/vimcat-functions.bats new file mode 100644 index 0000000..86040f6 --- /dev/null +++ b/test/vimcat-functions.bats @@ -0,0 +1,46 @@ +#!/usr/bin/env bats + +load helpers + +. "$src/inc/vimcat_functions.sh" + +@test "quit returns arguments as return codes" { + run quit 42 + [[ "$status" -eq 42 ]] + no_output + run quit + status_ok + no_output +} + +@test 'quit cleans up the $tmp direcotry' { + tmp=$BATS_TMPDIR/quit-test + mkdir "$tmp" + touch "$tmp/some-file" + run quit 1 + [[ ! -e "$tmp/some-file" ]] + [[ ! -e "$tmp" ]] +} + +@test 'quit kills $vim_pid, $pipeline_pid and $tail_pid' { + sleep 100000 & + vim_pid=$! + sleep 200000 & + pipeline_pid=$! + sleep 300000 & + tail_pid=$! + run jobs + [[ ${#lines[@]} -eq 3 ]] + run quit 1 + sleep 1 + run jobs + no_output +} + +@test "usage prints the help message" { + run usage + [[ "$output" = "$(tr -d '\015' < "$fixtures/vimcat-help.txt")" ]] + status_ok +} + +# vim: filetype=sh et sw=4 sts=4 : diff --git a/test/vimpager-functions.bats b/test/vimpager-functions.bats new file mode 100644 index 0000000..4aa62e2 --- /dev/null +++ b/test/vimpager-functions.bats @@ -0,0 +1,60 @@ +#!/usr/bin/env bats + +load helpers + +. "$src/inc/vimpager_functions.sh" + +@test 'detect_term_size sets $cols and $lines' { + # detect_term_size will write files to $tmp (assuming it is a direcotry) + tmp=$BATS_TMPDIR + [[ -z "$cols" ]] + [[ -z "$lines" ]] + detect_term_size + [[ "$cols" =~ [0-9]+ ]] + [[ "$lines" =~ [0-9]+ ]] +} + +@test 'read_vim_settings may set $use_gvim, $ansiesc_available, $disable_x11 and $no_pass_thru' { + [[ -z "$use_gvim" ]] + [[ -z "$ansiesc_available" ]] + [[ -z "$disable_x11" ]] + [[ -z "$no_pass_thru" ]] + read_vim_settings + [[ "$use_gvim" -eq 1 || -z "$use_gvim" ]] + [[ "$ansiesc_available" -eq 1 || -z "$ansiesc_available" ]] + [[ "$disable_x11" -eq 1 || -z "$disable_x11" ]] + [[ "$no_pass_thru" -eq 1 || -z "$no_pass_thru" ]] +} + +@test 'find_vim_executable sets $tvim and $vim_cmd' { + [[ -z "$tvim" ]] + [[ -z "$vim_cmd" ]] + find_vim_executable + [[ -n "$tvim" ]] + [[ -n "$vim_cmd" ]] +} + +@test "quit returns arguments as return codes" { + run quit 42 + [[ "$status" -eq 42 ]] + no_output + run quit + status_ok + no_output +} + +@test 'quit cleans up the $tmp direcotry' { + tmp=$BATS_TMPDIR/quit-test + mkdir "$tmp" + touch "$tmp/some-file" + run quit 1 + [[ ! -e "$tmp/some-file" ]] + [[ ! -e "$tmp" ]] +} + +@test "usage prints the help message" { + run usage + [[ "$output" = "$(tr -d '\015' < "$fixtures/vimpager-help.txt")" ]] + status_ok +} +# vim: filetype=sh et sw=4 sts=4 : diff --git a/test/vimpager-output.bats b/test/vimpager-output.bats new file mode 100644 index 0000000..eba830c --- /dev/null +++ b/test/vimpager-output.bats @@ -0,0 +1,12 @@ +#!/usr/bin/env bats + +load helpers + +@test "vimpager cats files if stdout is not a terminal" { + run ./vimpager uganda.txt + status_ok + [[ "${lines[0]}" = '*uganda.txt* For Vim version 7.4. Last change: 2013 Jul 06' ]] + [[ "$(sed -n '10{p;q;}' <<<"$output")" = 'Vim is Charityware. You can use and copy it as much as you like, but you are' ]] +} + +# vim: filetype=sh et sw=4 sts=4 : diff --git a/vimcat b/vimcat index 4d0e42c..6e5a4e8 100755 --- a/vimcat +++ b/vimcat @@ -24,311 +24,17 @@ project_dir=`dirname "$link"` version="$(cd "$project_dir" && git describe 2>/dev/null) (git)" || version="$version_tag (checkout)" runtime=$project_dir -if command -v vim >/dev/null; then - vim=vim -elif command -v nvim >/dev/null; then - vim=nvim -else - echo "$0: neither vim nor nvim found, vim or nvim is required for vimcat" >&2 - exit 1 -fi - -quit() { - ( - kill "$vim_pid" >/dev/null 2>&1 - do_sleep 100 - kill -9 "$vim_pid" >/dev/null 2>&1 - - kill "$pipeline_pid" >/dev/null 2>&1 - do_sleep 100 - kill -9 "$pipeline_pid" >/dev/null 2>&1 - - kill "$tail_pid" >/dev/null 2>&1 - do_sleep 100 - kill -9 "$tail_pid" >/dev/null 2>&1 - - cd "${tmp_dir%/*}" 2>/dev/null # some systems cannot remove CWD - - rm -rf "$tmp_dir" 2>/dev/null # rm -rf "" shows error on OpenBSD - ) & - exit "$@" -} - -do_sleep() { - _ms=${1:-100} - "$vim" -NEsnR -i NONE -u NONE +"sleep $_ms m" +q >/dev/null 2>&1 -} - -usage() { - cat <<'EOF' -Usage: vimcat [OPTION]... [FILE | -]... -Display FILE(s) in the terminal with (n)vim syntax highlighting using ANSI escape codes. - -With no FILE, or when FILE is -, read standard input. - - -h, --help, --usage This help screen. - -v, --version Show version information and exit. - -n Print with line numbers. - -s Squeeze multiple blank lines into one. - -o FILE | - Output ANSI highlighted text to FILE or standard output. - --cmd COMMAND Run (n)vim COMMAND before initialization. - -c COMMAND Run (n)vim COMMAND after initialization. - -u FILE Use FILE as the vimrc. - -x  Give debugging output on stderr. - -Examples: - vimcat program.py # output program.py with highlighting to terminal - -Project homepage: <http://github.com/rkitover/vimpager> -and documentation: <https://github.com/rkitover/vimpager/blob/master/markdown/vimcat.md> -or available locally via: man vimcat -EOF -} - -write_chunks() { - cd "$chunks_dir" - rm -f -- * - split -b 4096 - - touch PIPELINE_DONE -} - -start_pipeline() { - if [ -n "$pipeline" ]; then - pipeline="$pipeline | write_chunks" - else - pipeline=write_chunks - fi - cat -- "$pipeline_start" | (eval "$pipeline" <&3 & echo $! > "$tmp_dir/pipeline_pid") 3<&0 - pipeline_pid=$(cat "$tmp_dir/pipeline_pid") -} - -start_highlight_job() { - # INSERT VIMCAT_DEBUG PREPARATION HERE - set -- -NE -i NONE -n \ - --cmd "set runtimepath^=$runtime" \ - --cmd "call vimcat#Init({ 'rc': '$vimcatrc' })" \ - --cmd visual \ - ${extra_cmd:+--cmd "$extra_cmd"} \ - ${extra_c:+-c "$extra_c"} \ - -c "call vimcat#Run(\"$dest_file\", ${line_numbers:-0}, \"$chunks_dir\", \"$pipeline_start\")" - - [ -n "$vimcatrc" ] && set -- "$@" -u "$vimcatrc" - - if [ "${VIMCAT_DEBUG:-0}" -eq 0 ]; then - ("$vim" "$@" /dev/null 2>&1; touch "$tmp_dir/vim_done") & - vim_pid=$! - else - "$vim" "$@" &2 - exit 1 -fi - -trap 'quit 1' PIPE HUP INT QUIT ILL TRAP KILL BUS TERM -tmp_file_in=$tmp_dir/vimcat_in.txt -out_fifo=$tmp_dir/vimcat_out.fifo - -case $(uname -s) in - SunOS*|CYGWIN*|MINGW*|MSYS*) - # the fifo streaming doesn't work on windows and solaris - touch "$out_fifo" - ;; - *) - mkfifo "$out_fifo" - ;; -esac - -# check for arguments -while [ $# -gt 0 ] ; do - case "$1" in - "-c") - shift - if [ -z "$extra_c" ]; then - extra_c=$1 - else - extra_c="$extra_c | $1" - fi - shift - ;; - "--cmd") - shift - if [ -z "$extra_cmd" ]; then - extra_cmd=$1 - else - extra_cmd="$extra_cmd | $1" - fi - shift - ;; - "-u") - shift - vimcatrc=$1 - shift - ;; - "-o") - shift - output_file=$1 - shift - ;; - "-s") - shift - squeeze_blank_lines=1 - ;; - "-n") - shift - line_numbers=1 - ;; - "-x") - # xtrace should already be set by the first option parsing - shift - ;; - "--") - shift - break - ;; - -) - break - ;; - -*) - echo "$0: bad option '$1', see --help for usage." >&2 - quit 1 - ;; - *) - break - ;; - esac -done - -# Just pass through if not on a tty, unless -o was given -if [ -z "$output_file" ]; then - if [ ! -t 1 ]; then - exec cat "$@" - fi -fi - -if [ -z "$vimcatrc" ]; then - if [ -f ~/.vimcatrc ]; then - vimcatrc=~/.vimcatrc - else - vimcatrc= - fi -fi - -if [ $# -eq 0 ]; then - set -- - -fi - -if [ -n "$output_file" -a $# -gt 1 ]; then - echo "$0: -o can only be used with one input file or stdin." >&2 - quit 1 -fi - -chunks_dir=$tmp_dir/chunks -mkdir "$chunks_dir" - -i=1 -for file in "$@" -do - if [ $# -ge 2 ]; then - if [ $i -gt 1 ]; then - printf '\n' - fi - printf "==> %s <==\n\n" "$file" - fi - - pipeline= - pipeline_start=$file - - if [ "${squeeze_blank_lines:-0}" -eq 1 ]; then - pipeline=squeeze_blank_lines - fi - - exit_code=0 - - # Check that the file is readable - if [ "$file" != - ]; then - if [ ! -r "$file" ]; then - echo "$0: Cannot read file: $file" >&2 - exit_code=1 - fi - - [ ! -s "$file" ] && continue - fi - - if [ -z "$output_file" -o "$output_file" = "-" ]; then - dest_file=$out_fifo - - tail -f "$out_fifo" & - tail_pid=$! - else - dest_file=$output_file - printf '' > "$dest_file" - fi - - start_highlight_job - start_pipeline - while [ ! -f "$tmp_dir/vim_done" ]; do - do_sleep 50 - done - - if [ -n "$tail_pid" ]; then - # if it's not a fifo where this doesn't work, tail needs some time to catch up - [ ! -p "$out_fifo" ] && do_sleep 1100 - - kill $tail_pid >/dev/null 2>&1 - fi - - i=$((i + 1)) -done - -quit $exit_code +. "$project_dir/inc/vimcat_functions.sh" +. "$project_dir/inc/common_functions.sh" + +set_system_vars +select_vim_executable +parse_command_line_options_1 "$@" +find_tmp_directory +create_tmp_directory +install_trap +create_fifo + +main "$@" # vim: sw=4 et ft=sh diff --git a/vimpager b/vimpager index f8331db..6b81871 100755 --- a/vimpager +++ b/vimpager @@ -36,907 +36,20 @@ runtime='$project_dir' vimcat='$project_dir/vimcat' system_vimpagerrc='$project_dir/vimpagerrc' -case "$(uname -s)" in - Linux) linux=1 ;; - SunOS) solaris=1 ;; - Darwin) osx=1; bsd=1 ;; - CYGWIN*) cygwin=1; win32=1 ;; - MINGW*) msys=1; win32=1 ;; - MSYS*) msys=1; win32=1 ;; - OpenBSD) openbsd=1; bsd=1 ;; - FreeBSD) freebsd=1; bsd=1 ;; - NetBSD) netbsd=1; bsd=1 ;; - *) bsd=1 ;; -esac - -main() { - # if no args and no stdin, display usage - if [ $# -eq 0 -a -t 0 ]; then - usage - quit 0 - fi - - # Parse the command line options as early as possible. These variables might be set because of command line options and are thus initialized. - vim_options= - vimrc= - extra_c= - extra_cmd= - line_numbers=0 - - # Check for certain parameters to pass on to vim (or conceivably do something else) - # Couldn't use getopt or getopts as neither supports options prepended with + - while [ $# -gt 0 ] ; do - case "$1" in - -h|--help|-help|--usage|-usage) - usage - quit 0 - ;; - -v|--version|-version) - echo "vimpager $version" - quit 0 - ;; - +G|+) - vim_options="$vim_options +"; - shift - ;; - -N|--LINE-NUMBERS) - line_numbers=1 - shift - ;; - -c) - shift - extra_c="${extra_c:+$extra_c | }$1" - shift - ;; - --cmd) - shift - extra_cmd="${extra_cmd:+$extra_cmd | }$1" - shift - ;; - -u) - shift - vimrc=$1 - shift - ;; - -s) # Ubuntu man passes this option to /usr/bin/pager - shift - squeeze_blank_lines=1 - ;; - --) - shift - break - ;; - -x) - trace=1 - shift - set -x - ;; - -) - break - ;; - -*) - echo "$0: bad option '$1', see --help for usage." >&2 - quit 1 - ;; - *) - break - ;; - esac - done - - find_tmp_directory - - trap "quit 1" PIPE HUP INT QUIT ILL TRAP KILL BUS TERM - - detect_term_size - - expand_config_vars - - find_vim_executable - - find_vimpagerrc_files - - read_vim_settings - - parse_pstree - - # if no args, assume stdin - if [ $# -eq 0 ]; then - set -- - - # turn off man/perldoc support for > 1 arg - elif [ $# -gt 1 ]; then - is_man= - is_perldoc= - is_doc= - force_strip_ansi= - fi - - file_idx=1 - - for file in "$@"; do - if [ "$file" = - ]; then - filename=stdin - else - if [ ! -r "$file" ]; then - echo "$0: cannot read file '$file'" >&2 - quit 1 - fi - - filename=$(resolve_path "$file") - fi - - set_key orig_file_names $file_idx "$filename" - - # $file still holds the orginal file name. $filename will be - # the encoded version of $file. $tempfile is the path under - # $tmp if the file is to be opend from there instead of the - # original location. If $tempfile is empty the file is to be - # opened as $file. - filename=$(encode_filename "$filename") - tempfile= - - case "$(echo "$file" | tr 'A-Z' 'a-z')" in - *.gz) - filename=${filename%.??} - tempfile=$tmp/$filename - gunzip -c -- "$file" > "$tempfile" - - ;; - *.bz2) - filename=${filename%.??2} - tempfile=$tmp/$filename - bunzip2 -c -- "$file" > "$tempfile" - ;; - *.xz) - filename=${filename%.??} - tempfile=$tmp/$filename - xzcat -c -- "$file" > "$tempfile" - ;; - *.z) - filename=${filename%.?} - tempfile=$tmp/$filename - uncompress -c -- "$file" > "$tempfile" - ;; - *) - if [ "$file" = - ]; then - tempfile=$tmp/$filename - cat -- "$file" > "$tempfile" - fi - ;; - esac - - # check for ANSI codes and strip if not using ansiesc - if head -100 "${tempfile:-$file}" | grep -Eq "$ANSI_RE"; then - if [ -z "$ansiesc_available" -o -n "$force_strip_ansi" ]; then - ansi_filter "${tempfile:-$file}" > "$tmp/$filename.work" - tempfile=$tmp/$filename - mv -f -- "$tempfile.work" "$tempfile" - else - echo 'call vimpager_utils#DoAnsiEsc()' >> "$tmp/$file_idx.vim" - set_key ansi_files "$file_idx" yes - fi - fi - - # squeeze blank lines if option was specified, Ubuntu man with /usr/bin/pager does this - if [ "${squeeze_blank_lines:-0}" -eq 1 ]; then - sed -e '/^[ ]*$/{ - N - /^[ ]*\n[ ]*$/D - }' < "${tempfile:-$file}" > "$tmp/$filename.work" - tempfile=$tmp/$filename - mv -f -- "$tempfile.work" "$tempfile" - fi - - # dumb man detection when the pstree heuristic fails - if [ -z "$is_doc" ] && head -12 "${tempfile:-$file}" | grep -Eq '^N(.)?A(.)?M(.)?E(.)?[ \t]*$'; then - is_man=1 - is_doc=1 - fi - - # if it's a man page, remove starting blank lines, or the C syntax highlighting fails - # and write out ft command for vim - if [ -n "$is_doc" ]; then - ansi_filter "${tempfile:-$file}" | overstrike_filter | awk ' - BEGIN { skipblank=1 } - /^[ ]*$/ { if (!skipblank) print } - /[^ ]/ { skipblank=0; print } - ' > "$tmp/$filename.work" - tempfile=$tmp/$filename - mv -f -- "$tempfile.work" "$tempfile" - - if [ -n "$is_man" ]; then - echo 'set ft=man' >> "$tmp/$file_idx.vim" - elif [ -n "$is_perldoc" ]; then - echo 'set ft=perldoc' >> "$tmp/$file_idx.vim" - fi - fi - - # if file is zero length, or one blank line (cygwin), and is only arg, exit - if [ ! -s "${tempfile:-$file}" \ - -a $# -eq 1 \ - -o "$(cat "${tempfile:-$file}")" = "" \ - -a "$(wc -l < "${tempfile:-$file}")" -eq 1 ]; then - - quit 0 - fi - - set_key files $file_idx "$(resolve_path "${tempfile:-$file}")" - - file_idx=$((file_idx + 1)) - done - - file_count=$# - - set -- - i=1 - while [ $i -le $file_count ]; do - set -- "$@" "$(get_key files $i)" - i=$((i + 1)) - done - - if [ "${no_pass_thru:-0}" -ne 1 ] && fits_on_screen "$@"; then - cat_files=1 - fi - - page_files "$@" - - quit $? -} - -find_tmp_directory() { - # Find and create the temporary directory used by vimpager. Set the $tmp variable. - mkdir_options="-m 700" - - if [ -n "$win32" ]; then - # Use the real TEMP directory on windows in case we are - # using a native vim/gvim - # TEMP can be /tmp sometimes too - - tmp=$(resolve_path "$TEMP") - - # chmod doesn't work here, even in /tmp sometimes - mkdir_options= - else - # ... and /tmp otherwise - tmp=/tmp - fi - - # Create a safe directory in which we place all other tempfiles. - tmp=$tmp/vimpager_$$ - if ! mkdir $mkdir_options "$tmp"; then - echo "ERROR: Could not create temporary directory $tmp" >&2 - quit 1 - fi -} - -detect_term_size() { - # Detect the terminal size and set the variables $cols and $lines. If necessary $no_pass_thru is set. - if command -v tput >/dev/null; then - # this is the only way it works on some versions of Cygwin - - # 2>/dev/null makes tput not work, so don't do that - # we are just going to hope that tput errors don't happen - tput cols > "$tmp/cols" - tput lines > "$tmp/lines" - - cols=$(cat "$tmp/cols") - lines=$(cat "$tmp/lines") - - rm -f -- "$tmp/cols" "$tmp/lines" - fi - - # msys has no tput, this doesn't work on Cygwin by the way - if [ -z "$cols" ] && command -v bash >/dev/null; then - cols=$(bash -i -c 'echo $COLUMNS') - lines=$(bash -i -c 'echo $LINES') - fi - - # If we are unable to detect lines/columns, maximize - # the window. - if [ -z "$cols" ]; then - cols=999 - lines=999 - no_pass_thru=1 # force loading vimpager - fi -} - -find_vimpagerrc_files() { - # This function will find the system and the user vimpagerrc file and set the variables $system_vimrc and $vimrc. - - # determine location of rc file - i=1 - OLDIFS=$IFS - IFS=' -' - for var in $(IFS=$OLDIFS; "$tvim" -NEnR -i NONE ${vimrc:+-u "$vimrc"} +'call writefile(["", "VAL:" . $VIM, "VAL:" . $MYVIMRC], "/dev/stderr")' +q &1 >/dev/null); do - case "$var" in - VAL:*) - case $i in - 1) - vim_dir=${var#VAL:} - ;; - 2) - user_vimrc=${var#VAL:} - user_vimrc_dir=${user_vimrc%/*} - break - ;; - esac - i=$((i + 1)) - ;; - esac - done - IFS=$OLDIFS - - # find system vimrc - system_vimrc=$("$tvim" --version | sed -n '/system vimrc file: "/{ - s|\$VIM|'"$vim_dir"'| - s/.*: "\([^"]*\).*/\1/p - q - }') - - # find the users vimpagerrc - if [ -n "$vimrc" ]; then - # The vimrc file was given on the command line. - : - elif [ -n "$VIMPAGER_RC" ]; then - vimrc=$VIMPAGER_RC - # check for vimpagerrc in same dir as vimrc in case it is set in VIMINIT - elif [ -n "$user_vimrc_dir" -a -r "$user_vimrc_dir/.vimpagerrc" ]; then - vimrc=$user_vimrc_dir/.vimpagerrc - elif [ -n "$user_vimrc_dir" -a -r "$user_vimrc_dir/_vimpagerrc" ]; then - vimrc=$user_vimrc_dir/_vimpagerrc - elif [ -n "$user_vimrc_dir" -a -r "$user_vimrc_dir/vimpagerrc" ]; then - vimrc=$user_vimrc_dir/vimpagerrc - # check standard paths, according to :h initialization - elif [ -r ~/.vimpagerrc ]; then - vimrc=~/.vimpagerrc - elif [ -r ~/.vim/vimpagerrc ]; then - vimrc=~/.vim/vimpagerrc - elif [ -r ~/_vimpagerrc ]; then - vimrc=~/_vimpagerrc - elif [ -r ~/vimfiles/vimpagerrc ]; then - vimrc=~/vimfiles/vimpagerrc - elif [ -r "$vim_dir/_vimpagerrc" ]; then - vimrc=$vim_dir/_vimpagerrc - # try the user's ~/.vimrc - elif [ -n "$user_vimrc" ]; then - : - # if no user vimrc, then check for a global /etc/vimpagerrc - elif [ -n "$system_vimpagerrc" -a -f "$system_vimpagerrc" ]; then - : - # check a couple of common places for the standalone version - elif [ -f /usr/local/etc/vimpagerrc ]; then - vimrc=/usr/local/etc/vimpagerrc - elif [ -f /etc/vimpagerrc ]; then - vimrc=/etc/vimpagerrc - fi -} - -read_vim_settings() { - # Read settings from the vimpagerrc file and possibly set $use_gvim, $ansiesc_available, $disable_x11 and $no_pass_thru. - i=1 - OLDIFS=$IFS - IFS=' -' - for var in $(IFS=$OLDIFS; "$tvim" -NEnR ${vimrc:+-u "$vimrc"} -i NONE --cmd 'let g:vimpager = { "enabled": 1 }' +' - if !exists("g:vimpager.gvim") - if !exists("g:vimpager_use_gvim") - let g:vimpager.gvim = 0 - else - let g:vimpager.gvim = g:vimpager_use_gvim - endif - endif - - if !exists("g:vimpager.X11") - if !exists("g:vimpager_disable_x11") - let g:vimpager.X11 = 1 - else - let g:vimpager.X11 = !g:vimpager_disable_x11 - endif - endif - - if !exists("g:vimpager.passthrough") - if !exists("g:vimpager_passthrough") - let g:vimpager.passthrough = 1 - else - let g:vimpager.passthrough = g:vimpager_passthrough - endif - endif - - let g:use_ansiesc = 0 - - if has("conceal") && (!exists("g:vimpager.ansiesc") || g:vimpager.ansiesc == 1) && (!exists("g:vimpager_disable_ansiesc") || g:vimpager_disable_ansiesc == 0) - let g:use_ansiesc = 1 - endif - - call writefile([""] + map([g:vimpager.gvim, g:vimpager.X11, g:vimpager.passthrough, g:use_ansiesc], "\"VAL:\".v:val"), "/dev/stderr") - quit - ' &1 >/dev/null); do - case "$var" in - VAL:*) - case $i in - 1) - [ "${var#VAL:}" -eq 1 ] && use_gvim=1 - ;; - 2) - [ "${var#VAL:}" -eq 0 ] && disable_x11=1 - ;; - 3) - [ "${var#VAL:}" -eq 0 ] && no_pass_thru=1 - ;; - 4) - [ "${var#VAL:}" -ne 0 ] && ansiesc_available=1 - break - ;; - esac - i=$((i + 1)) - ;; - esac - done - IFS=$OLDIFS -} - -find_vim_executable() { - # Find the vim executable to use. Set $vim_cmd and $gui. - if [ -n "$win32" ]; then - # msys/cygwin may be using a native vim, and if we're not in a real - # console the native vim will not work, so we have to use gvim. - - if [ "x$TERM" != "xdumb" -a "x$TERM" != "xcygwin" -a "x$TERM" != "x" ]; then - if command -v vim >/dev/null | grep -Eq '^/(cygdrive/)?[a-z]/'; then - use_gvim=1 - fi - fi - fi - - tvim=vim - - if [ -n "$EDITOR" -a -z "$VIMPAGER_VIM" ]; then - case "${EDITOR##*/}" in - *vim*) - export VIMPAGER_VIM=$EDITOR - ;; - esac - fi - - if [ -n "$VIMPAGER_VIM" ]; then - case "${VIMPAGER_VIM##*/}" in - vim*) - tvim=$VIMPAGER_VIM - ;; - nvim*) - tvim=$VIMPAGER_VIM - ;; - gvim*|mvim*) - use_gvim=1 - gvim=$VIMPAGER_VIM - ;; - esac - fi - - if [ -n "$use_gvim" ]; then - # determine if this is an ssh session and/or $DISPLAY is set - if [ -n "$osx" ]; then - if [ -z "$SSH_CONNECTION" ] && command -v mvim >/dev/null; then - vim_cmd=${gvim:-mvim} - gui=1 - else - vim_cmd=$tvim - fi - elif [ -n "$cygwin" ]; then - if command -v gvim >/dev/null; then - if [ -n "$SSH_CONNECTION" ]; then - vim_cmd=$tvim - # The Cygwin gvim uses X - elif win32_native gvim; then - if [ -z "$DISPLAY" ]; then - vim_cmd=$tvim - else - vim_cmd=${gvim:-gvim} - gui=1 - fi - else - vim_cmd=${gvim:-gvim} - gui=1 - fi - else - vim_cmd=$tvim - fi - elif [ -n "$msys" ]; then - if [ -z "$SSH_CONNECTION" ] && command -v gvim >/dev/null; then - vim_cmd=${gvim:-gvim} - gui=1 - else - vim_cmd=$tvim - fi - elif [ -z "$DISPLAY" ]; then - vim_cmd=$tvim - else - if command -v gvim >/dev/null; then - vim_cmd=${gvim:-gvim} - gui=1 - else - vim_cmd=$tvim - fi - fi - else - vim_cmd=${vim_cmd:-${tvim:-vim}} - fi - - if [ ! -n "$gui" -a -n "$disable_x11" ]; then - vim_cmd="$vim_cmd -X" - fi -} - -parse_pstree() { - # Parse the process tree and set $is_man, $is_doc, $is_perldoc, $force_strip_ansi and $extra_cmd. - ptree=$(do_ptree) - - # Check if called from man, perldoc or pydoc - if echo "$ptree" | grep -Eq '([ \t]+|/)(man|[Pp]y(thon|doc)[0-9.]*|[Rr](uby|i)[0-9.]*)([ \t]|$)'; then - is_man=1 - is_doc=1 - force_strip_ansi=1 - elif echo "$ptree" | grep -Eq '([ \t]+|/)perl(doc)?([0-9.]*)?([ \t]|$)'; then - is_perldoc=1 - is_doc=1 - force_strip_ansi=1 - fi - - extra_cmd="${extra_cmd:+$extra_cmd | }let g:vimpager.ptree=[$(echo "$ptree" | awk '{ print "\"" $2 "\"" }' | tr '\n' ',')] | call remove(g:vimpager.ptree, -1) | let g:vimpager_ptree = g:vimpager.ptree" -} - -expand_config_vars() { - eval runtime=\"$runtime\" - eval vimcat=\"$vimcat\" - eval system_vimpagerrc=\"$system_vimpagerrc\" -} - -# special handling to rewrite cygwin/msys paths to windows POSIX paths -if [ -n "$win32" ] && command -v cygpath >/dev/null; then - _have_cygpath=1 -fi - -resolve_path() { - if [ -n "$_have_cygpath" ]; then - cygpath -w "$1" | tr '\\' '/' - else - echo "$1" - fi -} - -quit() { - rm -f gvim.exe.stackdump # for a cygwin bug - cd "${tmp%/*}" 2>/dev/null # some systems cannot remove CWD - rm -rf "$tmp" 2>/dev/null # rm -rf "" shows error on OpenBSD - exit "$@" -} - -usage() { - cat <<'EOF' -Usage: vimpager [OPTION]... [FILE | -]... -Display FILE(s) in (n)vim with a pager emulating less. - -With no FILE, or when FILE is -, read standard input. - - -h, --help, --usage Show this help screen and exit. - -v, --version Show version information and exit. - +G, + Go to the end of the file. - -N, --LINE-NUMBERS Show line numbers. - -s Squeeze multiple blank lines into one. - --cmd COMMAND Run (n)vim COMMAND before initialization. - -c COMMAND Run (n)vim COMMAND after initialization. - -u FILE Use FILE as the vimrc. - -x  Give debugging output on stderr. - -Examples: - vimpager program.py # view program.py in the pager - PAGER=vimpager man 3 sprintf # view man page for sprintf(3) - -Project homepage and documentation: <http://github.com/rkitover/vimpager> -or available locally via: man vimpager -Press ',h' for a summary of keystrokes in the program. -EOF -} - -if command -v gawk >/dev/null; then - _awk=gawk -elif command -v nawk >/dev/null; then - _awk=nawk -elif command -v mawk >/dev/null; then - _awk=mawk -elif [ -x /usr/xpg4/bin/awk ]; then - _awk=/usr/xpg4/bin/awk -elif command -v awk >/dev/null; then - _awk=awk -else - echo "ERROR: No awk found!" >&2 - quit 1 -fi - -awk() { - command "$_awk" "$@" -} - -if command -v gsed >/dev/null; then - _sed=gsed -elif [ -x /usr/xpg4/bin/sed ]; then - _sed=/usr/xpg4/bin/sed -elif command -v sed >/dev/null; then - _sed=sed -else - echo "ERROR: No sed found!" >&2 - quit 1 -fi - -sed() { - command "$_sed" "$@" -} - - -if command -v ggrep >/dev/null; then - _grep=ggrep -elif [ -x /usr/xpg4/bin/grep ]; then - _grep=/usr/xpg4/bin/grep -elif command -v grep >/dev/null; then - _grep=grep -else - echo "ERROR: No grep found!" >&2 - quit 1 -fi - -# check that grep -Eq works -if [ -z "$(echo foo | "$_grep" -Eq foo >/dev/null 2>&1)" -a $? -eq 0 ]; then - _have_grep_E_q=1 -fi - -grep() { - case "$1" in - -Eq) - # we only check for -Eq when it's the only option - case "$2" in - -*) - command "$_grep" "$@" - ;; - *) - if [ "${_have_grep_E_q:-0}" -eq 1 ]; then - command "$_grep" "$@" - else - shift - awk_grep_E_q "$@" - fi - ;; - esac - ;; - *) - command "$_grep" "$@" - ;; - esac -} - -awk_grep_E_q() { - _pat=$(printf '%s' "$1" | sed -e 's!/!\\/!g') - shift - awk ' - BEGIN { exit_val = 1 } - /'"$_pat"'/ { exit_val = 0; exit(exit_val) } - END { exit(exit_val) } - ' "$@" -} - -if command -v ghead >/dev/null; then - _head=ghead -else - _head=head -fi - -if [ "$(echo xx | head -n 1 2>/dev/null)" = "xx" ]; then - _head_syntax=new -else - if ! head -1 -- "$0" >/dev/null 2>&1; then - _head_no_double_dash=1 - fi -fi - -head() { - _lines= - case "$1" in - -[0-9]*) - _lines=${1#-} - shift - esac - - if [ -z "$_lines" ]; then - command "$_head" "$@" - elif [ "$_head_syntax" = "new" ]; then - command "$_head" -n $_lines -- "$@" - elif [ -z "$_head_no_double_dash" ]; then - command "$_head" -$_lines -- "$@" - else - command "$_head" -$_lines "$@" - fi -} - -# We are escaping only slashes (because they are special in file paths) and -# percent (because it is our escape char). This makes it possible to encode -# the path to the original file in the basename of the temporary file. -encode_filename() { - echo "$@" | sed -e 's|%|%25|g' -e 's|/|%2F|g' -} - -# emulate arrays -set_key() { - eval "$1_$2=\"$3\"" -} - -get_key() { - eval "echo \"\${$1_$2}\"" -} - -# this actually runs vim or gvim, or vimcat, or just cats the file -page_files() { - # EXTRACT BUNDLED SCRIPTS HERE - - if [ -n "$cat_files" ]; then - i=1 - for cur_file in "$@"; do - orig_file=$(get_key orig_file_names $i) - - if [ $# -gt 1 ]; then - if [ $i -gt 1 ]; then - printf '\n' - fi - printf '==> %s <==\n\n' "$orig_file" - fi - - if [ -n "$(get_key ansi_files $i)" ]; then - cat "$cur_file" - _exit_status=$? - else - "$POSIX_SHELL" ${trace:+-x} "$vimcat" ${vimrc:+-u "$vimrc"} \ - --cmd "set rtp^=$runtime | let vimpager={ 'enabled': 1 }" \ - ${extra_cmd:+--cmd "$extra_cmd"} \ - -c 'silent! source '"$tmp/$i"'.vim' \ - ${extra_c:+-c "$extra_c"} \ - "$cur_file" /dev/null -} - -win32_native() { - if [ "x$(get_key _win32_native "$1")" = x1 ]; then - return 0 - else - if [ -n "$msys" -o -n "$cygwin" ]; then - if command -v "$1" > /dev/null | grep -Eq '^/(cygdrive/)?[a-z]/'; then - set_key _win32_native "$1" 1 - return 0 - else - set_key _win32_native "$1" 0 - return 1 - fi - else - set_key _win32_native "$1" 0 - return 1 - fi - fi - return 1 -} - -# this is compatible with osed -ANSI_RE='\[[;?]*[0-9.;]*[A-Za-z]' - -ansi_filter() { - sed -e 's/'"$ANSI_RE"'//g' "$@" -} - -# Even /bin/sed on Solaris handles UTF-8 characters correctly, so we can safely -# use sed for this. -overstrike_filter() { - sed 's/.//g' "$@" -} - -fits_on_screen() { - [ $# -eq 0 ] && set -- - - - # First remove overstrikes and ANSI codes with sed - ansi_filter "$@" | overstrike_filter | \ - awk ' - { - if (NR == 1) { - lines = total_lines - 2 - (num_files - 1) * file_sep_lines - - if (num_files - 1) - lines -= first_file_sep_lines - - total_cols += 0 # coerce to number - } - - col = 0 - - for (pos = 1; pos <= length($0); pos++) { - c = substr($0, pos, 1) - - # handle tabs - if (c == "\t") - col += 8 - (col % 8) - else - col++ - - if (col > total_cols) { - if (!--lines) exit(1) - col = 1 - } - } - - if (!--lines) exit(1) - } - ' num_files=$# total_lines=$lines total_cols=$cols file_sep_lines=3 first_file_sep_lines=2 - -} - +. "$project_dir/inc/vimpager_functions.sh" +. "$project_dir/inc/common_functions.sh" # INCLUDE BUNDLED SCRIPTS HERE # END OF BUNDLED SCRIPTS +set_system_vars +check_for_cygpath +select_awk_executable +select_sed_executable +select_grep_executable +check_for_grep_q +select_head_executable +select_head_syntax + main "$@" # Copyright (c) 2016, Rafael Kitover and