#!/bin/zsh -f ############################################################################## # # Original code by Lynn C. Robinson. # Maintainership taken over by Christian J. Robinson # # Copyright 1990(?) - 1999 Lynn C. Robinson # Copyright 1999 - 2007 Christian J. Robinson # # You may copy/use this under the terms of the GNU General Public License, # version 2 or later. # # $Id: vv,v 1.22 2011/07/14 19:57:12 infynity Exp $ # # Changelog: {{{1 # # $Log: vv,v $ # Revision 1.22 2011/07/14 19:57:12 infynity # Add evince to PDF viewer search # # Revision 1.21 2010/10/19 01:35:27 infynity # Handle .xz files # # Revision 1.20 2010/07/07 19:16:59 infynity # - Computation for large files was VERY broken # - Misc minor corrections # # Revision 1.19 2010/05/31 23:41:14 infynity # Add contact information # # Revision 1.18 2008/03/27 02:56:54 infynity # *** empty log message *** # # Revision 1.17 2007/08/31 17:46:02 infynity # rm -r => rm -rf # # Revision 1.16 2007/08/07 07:03:31 infynity # Test for a URI argument # # Revision 1.15 2007/08/03 04:29:47 infynity # *** empty log message *** # # Revision 1.14 2007/08/01 07:23:49 infynity # View files within archives with a handler, if possible # Separated config from help # # Revision 1.13 2007/07/21 06:45:58 infynity # *** empty log message *** # # Revision 1.12 2007/07/21 06:44:34 infynity # .lrp files are .tar.gz files # # Revision 1.11 2007/07/10 16:55:08 infynity # Somehow missed out the .gz and .bz2 non-large file handlers # # Revision 1.10 2007/07/08 11:44:12 infynity # cbz/cbr added # # Revision 1.9 2007/06/22 12:15:12 infynity # *** empty log message *** # # Revision 1.8 2007/06/22 12:14:16 infynity # Allow viewing files within RPMs. # # Revision 1.7 2007/06/04 11:39:08 infynity # *** empty log message *** # # Revision 1.6 2007/06/04 11:35:29 infynity # Allow handling of URIs # # Revision 1.5 2006/07/16 02:55:22 infynity # *** empty log message *** # # Revision 1.4 2006/06/16 18:48:46 infynity # Configurable multi-part extensions. # # Revision 1.3 2006/04/29 07:35:53 infynity # Try to gracefully handle some instances where a viewer isn't available. # # Revision 1.2 2006/04/29 06:56:47 infynity # *** empty log message *** # # Revision 1.1 2006/04/23 03:29:13 infynity # Initial revision # # Apr 22, 2006 - Near total rewrite. # # Apr 20, 2006 - .jar and .xpi files are just zip files. # # Jan 30, 2004 - Add unrar support. # # Oct 15, 2001 - Support for .pdf and .rpm files. # # Jun 30, 2000 - Support for a few anim formats. (mov, avi, qt, mpg) # # May 09, 2000 - Support for .ps files. # - Use the "file" utility to try to determine what to do with # unknown file types. (Experimental.) # # Nov 10, 1999 - Added handling for images. # # May 28, 1999 - Made it possible to view files in tar* archives, instead of # just list them. # # May 14, 1999 - Made filename matching case insensitive. (Actually, I just # converted the filename to all lowercase for the matching.) # # }}}1 # # TODO: # - Use the return value of the file utility a lot more. # - Find a way to use mime types? # ############################################################################## # Initialize: {{{1 # Do this even before sourcing the config file: typeset -A multi_extensions uri_protocols # Do this before anything else so it can't clobber internally needed stuff: . ~/.vv-config 2>/dev/null Version="3.2 BETA July 7, 2010" RCSVersion='$Revision: 1.22 $' # ' RCSVersion=${${=RCSVersion}[-2]} RCSDate='$Date: 2011/07/14 19:57:12 $' # ' RCSDate=${${=RCSDate}[2]} # Set some viewers if they're not set in the config file: : ${PAGER:=less} : ${IMAGE_VIEWER:=$(whence xv || whence qiv || whence eog || whence kview || echo 'no_viewer image')} : ${ANIM_VIEWER:=$(whence mplayer || whence totem || whence kaffeine || echo 'no_viewer video')} : ${HTML_VIEWER:=$(whence w3m || whence links || whence lynx || echo 'no_viewer html')} : ${PS_VIEWER:=$(whence gv || whence ghostview || echo 'no_viewer postscript')} : ${PDF_VIEWER:=$(whence acroread || whence evince || whence gv || whence xpdf || echo 'no_viewer pdf')} # Some files have multiple extensions: typeset -A default_multi_extensions default_uri_protocols default_multi_extensions[tar.*]='tar.%ext%' default_multi_extensions[html.*]='html.%ext%' default_multi_extensions[htm.*]='htm.%ext%' default_uri_protocols[http]='html' default_uri_protocols[https]='html' BASENAME=${0:t} if [[ -z "$HTML_FORCE_HTML_OPT" ]] then case ${=HTML_VIEWER} in *w3m) HTML_FORCE_HTML_OPT='-T text/html' ;; *links) HTML_FORCE_HTML_OPT='-default-mime-type text/html' ;; *lynx) HTML_FORCE_HTML_OPT='-force_html' ;; esac fi RAR=$(whence rar || whence unrar || echo 'no_viewer rar') [[ "$PAGER[-4,-1]" == "less" ]] && LESSFORCEOPT="-f" if [[ ! -z "$TMPDIR" ]] then export TMPPREFIX=$TMPDIR/vv-$$- else export TMPPREFIX=/usr/tmp/vv-$$- fi setopt NULL_GLOB # Suppress glob error message. #ulimit -f 58594 # limit file size to 58594 512 byte blocks ( ~30meg ) TRAPINT() { #echo "\rInterrupt received, cleaning up..." #echo "/bin/rm -v ${TMPPREFIX}*$$" #echo " produced the output:" #/bin/rm -v ${TMPPREFIX}*$$ /bin/rm -rf ${TMPPREFIX}* > /dev/null 2>&1 exit 1 } # Default large file handlers: {{{1 internal_view_zoo_big() # {{{2 { if [[ "X$2" == "X-a" || "`zoo lq $1 | wc -l`" -eq 1 ]] then $PAGER =(zoo epq $1) elif [[ ! -z "$2" ]] then $PAGER =(zoo ep $*) else $PAGER =( zoo V $1 zoo ep $1 \ \*read\* \*READ\* \*Read\* \*doc \*man \*txt) fi } internal_view_zip_big internal_view_jar_big internal_view_xpi_big internal_view_cbz_big() # {{{2 { if [[ "X$2" == "X-a" || "`unzip -qql $1 | wc -l`" -eq 1 ]] then $PAGER =(unzip -c $1) elif [[ ! -z "$2" ]] then $PAGER =(unzip -p $*) else $PAGER =( unzip -v $1 unzip -p $1 \ \*read\* \*READ\* \*Read\* \*doc \*man \*txt ) fi } internal_view_rar_big internal_view_cbr_big() # {{{2 { if [[ "X$2" == "X-a" || "`$RAR lb $1 | wc -l`" -eq 1 ]] then $PAGER =(${=RAR} p $1) elif [[ ! -z "$2" ]] then $PAGER =(${=RAR} -inul p $*) else $PAGER =( ${=RAR} l $1 ${=RAR} -inul p $1 \ \*read\* \*READ\* \*Read\* \*doc \*man \*txt ) fi } internal_view_gz_big() # {{{2 { $PAGER =(zcat $*) } internal_view_bz2_big() # {{{2 { $PAGER =(bzcat $*) } internal_view_xz_big() # {{{2 { $PAGER =(bzcat $*) } # Default file handlers: {{{1 internal_view_zoo() # {{{2 { if [[ "X$2" == "X-a" || "`zoo lq $1 | wc -l`" -eq 1 ]] then zoo epq $1 | $PAGER elif [[ ! -z "$2" ]] then zoo ep $* | $PAGER else ( zoo V $1 zoo ep $1 \ \*read\* \*READ\* \*Read\* \*doc \*man \*txt ) | $PAGER fi } internal_view_zip internal_view_jar internal_view_xpi internal_view_cbz() # {{{2 { if [[ "X$2" == "X-a" || "`unzip -qql $1 | wc -l`" -eq 1 ]] then unzip -c $1 | $PAGER elif [[ ! -z "$2" ]] then unzip -p $* | $PAGER else ( unzip -v $1 unzip -p $1 \ \*read\* \*READ\* \*Read\* \*doc \*man \*txt ) | $PAGER fi } internal_view_rar internal_view_cbr() # {{{2 { if [[ "X$2" == "X-a" || "`$RAR lb $1 | wc -l`" -eq 1 ]] then $RAR p $1 | $PAGER elif [[ ! -z "$2" ]] then $RAR -inul p $* | $PAGER else ( $RAR l $1 $RAR -inul p $1 \ \*read\* \*READ\* \*Read\* \*doc \*man \*txt ) | $PAGER fi } internal_view_gz() # {{{2 { zcat $* | $PAGER } internal_view_bz2() # {{{2 { bzcat $* | $PAGER } internal_view_xz() # {{{2 { xzcat $* | $PAGER } internal_view_tar() # {{{2 { if [[ $# -gt 1 ]] then tar -xOf $* | $PAGER else tar -tvvf $* | $PAGER fi } internal_view_lrp internal_view_tgz internal_view_tar.z internal_view_tar.gz() # {{{2 { if [[ $# -gt 1 ]] then tar -xzOf $* | $PAGER else tar -tzvvf $* | $PAGER fi } internal_view_tar.bz2() # {{{2 { if [[ $# -gt 1 ]] then bunzip2 -c $1 | tar -xO ${@[2,-1]} | $PAGER else bunzip2 -c $* | tar -tvv | $PAGER fi } internal_view_tar.xz() # {{{2 { if [[ $# -gt 1 ]] then unxz -c $1 | tar -xO ${@[2,-1]} | $PAGER else unxz -c $* | tar -tvv | $PAGER fi } internal_view_html.gz internal_view_htm.gz() # {{{2 { ${=HTML_VIEWER} ${=HTML_FORCE_HTML_OPT} =(zcat $*) } internal_view_html.bz2 internal_view_htm.bz2() # {{{2 { ${=HTML_VIEWER} ${=HTML_FORCE_HTML_OPT} =(bzcat $*) } internal_view_html.xz internal_view_htm.xz() # {{{2 { ${=HTML_VIEWER} ${=HTML_FORCE_HTML_OPT} =(xzcat $*) } internal_view_htm internal_view_html() # {{{2 { ${=HTML_VIEWER} ${=HTML_FORCE_HTML_OPT} $* } internal_view_ps() # {{{2 { if [[ ! -z $DISPLAY ]] then ${=PS_VIEWER} $* else echo "This is a postscript file, and you do not appear to have a GUI running." read -q REPLY\?"I can run the file through 'ps2ascii', but the output will look bad. Continue? " if [[ "$REPLY" == 'y' ]] then ps2ascii $* | $PAGER fi fi } internal_view_pdf() # {{{2 { if [[ ! -z $DISPLAY ]] then ${=PDF_VIEWER} $* else echo "This is a pdf file, and you do not appear to have a GUI running." read -q REPLY\?"I can run the file through 'ps2ascii', but the output will look bad. Continue? " if [[ "$REPLY" == 'y' ]] then ps2ascii $* | $PAGER fi fi } # Image viewers: {{{2 internal_view_gif \ internal_view_jpg \ internal_view_jpeg \ internal_view_tif \ internal_view_tiff \ internal_view_bmp \ internal_view_xpm \ internal_view_xbm \ internal_view_ppm \ internal_view_png \ internal_view_tga \ internal_view_pcx() { if [[ ! -z $DISPLAY ]] then ${=IMAGE_VIEWER} $* else if whence zgv > /dev/null then zgv $* else echo "Don't have a way to display images in the current display mode,\nor your DISPLAY variable isn't set." fi fi } # Animation/movie viewers: {{{2 internal_view_wmv \ internal_view_mov \ internal_view_avi \ internal_view_mpg \ internal_view_mpeg \ internal_view_qt \ internal_view_rm() { if [[ ! -z $DISPLAY ]] then ${=ANIM_VIEWER} $* else echo "Don't have a way to display anims in the current display mode,\nor your DISPLAY variable isn't set." fi } internal_view_rpm() # {{{2 { local rpmtmp archive if [[ $# -gt 1 ]] then rpmtmp=$(/bin/mktemp -d ${TMPPREFIX}rpm.XXXXXX) #rpm -i --root $rpmtmp --nodeps --noscripts --nouser --nogroup --nomtime --nomode --nordev $1 archive=$PWD/$1 shift cd $rpmtmp rpm2cpio $archive | cpio -di --quiet #$PAGER ${*/#/$rpmtmp} $PAGER .$* cd - /bin/rm -rf $rpmtmp else rpm -qvlip $* | $PAGER fi } # }}}1 no_viewer() # {{{1 { echo "$BASENAME: ERROR: I can't find an executable to view $1 files." } # }}}1 find_handler() # {{{1 { local extension big_file tmp i handler big_file=0 for i in ${(k)default_uri_protocols} ${(k)uri_protocols} do if [[ $1 == $i://* ]] then extension=${uri_protocols[$i]:-$default_uri_protocols[$i]} fi done if [[ -z "$extension" ]] then [[ ! -z "`echo ${1}*(Lm+8)`" ]] && big_file=1 2>&/dev/null extension=${(L)1:e} for i in ${(k)default_multi_extensions} ${(k)multi_extensions} do if eval \[\[ \$1 \== \*.$i \]\] then tmp=${multi_extensions[$i]:-$default_multi_extensions[$i]} tmp=${tmp:s/%ext%/$extension/} extension=$tmp fi done fi if [[ $big_file == 1 ]] && type view_${extension}_big > /dev/null then handler="view_${extension}_big" elif [[ $big_file == 1 ]] && type internal_view_${extension}_big > /dev/null then handler="internal_view_${extension}_big" elif type view_${extension} > /dev/null then handler="view_${extension}" elif type internal_view_${extension} > /dev/null then handler="internal_view_${extension}" else handler="" fi echo $handler } # }}}1 view() # {{{1 # Arguments: # 1 - The file to view # 2..N - The files within the archive to view # Return value: # 0 - A handler was found or the named file didn't exist # 1 - No handler was found { local i handler local -a handlers if [[ ! -e $1 && $1 != *://* ]] then echo "$BASENAME: $1: No such file or directory" >&2 return 0 fi handler=$(find_handler $1) let i=1 while [[ $((++i)) -le $# ]] do handlers[$i]=$(find_handler $*[$i]) done if [[ ! -z "$handler" ]] then if [[ $# -gt 1 ]] then # A major kluge, and will fail in interesting ways depending on the code # of the handlers, but generally it works: let i=1 while [[ $((++i)) -le $# ]] do tmp==(PAGER=/bin/cat $handler $1 $*[$i] 2>/dev/null) if [[ -s "$tmp" ]] then ${handlers[$i]:=$PAGER} $tmp else ${handlers[$i]:=$PAGER} $*[$i] fi done else $handler $* fi return 0 else return 1 fi } # }}}1 if [[ "$1" == "--version" || "X$1" == "X-v" ]] # {{{1 then echo "$BASENAME version $Version (RCS revision: $RCSVersion $RCSDate)\n" echo "(C) Copyright 1990 - 2010 Lynn C. and Christian J. Robinson " echo "New versions are available here: http://christianrobinson.name/zsh/\n" echo "You may copy/use this under the terms of the GNU General Public License," echo "version 2 or later." exit fi if [[ "$1" == "--help" || "X$1" == "X-h" ]] # {{{1 then /bin/cat <" in ~/.vv-config -- for example, to handle .baz files: view_baz() { bazview \$* } The return value from this function is never checked -- it is assumed that if there's a handler for an extension it will properly view the file. You may also have an optional "view__big" function that handles viewing when the file is larger than about eight megabytes. This is to be defined when viewing compressed files that would potentially expand too much in RAM when being viewed through a pipe. You should figure that a compressed file could expand at least five to eight times larger than its compressed size. (See this script's code for examples of view__big functions versus vew_ functions.) Make sure you use $PAGER instead of less/more in your handlers. If you have a special file type with a "multi-part" extension you may configure them like this (these are defaults): # Handle .tar.gz and .tar.bz2 files with view_tar.gz() / view_tar.bz2(): multi_extensions[tar.*]='tar.%ext%' # Handle .html.gz and .html.bz2 files with view_html.gz() / view_html.bz2(): multi_extensions[html.*]='html.%ext%' multi_extensions[htm.*]='htm.%ext%' You may also map url://example.com/ URIs to viewer functions like this (these are defaults): # http://* should be handled by the view_html handler: uri_protocols[http]='html' uri_protocols[https]='html' EOF exit fi # }}}1 [[ $# == 0 ]] && $0 --help >&2 && exit 1 view $* && exit for i in $* # {{{1 do view $i && continue FILE_OUT=$(file -Lb $i) case "$FILE_OUT" in *text*) $PAGER $i ;; *executable*) echo "$BASENAME: $i:" read -q REPLY\?"This is a binary executable, pipe through 'strings'? " if [[ "$REPLY" == 'y' ]] then strings $i | $PAGER else read -q REPLY\?"View with $PAGER anyway? " [[ "$REPLY" == 'y' ]] && $PAGER $LESSFORCEOPT $i fi ;; *data*) echo "$BASENAME: $i:" read -q REPLY\?"This is a potentially unreadable data file, pipe through 'strings'? " if [[ "$REPLY" == 'y' ]] then strings $i | $PAGER else read -q REPLY\?"View with $PAGER anyway? " [[ "$REPLY" == 'y' ]] && $PAGER $LESSFORCEOPT $i fi ;; *) $PAGER $i ;; esac done # vim:ts=2:sw=2:ai:fo=croq2: # vim600:fdm=marker:fdc=3: