#!/usr/bin/env zsh # # ZShelf, file directory indexing for the web # # Copyright (C) 2014 Denis Roio # # This source code is free software; you can redistribute it and/or # modify it under the terms of the GNU Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This source code is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # Please refer to the GNU Public License for more details. # # You should have received a copy of the GNU Public License along with # this source code; if not, write to: # Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # API in brief: # # filetype_icon() - takes a filename as arg (can be file only or real path) # - returns a filename for the icon to be used from icons/ # # index_dir() - indexes a directory into an html file linking all its contents # # preview_file() - takes a filename as arg # renders the file in a usable format (thumb if image, html if markdown, etc.) # returns the filename of the rendered result # # file list map for current dir # format: filename;size;date typeset -alU files filetype_icon() { { test "$1" = "" } && { error "filetype_icon called without argument"; return 1 } fpath="$1" filename="${fpath##*/}" ext="${filename##*.}" name="${filename%%.*}" res="default.png" # analize extensions, will be overridden by name case $ext:l in txt) res=text-plain.png ;; html) res=text-html.png ;; xml) res=text-xml.png ;; md5|sha*) res=hash.png ;; asc|gpg) res=signature.png ;; tex|md|org) res=text-x-texinfo.png ;; patch) res=text-x-patch.png ;; sql) res=text-x-sql.png ;; vcart) res=text-x-vcard.png ;; dmg) res=dmg.png ;; esac # analize name case $name:l in changelog) res=text-x-changelog.png ;; readme) res=text-x-readme.png ;; install) res=text-x-install.png ;; authors) res=text-x-authors.png ;; makefile) res=text-x-makefile.png ;; news|usage) res=text-x-nfo.png ;; copying) res=text-x-copying.png ;; known_bugs|todo) res=text-x-log.png ;; sources) res=folder-development.png ;; doc|docs) res=folder-documents.png ;; releases|binaries) res=folder-downloads.png ;; esac # todo using mimetype -b and more print "$res" } index_short() { func "index_long_preview \"$1\" \"$2\" \"$3\"" dir="${1}${3}" { test -d "$dir" } || { error "cannot index: not a directory '$dir'"; return 1 } files=() ttmp=`ls "$dir" | awk ' /^total/ { next } /^$/ { next } { printf "files+=(\"$1\");" } '` { test $? = 0 } || { error "Error parsing directory: $dir" return 1 } eval "$ttmp" act "${#files} files parsed in $dir" diralias="$2" dirbase="$3" # setup paths for test if [ "$CMD" = "test" ]; then LINK_PREFIX="file://${dir}" else LINK_PREFIX="${diralias}${dirbase}" fi func "LINK_PREFIX = $LINK_PREFIX" cat < EOF for f in ${files}; do name="${f}" ext="${name##*.}" # file extension file="${1}/${name}" # file path typefield="" namefield="" previewfield="" link="" icon="" icon_width="width=\"50px\"" if [ -L "$file" ]; then # is a symlink if [ -d "$file" ]; then # symlink to folder link="" typefield="${link}\"symlink\"" namefield="${link}${name}" else # symlink to file link="" typefield="${link}\"symlink\"" namefield="${link}${name}" fi elif [ -d "$file" ]; then # is a folder link="" typefield="${link}\"folder\"" namefield="${link}${name}" else # is a file { test "$icon" = "" } && { icon="`filetype_icon ${file}`" } link="" typefield="${link}\"${icon}\"" namefield="${link}${name}" fi # render it all cat < ${typefield} ${namefield} EOF tpwd="`pwd`" { test "$icon" = "" } && { continue } popd # copy the icon file { test -r ${destination}/icons/${icon} } || { func "copy icon in place: $icon (PWD: `pwd`)" cp $SYS/icons/$THUMB_SIZE/${icon} \ ${destination}/icons/${icon} } pushd "$tpwd" done print "" } index_long_preview() { func "index_long_preview \"$1\" \"$2\" \"$3\"" dir="${1}${3}" { test -d "$dir" } || { error "cannot index: not a directory '$dir'"; return 1 } files=() ttmp=`ls -l --time-style=long-iso "$dir" | awk ' /^total/ { next } /^$/ { next } { printf "files+=(\"%s;%s;%s\");", $8, $5, $6 } '` { test $? = 0 } || { error "Error parsing directory: $dir" return 1 } eval "$ttmp" act "${#files} files parsed in $dir" diralias="$2" dirbase="$3" # setup paths for test if [ "$CMD" = "test" ]; then LINK_PREFIX="file://${dir}" else LINK_PREFIX="${diralias}${dirbase}" fi func "LINK_PREFIX = $LINK_PREFIX" # human size dividers _mb=$((1024 * 1024)) _gb=$((1024 * 1024 * 1024)) cat < Filename Size Date Preview EOF for f in ${files}; do name="${f[(ws:;:)1]}" size="${f[(ws:;:)2]}" date="${f[(ws:;:)3]}" ext="${name##*.}" # file extension file="${1}/${name}" # file path # format size to human readable form if [[ $size -lt 1024 ]]; then hsize="$size B" elif [[ $size -gt 1024 ]]; then hsize="$(( $size / 1024 )) KB" elif [[ $size -gt 1048576 ]]; then hsize="$(( $size / $mb )) MB" else hsize="$(( $size / $gb )) GB" fi # format date to human readable form hdate=`date -d "$date" +'%d %b %Y'` func "$name \t $size \t $date" typefield="" namefield="" previewfield="" link="" icon="" preview=`preview_file "$file"` { test "$preview" = "" } || { func "file preview produced" case $ext:l in jpg|jpeg|png|gif|pdf|svg|eps) func "preview is a thumbnail" preview="\"$name\"" icon="image-x-generic.png" ;; esac } icon_width="width=\"50px\"" if [ -L "$file" ]; then # is a symlink if [ -d "$file" ]; then # symlink to folder link="" typefield="${link}\"symlink\"" namefield="${link}${name}" previewfield="" else # symlink to file link="" typefield="${link}\"symlink\"" namefield="${link}${name}" previewfield="${link}${preview}" fi elif [ -d "$file" ]; then # is a folder link="" typefield="${link}\"folder\"" namefield="${link}${name}" previewfield="" else # is a file { test "$icon" = "" } && { icon="`filetype_icon ${file}`" } link="" typefield="${link}\"${icon}\"" namefield="${link}${name}" previewfield="${link}${preview}" fi # render it all cat < ${typefield} ${namefield} $hsize $hdate ${previewfield} EOF tpwd="`pwd`" { test "$icon" = "" } && { continue } popd # copy the icon file { test -r ${destination}/icons/${icon} } || { func "copy icon in place: $icon (PWD: `pwd`)" cp $SYS/icons/$THUMB_SIZE/${icon} \ ${destination}/icons/${icon} } pushd "$tpwd" done print "" } preview_file() { { test "$1" = "" } && { error "no file to preview."; return 1 } # get the file extension using zsh builtins # %% is for deleting prefix and ## is for deleting suffix for f in ${1}; do filename="${f##*/}" name="${filename%%.*}" ext="${f##*.}" # lowercase case $ext:l in md) # markdown notice "$f: rendering markdown using maruku" output="$name".html maruku --html --html-frag $f -o "$output" print "$output" ;; jpg|jpeg|png) output="${name}-thumb.${ext}" { test -r "$output" } && { act "$f: thumbnail found, skip rendering" print "$output" continue } notice "$f: rendering thumbnail using ImageMagick" convert $f -resize 100 $output print "$output" ;; pdf|gif) # only first page / animation frame output="${name}-thumb.jpg" { test -r "$output" } && { act "$f: thumbnail found, skip rendering" print "$output" continue } notice "$f: rendering thumbnail using ImageMagick" convert "${f}[0]" -resize 100 $output print "$output" ;; svg|eps) output="${name}-thumb.jpg" { test -r "$output" } && { act "$f: thumbnail found, skip rendering" print "$output" continue } notice "$f: rendering thumbnail using ImageMagick" convert -density 144 "${f}" -resize 100 $output print "$output" ;; esac done return 0 }