aboutsummaryrefslogtreecommitdiff
path: root/.local/share
diff options
context:
space:
mode:
authortdro <tdro@users.noreply.github.com>2019-12-21 14:38:41 -0500
committertdro <tdro@users.noreply.github.com>2019-12-21 14:38:41 -0500
commitddca9eed12e717a613b2d7b67526c13f420ba3e8 (patch)
treec7a46b5e252ac46a2401465715b25ec8f63dcadd /.local/share
parente0d0105453987a12c0af8bee6da8b345f978a04f (diff)
downloaddotfiles-ddca9eed12e717a613b2d7b67526c13f420ba3e8.tar.gz
dotfiles-ddca9eed12e717a613b2d7b67526c13f420ba3e8.tar.bz2
dotfiles-ddca9eed12e717a613b2d7b67526c13f420ba3e8.zip
.local/share/bin: Move bin folder to .local/share
Diffstat (limited to '.local/share')
-rwxr-xr-x.local/share/bin/pdfmted-editor739
-rwxr-xr-x.local/share/bin/sl1227
2 files changed, 0 insertions, 1966 deletions
diff --git a/.local/share/bin/pdfmted-editor b/.local/share/bin/pdfmted-editor
deleted file mode 100755
index 8a3b46b..0000000
--- a/.local/share/bin/pdfmted-editor
+++ /dev/null
@@ -1,739 +0,0 @@
-#!/bin/bash
-# NAME: PDFMtEd – Editor
-# VERSION: 0.1
-# AUTHOR: (c) 2014 Glutanimate
-# DEPENDENCIES: yad exiftool python2.7 (sejda-pdf) (qpdf)
-#
-# DESCRIPTION: View and edit PDF metadata using exiftool.
-#
-# LICENSE: GNU GPLv3 (http://www.gnu.de/documents/gpl-3.0.en.html)
-#
-# NOTICE: THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
-# EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-# PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR
-# IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-# AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND
-# PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE,
-# YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-#
-# IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY
-# COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS
-# PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
-# INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
-# THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
-# INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE
-# PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER
-# PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-#
-# USAGE: pdfmted_editor [-r|-u] <file(s) or directory(ies)>
-
-################ GLOBVAR #################
-
-EXECUTABLE="$(readlink -f "$0")"
-PROGDIR="${EXECUTABLE%/*}"
-THUMBNAILER="$PROGDIR/pdfmted-thumbnailer.py"
-TMPDIR="/tmp/${0##*/}"
-
-############### SETTINGS #################
-
-# Supply password for protected PDF files
-PASSWORD=""
-# Rename modified files by default?
-RENAME_OPT="FALSE"
-
-############## DIALOGS #################
-
-# Usage message
-
-USAGE="
-Usage: $(basename "$0") [-r|-u|-h] <PDF Files/Directories>
-
- Title: PDFMtEd – Editor
- Author: (c) 2014 Glutanimate
- Description: View and edit PDF metadata
- Arguments: This script accepts multiple PDF files and
- directories as input.
-
-Available options:
- -r rename files by default
- -u arguments are URIs
- -h display this help message\
-"
-
-# General settings
-
-YAD_TITLE="PDFMtEd – Editor"
-WMCLASS="pdfmtededitor"
-ICON="application-pdf"
-WIDTH="800"
-HEIGHT="650"
-PROGWIDTH="400"
-PROGHEIGHT="100"
-
-# Descriptions and tooltips
-
-TEXT_INFO_DEPERR="Error: Missing dependencies. Please install the \
-following:"
-
-TEXT_MAIN_DESCRP="\
-<b>Important note</b>:
-
-Empty fields will <b>delete</b> the associated metadata. \
-However, modifications performed by exiftool are <b>reversible</b>. \
-To purge the file of all metadata please use PDFMtEd – Inspector
-"
-
-TEXT_CHCK_SAVECH="Save changes, file will be skipped if unchecked"
-TEXT_CHCK_RENMFL="Rename based on tags"
-TEXT_CHCK_DUPLIC="Copy tags over to next/previous file"
-
-TEXT_HOVR_CANCEL="Cancel and exit"
-TEXT_HOVR_FNNISH="Save (if option selected) and exit"
-TEXT_HOVR_PREVIT="Move back to previous document"
-TEXT_HOVR_NEXTIT="Move to next document in line"
-
-TEXT_BTTN_PDFVWR="_Open in PDF viewer"
-TEXT_HOVR_PDFVWR="Open document with default PDF viewer"
-
-TEXT_BTTN_SHWFLD="Show in parent _directory"
-TEXT_HOVR_SHWFLD="Open parent directory with default file manager"
-
-TEXT_BTTN_CLRALL="C_lear all"
-TEXT_HOVR_CLRALL="Clear all fields"
-
-TEXT_INFO_WARNG1="Something went wrong while writing the metadata. Processing the document with \
-sejda (recommended) or linearizing it with qpdf helps in these cases. Want to proceed?"
-TEXT_NOTI_SUCCE1="Metadata succesfully updated."
-TEXT_NOTI_ERROR1="Post-processing failed."
-
-############## FUNCTIONS #################
-
-# dependencies
-
-check_deps() {
- for i in "$@"; do
- type "$i" > /dev/null 2>&1
- if [[ "$?" != "0" ]]; then
- MISSINGDEPS+=" $i"
- fi
- done
-}
-
-gui_check_deps(){
- check_deps yad exiftool python2.7
- if [[ -n "$MISSINGDEPS" ]]; then
- gui_show_error "${TEXT_INFO_DEPERR}
- ${MISSINGDEPS}"
- exit 1
- fi
-}
-
-# cleanup
-
-var_unset(){
- unset -v KEYWORDS SUBJECT AUTHOR TITLE_FN UPDATED_NAME
-}
-
-cleanup(){
- [[ -n "$TMPDIR" && -d "$TMPDIR" ]] && rm -r "$TMPDIR"
-}
-
-# evaluate and check arguments
-
-arg_evaluate_options(){
- # grab options if present
- while getopts "ruh" OPTIONS; do
- case $OPTIONS in
- r ) RENAME_OPT="TRUE"
- ;;
- u ) URI_OPT="TRUE"
- ;;
- h ) echo "$USAGE"
- exit 0
- ;;
- \? ) echo "$USAGE"
- exit 1
- ;;
- esac
- done
-}
-
-arg_check_correct(){
- # check if arguments aside from options given
- if [[ "$#" = "0" ]]
- then
- error_log 1
- fi
-
- # check if arguments are files or folders
- for ITEM in "$@"; do
- if [[ ! -d "$ITEM" && ! -f "$ITEM" ]]
- then
- error_log 3
- fi
- done
-
- # notify user and exit
- if [[ "$ARGERROR" = "1" ]]
- then
- gui_show_error "ERROR: No files/folder selected."
- echo "Exiting..."
- exit 1
- fi
-}
-
-arg_uri_to_path(){
- # handle cases where we have to use "pseudo-uris"
- cnt=0
- FILEARGS=()
- for URI in "$@"; do
- # only handles file:// prefix
- # for other uri-specific substitutions we would have to
- # use python+gio-bindings
- FILEARGS[$cnt]="$(echo "$URI" | sed "s/^file:\/\///g")"
- ((cnt++))
- done
-}
-
-arg_compose_filearray(){
- # recursively add pdf files and folders in given arguments to array
- unset ARGS
- ARGID="1" # start array at 1
- while IFS= read -r -d $'\0' FILE; do
- if [[ ! "$(file -ib "$FILE")" == *application/pdf* ]]
- then
- echo "Error: '$FILE' is not a pdf file. Ignoring."
- continue
- fi
- ARGS[ARGID++]="$FILE"
- done < <(find "$@" -type f -name '*.pdf' -print0 | sort -z --version-sort)
- # numerical-composite (e.g. 1.1<1.2), zero-delimited sorting
- ARGSTOTAL="${#ARGS[@]}"
- ARGID="1"
- if [[ "$ARGSTOTAL" = "0" ]]; then
- gui_show_error "ERROR: No PDF files found."
- echo "Exiting..."
- exit 1
- fi
-}
-
-# Error handling
-
-error_log(){
- if [[ "$1" = "1" ]]; then
- ARGERROR="1"
- echo "ARG ERROR: insufficient arguments given."
- elif [[ "$1" = "2" ]]; then
- ARGERROR="1"
- echo "ARG ERROR: illegal arguments ('$ITEM' is not a file or directory.)"
- elif [[ "$1" = "3" ]]; then
- WRITEERROR="1"
- echo "WRITE ERROR: File not updated. Likely an issue with exiftool"
- elif [[ "$1" = "5" ]]; then
- PSTPRCERROR="1"
- echo "POST PROCESSING ERROR: qpdf/sejda failed."
- fi
-}
-
-# Set up environment
-
-setup_filevars(){
- FILE="$(readlink -f "$1")"
- FILE_ESCAPED="${FILE//%/%%}"
- FILENAME="${FILE##*/}"
- BASENAME="${FILENAME%.*}"
- DIRNAME="${FILE%/*}"
- DIRNAME_ESCAPED="${FILE_ESCAPED%/*}"
- PREV_FILE="${ARGS[$ARGID-1]}"
- NEXT_FILE="${ARGS[$ARGID+1]}"
- PREV_FILENAME="${PREV_FILE##*/}"
- NEXT_FILENAME="${NEXT_FILE##*/}"
-}
-
-# Set up GUI
-
-setup_geometry(){
- GEOMETRYOPTS=("--center" "--height=$HEIGHT" "--width=$WIDTH")
- PROGGEO=("--center" "--height=$PROGHEIGHT" "--width=$PROGWIDTH")
-}
-
-setup_gui(){
- MULTIDOC_TITLE="$YAD_TITLE ($ARGID/$ARGSTOTAL)"
- # we are using arrays to construct and modify YAD dialogs
- # multiple documents - show all elements
- if [[ "$MULTIDOC" = "middle" ]]
- then
- MULTIDOC_FIELDS=(\
- --field="$TEXT_CHCK_DUPLIC":CHK "FALSE" \
- --field="<b>Previous Document</b>:":RO "$PREV_FILENAME" \
- --field="<b>Next Document</b>:":RO "$NEXT_FILENAME")
- MULTIDOC_BUTTONS=(--button="_Previous!go-previous!$TEXT_HOVR_PREVIT:1" \
- --button="_Next!go-next!$TEXT_HOVR_NEXTIT:2")
- # multiple documents, first doc - hide previous
- elif [[ "$MULTIDOC" = "first" ]]
- then
- MULTIDOC_FIELDS=(\
- --field="$TEXT_CHCK_DUPLIC":CHK "FALSE" \
- --field="<b>Next Document</b>:":RO "$NEXT_FILENAME" )
- MULTIDOC_BUTTONS=(--button="_Next!go-next!$TEXT_HOVR_NEXTIT:2")
- # multiple documents, last doc - hide next
- elif [[ "$MULTIDOC" = "last" ]]
- then
- MULTIDOC_FIELDS=(\
- --field="$TEXT_CHCK_DUPLIC":CHK "FALSE" \
- --field="<b>Previous Document</b>:":RO "$PREV_FILENAME")
- MULTIDOC_BUTTONS=(--button="_Previous!go-previous!$TEXT_HOVR_PREVIT:1")
- # single document - hide all elements
- elif [[ "$MULTIDOC" = "single" ]]
- then
- HEIGHT="620"
- MULTIDOC_FIELDS=()
- MULTIDOC_BUTTONS=()
- fi
- setup_geometry
-}
-
-# GUIs
-
-gui_notify(){
- notify-send -i "$NOTIFY_ICON" "$YAD_TITLE" "$1"
-}
-
-gui_progress(){
- # ampersands in gui elements cause issues in gtk/pango markup
- BASENAME_PANGO="$(echo "$BASENAME" | sed 's/\&/\&amp;/g')"
- yad --progress --pulsate --auto-close \
- "${PROGGEO[@]}" \
- --image="$ICON" --window-icon="$ICON" \
- --class="$WMCLASS" \
- --title="$YAD_TITLE" \
- --text="Processing <b>$BASENAME_PANGO</b> ..." \
- --button='_Cancel!gtk-cancel!':1 \
- 2> /dev/null
- PROG_RET="$?"
- if [[ "$PROG_RET" != "0" ]]
- then
- kill -s TERM "$TOP_PID"
- fi
-}
-
-gui_show_error(){
- yad --title="$YAD_TITLE" \
- --center --width="400" --height="100" \
- --image=dialog-error \
- --window-icon=dialog-error \
- --class="$WMCLASS" \
- --text="$1" \
- --button="OK":0 2> /dev/null
- echo "$1"
-}
-
-gui_metadata_entry_multi(){
- NEW_METADATA="$( \
- yad --form --scroll \
- --always-print-result \
- "${GEOMETRYOPTS[@]}" \
- --image="$IMAGE" --window-icon="$ICON" \
- --class="$WMCLASS" \
- --title="$MULTIDOC_TITLE" \
- --buttons-layout=end \
- --button="_Cancel!gtk-cancel!$TEXT_HOVR_CANCEL:33" \
- "${MULTIDOC_BUTTONS[@]}" \
- --button="_Finish!gtk-ok!$TEXT_HOVR_FNNISH:3" \
- --field="<b>Current Document</b>:":RO "$FILENAME"\
- --field="":LBL "" \
- --field="$TEXT_MAIN_DESCRP":LBL "" \
- --field="Author:" "$AUTHOR_ORIGINAL" \
- --field="Title:" "$TITLE_ORIGINAL" \
- --field="Year:":NUM "$YEAR_ORIGINAL[!0..3000[!1]]" \
- --field="Keywords:" "$KEYWORDS_ORIGINAL" \
- --field="Subject:" "$SUBJECT_ORIGINAL" \
- --field "$TEXT_BTTN_CLRALL!gtk-clear!$TEXT_HOVR_CLRALL":FBTN \
- "@bash -c gui_clear_fields" \
- --field="":LBL "" \
- --field="$TEXT_BTTN_PDFVWR!application-pdf!$TEXT_HOVR_PDFVWR:FBTN" \
- "bash -c \"gui_open '$FILE_ESCAPED'\"" \
- --field="$TEXT_BTTN_SHWFLD!folder!$TEXT_HOVR_SHWFLD:FBTN" \
- "bash -c \"gui_open '$DIRNAME_ESCAPED'\"" \
- --field="":LBL "" \
- --field="$TEXT_CHCK_SAVECH":CHK "TRUE" \
- --field="$TEXT_CHCK_RENMFL":CHK "$RENAME_OPT" \
- "${MULTIDOC_FIELDS[@]}" \
- 2> /dev/null)"
- RET_MAIN=$?
- if [[ "$RET_MAIN" = 1 ]]
- then
- PROGRESSION="PREVIOUS"
- elif [[ "$RET_MAIN" = 2 ]]
- then
- PROGRESSION="NEXT"
- elif [[ "$RET_MAIN" = 3 ]]
- then
- PROGRESSION="FINISH"
- else
- echo "Aborted."
- exit 1
- fi
-}
-
-gui_postprocess_pdf(){
- yad --title="$YAD_TITLE" \
- --center --width="400" --height="100" \
- --image=dialog-error \
- --window-icon=dialog-error \
- --class="$WMCLASS" \
- --text="$TEXT_INFO_WARNG1" \
- --button="_Process with Sejda!gtk-ok!$TEXT_HOVR_PSTPRC_SEJ:0" \
- --button="_Linearize!gtk-ok!$TEXT_HOVR_PSTPRC_LIN:2" \
- --button="_Cancel!gtk-cancel!$TEXT_HOVR_PSTPRC_CANCEL:1"
- RET_PSTPRC="$?"
- if [[ "$RET_PSTPRC" = "0" ]]; then
- if ! type sejda-console > /dev/null 2>&1; then
- gui_show_error "Error: Sejda not installed."
- gui_postprocess_pdf
- return
- fi
- sejda_process_pdf | gui_progress
- check_modify_success
- error_recheck_write
- elif [[ "$RET_PSTPRC" = "2" ]]; then
- if ! type qpdf > /dev/null 2>&1; then
- gui_show_error "Error: qpdf not installed."
- gui_postprocess_pdf
- return
- fi
- linearize_pdf | gui_progress
- check_modify_success
- error_recheck_write
- else
- return 1
- fi
-}
-
-
-# GUI extensions
-
-# create thumbnail
-gui_thumbnail_generate() {
- if [[ -f "$THUMBNAILER" ]]; then
- echo "#Generating preview..."
- $THUMBNAILER "$FILE" > /dev/null 2>&1
- fi
-}
-
-# assign thumbnail
-gui_thumbnail_assign(){
-
- PREVIEWIMG="$(python2.7 -c "import gio; import sys; \
- print gio.File(sys.argv[1]).query_info('*').get_attribute_byte_string \
- ('''thumbnail::path''')" "$FILE")"
-
- if [[ "$(file -ib "$FILE" | cut -d';' -f1)" != "application/pdf" || ! -f "$PREVIEWIMG" ]]
- then
- PREVIEWIMG="$ICON"
- fi
-
- IMAGE="$PREVIEWIMG"
- NOTIFY_ICON="$PREVIEWIMG"
-}
-
-# generic yad xdg-open function
-gui_open(){
- PATH_ESCAPED="${1//%/%25}"
- xdg-open "$PATH_ESCAPED" > /dev/null 2>&1 &
-}
-
-# clear yad fields
-gui_clear_fields(){
- for FIELDNR in {4..8}; do
- echo "$FIELDNR:"
- done
-}
-
-# preserve metadata for next document in line
-duplicate_metadata () {
- AUTHOR_ORIGINAL="$AUTHOR"
- TITLE_ORIGINAL="$TITLE"
- YEAR_ORIGINAL="$YEAR"
- SUBJECT_ORIGINAL="$SUBJECT"
- KEYWORDS_ORIGINAL="$KEYWORDS"
-}
-
-
-process_yad_formchoices(){
- echo "$NEW_METADATA"
- AUTHOR="$(echo "$NEW_METADATA" | cut -d '|' -f4)"
- TITLE="$(echo "$NEW_METADATA" | cut -d '|' -f5)"
- YEAR="$(echo "$NEW_METADATA" | cut -d '|' -f6 | cut -d ',' -f1 | cut -d '.' -f1)"
- KEYWORDS="$(echo "$NEW_METADATA" | cut -d '|' -f7)"
- SUBJECT="$(echo "$NEW_METADATA" | cut -d '|' -f8)"
- SAVECHOICE="$(echo "$NEW_METADATA" | cut -d '|' -f14)"
- RENAMECHOICE="$(echo "$NEW_METADATA" | cut -d '|' -f15)"
- DUPLICATECHOICE="$(echo "$NEW_METADATA" | cut -d '|' -f16)"
- MODIFYDATE="$(date +"%Y:%m:%d %H:%M:%S")"
- if [[ "$YEAR" = "0" ]]; then
- CREATEDATE=""
- else
- CREATEDATE="$(printf "%04d" "$YEAR"):01:01 00:00:00"
- fi
-}
-
-# Metadata input
-
-read_metadata() {
- echo "#Reading embedded metadata..."
- exiftool -e -S -sep ";" -PDF:Title -PDF:Author -PDF:Subject \
- -PDF:Keywords -PDF:CreateDate -PDF:ModifyDate \
- "$FILE" > "$PROGPIPE"
-}
-
-print_original_metadata() {
- echo "###Original metadata:"
- echo ""
- echo "Author: $AUTHOR_ORIGINAL"
- echo "Title: $TITLE_ORIGINAL"
- echo "Year: $YEAR_ORIGINAL"
- echo "Creation date: $CREATEDATE_ORIGINAL"
- echo "Modification date: $MODIFYDATE_ORIGINAL"
- echo "Subject: $SUBJECT_ORIGINAL"
- echo "Keywords: $KEYWORDS_ORIGINAL"
-}
-
-evaluate_original_metadata() {
- METADATA_PDF="$( [[ -f "$PROGPIPE" ]] && cat "$PROGPIPE")"
- AUTHOR_ORIGINAL="$(echo "$METADATA_PDF" | sed -n 's/Author: //p')"
- TITLE_ORIGINAL="$(echo "$METADATA_PDF" | sed -n 's/Title: //p')"
- CREATEDATE_ORIGINAL="$(echo "$METADATA_PDF" | sed -n 's/CreateDate: //p')"
- YEAR_ORIGINAL="${CREATEDATE_ORIGINAL:0:4}"
- MODIFYDATE_ORIGINAL="$(echo "$METADATA_PDF" | sed -n 's/ModifyDate: //p')"
- SUBJECT_ORIGINAL="$(echo "$METADATA_PDF" | sed -n 's/Subject: //p')"
- KEYWORDS_ORIGINAL="$(echo "$METADATA_PDF" | sed -n 's/Keywords: //p')"
-}
-
-# Metadata output
-
-delete_metadata(){
- echo "#Deleting existing metadata"
- exiftool -e -overwrite_original -all:all="" "$FILE"
-}
-
-write_metadata(){
- echo "#Writing new metadata"
- exiftool -e -overwrite_original -password "$PASSWORD" -sep ";" \
- -PDF:Title="$TITLE" -PDF:Author="$AUTHOR" \
- -PDF:Subject="$SUBJECT" -PDF:Keywords="$KEYWORDS" \
- -PDF:ModifyDate="$MODIFYDATE" -PDF:CreateDate="$CREATEDATE" \
- -XMP-dc:Subject="$KEYWORDS" -XMP-dc:Description="$SUBJECT" -XMP-dc:Title="$TITLE" \
- -XMP-dc:Creator="$AUTHOR" -XMP-dc:Date="$CREATEDATE" \
- "$FILE"
- echo "$?" > "$ERRORPIPE1"
-}
-
-update_metadata(){
- delete_metadata; write_metadata
- check_modify_success
- error_check_write
-}
-
-print_updated_metadata() {
- echo "###Updated metadata:"
- echo ""
- echo "Author: $AUTHOR"
- echo "Title: $TITLE"
- echo "Year: $YEAR"
- echo "Creation date: $CREATEDATE"
- echo "Modificaiton date: $MODIFYDATE"
- echo "Subject: $SUBJECT"
- echo "Keywords: $KEYWORDS"
-}
-
-update_filename(){
- TARGETFOLDER="$DIRNAME"
- # replace characters in title that might cause issues on some file systems
- TITLE_FN="$(echo "$TITLE" | sed -e 's/\:/\;/g' -e 's/\&/and/g')"
- # Modify the following lines to change the naming scheme:
- # (default naming scheme: ${AUTHOR} - ${TITLE_FN} - ${YEAR})
- if [[ -n "$AUTHOR" ]]; then
- UPDATED_NAME="$AUTHOR"
- fi
- if [[ -n "$TITLE" ]]; then
- if [[ -n "$UPDATED_NAME" ]]; then
- UPDATED_NAME+=" - $TITLE_FN"
- else
- UPDATED_NAME="$TITLE"
- fi
- fi
- if [[ -n "$YEAR" && "$YEAR" != "0" ]]; then
- if [[ -n "$UPDATED_NAME" ]]; then
- UPDATED_NAME+=" - $YEAR"
- else
- UPDATED_NAME="$YEAR"
- fi
- fi
- echo "Updating file name..."
- mv "$FILE" "$TARGETFOLDER/$UPDATED_NAME.pdf"
- ARGS[$ARGID]="$TARGETFOLDER/$UPDATED_NAME.pdf" # make sure to update file array with
- # new location
-}
-
-# Error checks and workarounds
-
-# check if modification was successful
-check_modify_success(){
- # if the PDF ModifyDate hasn't been altered we can assume
- # that the exiftool tagging failed
- MODDATE_CHECK="$(exiftool -e -S -sep ";" -PDF:ModifyDate "$FILE"\
- | sed -n 's/ModifyDate: //p')"
- if [[ "$MODDATE_CHECK" != "$MODIFYDATE" ]]
- then
- error_log 3
- fi
-}
-
-error_check_write(){
- WRITE_RETURNCODE="$(cat "$ERRORPIPE1")"
- if [[ "$WRITE_RETURNCODE" != "0" || "$WRITEERROR" = "1" ]]
- then
- WRITEERROR="0"
- echo "$TEXT_INFO_WARNG1"
- gui_postprocess_pdf
- else
- gui_notify "$TEXT_NOTI_SUCCE1"
- echo "Success."
- fi
-}
-
-error_recheck_write(){
- PSTPRC_RETURNCODE="$(cat "$ERRORPIPE2")"
- if [[ "$PSTPRC_RETURNCODE" != "0" || "$WRITEERROR" = "1" ]]
- then
- error_log 4
- echo "$TEXT_NOTI_ERROR1"
- gui_notify "$TEXT_NOTI_ERROR1"
- else
- gui_notify "$TEXT_NOTI_SUCCE1"
- echo "Success."
- fi
-}
-
-sejda_process_pdf(){
- # Sejda does not preserve any document metadata
- # so we have to perform the exiftool actions again;
- # we use delete_metadata() in order to remove sejda-related metadata
- echo "#Running Sejda on $FILENAME..."
- TEMPFILE="$DIRNAME/${BASENAME}_sejda.pdf"
- sejda-console merge --compressed -v 7 -f "$FILE" -o "$TEMPFILE"
- RET_POSTPROC="$?"
- if [[ "$RET_POSTPROC" = "0" ]]; then
- mv "$TEMPFILE" "$DIRNAME/${BASENAME}.pdf"
- delete_metadata
- write_metadata
- fi
- echo "$RET_POSTPROC" > "$ERRORPIPE2"
-}
-
-linearize_pdf(){
- # qpdf only preserves metadata that is not marked as
- # obsolete by exiftool; to purge all existing (and potentially
- # corrupted metadata entries) we first delete all metadata,
- # linearize with qpdf, and then rewrite the metadata
- echo "#Linearizing $FILENAME..."
- delete_metadata
- TEMPFILE="$DIRNAME/${BASENAME}_linearized.pdf"
- qpdf --linearize "$FILE" "$TEMPFILE"
- RET_QPDF="$?"
- if [[ "$RET_QPDF" = "0" ]]; then
- mv "$TEMPFILE" "$DIRNAME/${BASENAME}.pdf"
- write_metadata
- fi
- echo "$RET_QPDF" > "$ERRORPIPE2"
-}
-
-# Main
-
-main_check_argid(){
- echo "Processing $ARGID of $ARGSTOTAL"
- if [[ "$ARGSTOTAL" = "1" ]]
- then
- MULTIDOC="single"
- elif [[ "$ARGID" = "1" ]]
- then
- MULTIDOC="first"
- elif [[ "$ARGID" = "$ARGSTOTAL" ]]
- then
- MULTIDOC="last"
- else
- MULTIDOC="middle"
- fi
-}
-
-main_iteration(){
- var_unset
- setup_filevars "$1"
- setup_gui
- gui_thumbnail_generate; read_metadata
- gui_thumbnail_assign
- evaluate_original_metadata
- [[ "$DUPLICATECHOICE" = "TRUE" ]] && duplicate_metadata
- print_original_metadata
- gui_metadata_entry_multi
- process_yad_formchoices
- print_updated_metadata
- [[ "$SAVECHOICE" = "TRUE" ]] && update_metadata
- [[ "$SAVECHOICE" = "TRUE" && "$RENAMECHOICE" = "TRUE" ]] && update_filename
-}
-
-main_check_progression(){
- if [[ "$PROGRESSION" = "NEXT" ]]
- then
- ARGID="$((ARGID+1))"
- main
- elif [[ "$PROGRESSION" = "PREVIOUS" ]]
- then
- ARGID="$((ARGID-1))"
- main
- elif [[ "$PROGRESSION" = "FINISH" ]]
- then
- echo "Finished."
- exit 0
- fi
-}
-
-main(){
- main_check_argid
- main_iteration "${ARGS[$ARGID]}"
- main_check_progression
-}
-
-############## FCT EXPORT ################
-
-# functions assigned to yad buttons need to be exported
-
-export -f gui_clear_fields
-export -f gui_open
-
-############### CLEANUP ##################
-
-trap "cleanup; exit" EXIT
-
-############## PREPWORK ###################
-
-TOP_PID="$$"
-mkdir -p "$TMPDIR"
-PROGPIPE="$(mktemp -u --tmpdir="$TMPDIR" XXXXXXXX)"
-ERRORPIPE1="$(mktemp -u --tmpdir="$TMPDIR" XXXXXXXX)"
-ERRORPIPE2="$(mktemp -u --tmpdir="$TMPDIR" XXXXXXXX)"
-
-############## USGCHECKS #################
-
-gui_check_deps
-arg_evaluate_options "$@"
-shift $((OPTIND-1))
-FILEARGS=("$@")
-[[ "$URI_OPT" = "TRUE" ]] && arg_uri_to_path "${FILEARGS[@]}"
-arg_check_correct "${FILEARGS[@]}"
-arg_compose_filearray "${FILEARGS[@]}"
-
-################ MAIN ####################
-
-main
diff --git a/.local/share/bin/sl b/.local/share/bin/sl
deleted file mode 100755
index 408779a..0000000
--- a/.local/share/bin/sl
+++ /dev/null
@@ -1,1227 +0,0 @@
-#!/usr/bin/tclsh
-
-#
-# sl - usefully organize directories and point out interesting files
-# Invented by Tom Phelps on December 30, 2011.
-#
-# home site: http://www.PracticalThought.com/
-#
-# Copyright (c) 2011-2012 Thomas A. Phelps
-# Licensed under the GNU Public License version 3 (http://www.gnu.org/licenses/).
-# NO WARRANTY.
-#
-#
-# $Revision: 1.89 $ $Date: 2012/02/20 04:20:46 $
-#
-
-if {[package vcompare [info patchlevel] 8.5] < 0} { puts stderr "ERROR: requires Tcl 8.5"; exit 1 }
-
-
-if { ![catch {set tty [exec stty -a]}] && [regexp {(\d+)\s+columns} $tty all TERMINFO(width)]} {
-} elseif {[info exists env(COLUMNS)]} {set TERMINFO(width) $env(COLUMNS)
-} else {set TERMINFO(width) 80}
-
-array set TIME [list SECOND 1 MINUTE [expr 60] HOUR [expr 60*60] DAY [expr 60*60*24] MONTH [expr 60*60*24*30] YEAR [expr 60*60*24*365]]
-array set TIME [list now [clock seconds]]
-set TIME(epsilon) $TIME(MINUTE) ;# amount that times must differ to be considered different, due to different clocks, delays in file system and network, ...
-
-
-# groups: by suffix, by exact name (modulo compression wrapper), by regexp
-# This is not intended to be a complete database -- add more and reassign in your ~/.sl.tcl startup file.
-# are archives like dir (and sorted together)? both labelled collections
-# tosort: maybe psd/ai/TODO in dev for artist?
-# group names short so when used as title also separate groups by whitespace
-# conflicts: .asf audio/video, .iso data/video, .xml doc/usually data, .raw and .bin can be anything
-set GROUP {
- special {byascii} {}
- dir {title prefix by1} {}
-
- build {byascii} { Makefile GNUmakefile build.xml -build.number configure INSTALL README }
- app {bysuffix} { -.so -.a .jar .exe .dll -core }
- dev {} { .c .cpp .cc .m -.h -.o .java -.class .scala .f
- .tcl .sh .py -.pyc .pl .scpt .m4 .lua
- .nib .y
- -CVS -RCS -TAGS -tags .el -.elc }
- A2 {title} { .dsk .do .po .2mg .2img .shk .sdk .sea .bsq .bxy .bny .bsc .dc }
-
- WWW {} { .html .htm .shtml .php .php3 .ars }
- aux {bysuffix} { .css .js .url .webloc robots.txt .json }
-
- doc {prefix} { .txt .man .1 .3 .dox .chm .tex .bib .sty .dvi .texi .info .nfo .readme Index }
- book {title} { .ps .pdf .djvu .epub .mobi .cbr .cbz }
- document {title} { .indd .odt .doc .rtf .docx .ppt .pptx .xls .xlsx }
- image {title nosfx prefix} { .jpg .jpeg .jp2 .png .gif .tiff .tif .dng .psd .xbm .xpm .pbm .pgm .ppm .ico .bmp .raw Thumbs.db }
- vector {title} { .ai .svg .wmf .emf }
- audio {title nosfx prefix} { .aac .mp3 .m4a .m4b .m4p .aif .aiff .snd .flac .ogg .oga .mid .wav .wma .ra .mod }
- video {title nosfx prefix} { .mpg .mpeg .avi .mov .wmv .mkv .mp4 .m4v .flv .f4v .asf .ram .rm .rmvb .divx .vob .iso }
- font {title nosfx prefix} { .pfb .pfa -.afm -.pfm .ttf .ttc .dfont .otf .otc .woff -fonts.dir -fonts.list -fonts.scale -encodings.dir }
-
- data {} { .xml .plist .strings .data .dat .sfv .md5 .bin }
- other {byascii} {}
- archive {title prefix} { .zip .tar .tgz .xz .rar .sit .sitx .cpt .dmg }
-
- dotfile {} {}
- Mac {} { -.DS_Store "-Desktop DB" "-Desktop DF" }
- ignore {} { .bak bkup .bkup .bup tmp .tmp -old .old -obsolete ignore }
-}
-if {([llength $GROUP]%3) != 0} {puts stderr "GROUP: some line missing field"; exit 1}
-foreach {group flags entries} $GROUP {
- set G2F($group) $flags
- foreach x $entries {
- if {"-" == [string index $x 0]} {set EQ([string range $x 1 end]) -$group
- } else {set EQ($x) $group}
- }
-}
-set tmp "(?i)README"
-append tmp {|TODO$|notes|TOSORT}
-append tmp {|Makefile$|configure$|build.xml} ;#|INSTALL
-append tmp "|index.html?"; # HTML
-set EQ(NOTABLE) $tmp
-array set EQ {
- WRAPPER {\.(gz|Z|bz2|uu|hqx)$}
- AUTOBKUP {(.*?)(\.~\d+\.\d+\.)?~$}
- DEV {(?i)notes|^todo|changelog}
- DEVDIR {^lib$|^src|(^|[ _-])test|jar|dump}
- MANDIR {/man/man[^/]+$} MANPAGE {^.+\.[1-9oln][^/]*$}
-}
-
-array set STYLE {
- group ";01;35;4" file "" dir ";34" special ";36" link ";35"
- notable ";7" autosearch ";36"
- recent,file ";31" recent,dir ";31" relrec,file ";33" relrec,dir ";33"
- relsize ";36"
- warning ";1;01;31" pre ";36" post ""
- log ";4" OFF ";0"
-}
-
-# 0 means off, else some value (maybe useful, not necessarily 1)
-# filter - data - presentation - control - ls options
-array set SWITCH {
- version "v1.1.3 of February 19, 2012" site "http://www.PracticalThought.com/"
- ignore 3 only 0
- notable 1 autosearch {\W(urgent|password|pw|live)\W} relsize 5 relchange 10 relread 10 atime 0 vc 1
- group 1 grouptitle 1 series 5 prefix 0 shorten 10 nosfx 0 title 0 maxcol 0 summary 2
- startup "~/.sl.tcl" expando 2 log 0
- F 1 1 0 B {~$|^#.*#$|^\.#}
-}
-set SWITCH(abschange) [expr $TIME(DAY)*2]
-set SWITCH(TAP) [file exists "~/prj/Multivalent"] ;# experimental/aggressive
-
-
-# i18n for words shown every time (but not for error messages)
-foreach word {
- file files directory directories ignored
- second minute hour day month year
-} { set I18N($word) $word }
-foreach {key val} {
- ... '
- pre0 " "
-
- type-file "" type-directory "/" type-link "->" type-link-external "-->" type-special "_"
-
- warn-link-broken "X" warn-empty "0" warn-single "1"
- warn-box "!"
- warn-link-hard-multiple "<-"
- warn-bit-setUID "u" warn-bit-setGID "g" warn-bit-sticky "s" warn-world-writable "W"
- warn-not-readable-by-user "r" warn-dir-not-searchable-by-user "x"
- warn-vc-need "^" warn-vc-stale "v" warn-vc-other "v"
-} { set I18N($key) $val }
-
-# mount points of external drives (that might spin down and would be slow spin up)
-set SWITCH(mounts) {/Volumes}
-set SWITCH(encoding) [encoding system]
-encoding system identity
-
-
-# control
-proc sl {dir {level 0}} {
- global SWITCH
- # filenames have different encodings: UTF-8, ISO8859-1, UTF-16 NFD (on OS X)
- # we need both raw name for file stat and cooked name for display
- encoding system $SWITCH(encoding); set cooked [glob -nocomplain -directory $dir -- "*" ".*"]
- encoding system identity; set raw [glob -nocomplain -directory $dir -- "*" ".*"]
- # leave encoding as identity for file stat
- sl2 $dir $raw $cooked $level
-}
-
-proc sl2 {dir raw cooked level} {
- if {[llength $raw]==0} return
- global SWITCH GROUP G2F EQ STYLE LOG
- set LOG {}; array set stats {series 0 pfx 0 pfxch 0}
-
- set f [file normalize ~]; file stat $f home; set home(at) [string equal $f [file normalize $dir]]
-
- # 1. build up data record for each file
- log "[color collect log] [llength $raw]"
- set fcnt 0; set dcnt 0; set igcnt 0; set igl {}
- set totalsize 0; set totaldir 0; set dotcnt 0
- set l {}
- foreach f $raw show $cooked {
- set tail [file tail $f]; if {"."==$tail || ".."==$tail} continue
- set show [file tail $show]
-
- file lstat $f stat ;# sometimes we care if it's a link and other times not
- set type $stat(type); set typec ""
- set size 0; set mtime $stat(mtime); set atime $stat(atime)
- lassign [groupbyname $dir $show $type] group iglevel ts t sfx
-
- set warning ""
- if {[regexp $EQ(AUTOBKUP) $show all base] && [file join $dir $base] ni $cooked} {append warning [l10n warn-link-broken] ;# abandoned backup (emacs~, CVS.~<ver>.~)
- } elseif {$SWITCH(ignore) >= $iglevel} {
- if {$iglevel >= 3} {lappend igl $show}
- if {$iglevel==2} {incr dotcnt} else {incr igcnt}
- continue
- }
-
- set style ""; set pre " "; set post ""
-
- # a. general
- if {($stat(mode) & 04000) != 0} {append warning [l10n warn-bit-setUID]}
- if {($stat(mode) & 02000) != 0} {append warning [l10n warn-bit-setGID]}
- if {($stat(mode) & 01000) != 0} {append warning [l10n warn-bit-sticky]}
- append post [peculiar $stat(mode) $type]
- if {"link"==$type} {set typec [l10n type-link]; set style $STYLE(link)}
- if {$SWITCH(TAP) && $home(at) && $stat(uid) != $home(uid) && $stat(nlink)==1} {append post " [color owner warning post]=" [uid2name $stat(uid)]} ;# sometimes root
-
- # b. by type
- if {"link"==$type && [external $dir $f]} {
- # if symbolic link to another disk, don't stat b/c may be very slow (spin up drive or network)
- # in UNIX can mount volumes anywhere just like directory, so go with OS X convention of /Volumes
- set typec [l10n type-link-external]
- # assume dir if not suffix? test on "[file link $f]"?
- if {$sfx==""} {set style $STYLE(dir); set group dir; incr dcnt} else {incr fcnt}
- } elseif {![file exists $f]} {
- append warning [l10n warn-link-broken]
-
- } elseif {[file isfile $f]} {
- set typec [l10n type-file]
- set style $STYLE(file)
- if {[file executable $f]} {
- if {"other"==$group} {set group app}
- set warning "*$warning" ;#append post "*"
- }
-
- if {$type!="link"} {
- if {![file readable $f]} {append warning [l10n warn-not-readable-by-user]}
- set size $stat(size)
- if {$size==0} {
- set attrs [file attributes $f] ;# we don't use "file attributes $f -rsrclength" b/c error on non-Mac
- if {[set x [lsearch -exact $attrs -rsrclength]] >= 0} {set size [lindex $attrs $x+1]}
- }
- incr totalsize $size
- if {$size==0} {append warning [l10n warn-empty]}
- if {$stat(nlink) > 1} {
- append warning [l10n warn-link-hard-multiple] ;# or "$stat(nlink)", which is distinct from '0'-length file and '1' file in dir, but less mnemonic
- #append post $stat(ino)
- }
-
- if {".nib"==$sfx && $size==232960} {set group A2} ;# special cases
- if {".doc"==$sfx && $size < 22000} {set group doc}
- }
- incr fcnt
-
- } elseif {[file isdirectory $f]} {
- #set sfx ".directory" ?
- set content {}
- set fread [file readable $f]
- if {$fread} {
- set content [glob -nocomplain -directory $f -tails -- "*"]
- # try to reset atime -- treat read above as metadata read just like stat()
- set touchfmt [clock format $stat(atime) -format "%Y%m%d%H%M.%S"] ;# [[CC]YY]MMDDhhmm[.SS]
- catch { exec touch -f -a -t $touchfmt $f } ;# changes ctime, but we value atime over ctime
- }
- lassign [classifydir $f $show $content] group size
- set style $STYLE(dir)
-
- if {"link"==$type} {
- set size 0
- } else {
- set typec [l10n type-directory]
- incr totaldir [expr $size]
- if {!$fread} {append warning [l10n warn-not-readable-by-user]
- } elseif {$size==0} {append warning [l10n warn-empty]
- } elseif {$size==1 && [file exists "$f/Contents/Info.plist"]} { ;# "*.app" not reliable
- #set group app -- in /Applications don't split app vs dir-w/app and other, just avoid warning due to single directory
- } elseif {0 && $size==1} {set show [file join $show [lindex $content 0]] ;# inline singleton file, want to recurse here to a "proc maketuple {file}", probably make name too long as dirname + filename
- } elseif {$size==1} {append warning [l10n warn-single]
- } else {
- append post $size
- #if {$stat(nlink) < [llength $content]} {append warning "K"} ;# on UFS nlink = . + .. + #files, but on HFS+ external often wrong -- but in HFS+? no aux file for hard links?
- }
-
- if {($stat(mode) & 2) != 0} {append warning [l10n warn-world-writable]}
- if {![file executable $f]} {append warning [l10n warn-dir-not-searchable-by-user]}
- }
- incr dcnt
- } else {
- set group "special"; set style $STYLE(special); set typec [l10n type-special]
- incr fcnt
- }
-
- # c. appearance
- set display $show ;# default to faithful so no surprises
- set sortkey [normalizekey $t $sfx]
- if {$SWITCH(title)} {
- set display $t ;# "title" implies strip wrapper for every group
- if {"title" in $G2F($group)} {set display [prettyname $display $type $group]}
- # if chop suffix may get dups, which are zapped in lsort -unique
- if {$SWITCH(nosfx) && "nosfx" in $G2F($group) && "file"==$type} {set sortkey [file rootname $sortkey]
- } else {append display $sfx}
- }
- if {$SWITCH(F)} {append display $typec}
- if {$SWITCH(notable) && [regexp $EQ(NOTABLE) $ts]} {append style $STYLE(notable)}
-
- # 0 1 2 3 4 5 6 7 8 9 10 11 12 ... could use a struct here
- lappend l [list $tail $sfx $type $group $sortkey $style $pre $display $warning $post $size $mtime $atime]
- }
- if {[llength $igl]>0} {log "ignored ($igl)"}
-
- # comparison w/ls after ignore and before reindeer games
- set maxlen 0; set cnt [expr $fcnt+$dcnt+[llength $igl]]
- foreach tuple $l { set len [displaylen [lindex $tuple 0]]; if {$len > $maxlen} {set maxlen $len} }
- global TERMINFO
- set cols [expr $TERMINFO(width)/($maxlen+1+1)]; if {$cols==0} {set cols 1} ;# maxlen + 1 for trailing char + 1 for gap
- log "cf ls -B $TERMINFO(width)/($maxlen+1+1) for $cnt = ${cols}x[iceil $cnt $cols]"
-
-
- # 2. mark up data
- log "[color mark log] [llength $l]"
- if {$SWITCH(vc)} { foreach p [info commands "vc-*"] {set l [$p $dir $l]} }
- set l [relative $l]
-
- set datalen 0; set tuplelen [llength [lindex $l 0]]; set reported 0
- foreach tuple $l {
- if {[catch {set user [perfile $dir $tuple]} info]} {
- if {!$reported} {puts stderr "$SWITCH(startup): $info"; set reported 1}
- } elseif {[llength $user] == 0} { ;# NOT [lindex $user 2]=="ignore" b/c show orphaned ~ files
- } elseif {[llength $user] != $tuplelen} {
- if {!$reported} {puts stderr "$SWITCH(startup) returned bad list: $user"; set reported 1}
- } elseif {$SWITCH(only) && ![distinctive $user]} {
- } else {
- #puts "[lindex $user 0] -> [lindex $user 4]"
- lappend ga([lindex $user 3]) $user
- incr datalen
- }
- }
- if {$datalen == 0} return ;# possible after user processing and -only
-
-
- # 3. display
- # a. prepare display list: sort, groups
- # lsort doesn't support a secondary key (we need group then dictionary within group), so custom sort
- set l {}; set lastgroup "dir"; set gtitcnt 0
- foreach {g flags ignore} $GROUP {
- if {![info exists ga($g)]} continue
- set gl $ga($g)
-
- # "lsort command uses the merge-sort algorithm which is a stable sort" -- hooray!
- set sortl [lsort -index 4 -dictionary -unique $gl] ;# only sort, or sets order for elements that are equal in subsequent sort
- if {[set by [lsearch -glob -inline $flags "by*"]] != ""} {
- if {"bysuffix"==$by} {set sortl [lsort -index 1 -ascii $sortl]
- } else {set sortl [lsort -index 0 -command $by $sortl]}
- }
-
- # used to make series here, but better to treat series as a unit for proc relative
- if {$SWITCH(series) && [llength $sortl] >= $SWITCH(series)} {lassign [series $sortl] sortl cnt; incr stats(series) $cnt} ;# series before prefix
- if {$SWITCH(prefix) && "prefix" in $flags} {lassign [prefix $sortl] sortl cnt1 cnt2; incr stats(pfx) $cnt1; incr stats(pfxch) $cnt2}
-
- if {$SWITCH(grouptitle) && $g != $lastgroup} {
- lappend l [list "" "" "GROUP" "" "" $STYLE(group) " " $g "" "" 0 0 0] ;# "$name ([llength $ga($g)])" like dir? didn't like it
- set lastgroup $g
- incr gtitcnt
- }
-
- set l [concat $l $sortl]
- }
- if {$stats(series) > 0} {log "series -$stats(series)"}
- if {$stats(pfx) > 0} {log "prefix $stats(pfx) ($stats(pfxch) chars)"}
- if {!$SWITCH(group)} {set l [lsort -index 4 -dictionary $l]}
-
-
- # b. display: table, summary line, log
- log "[color display log] [llength $l]"
- column $l $gtitcnt
-
- if {$SWITCH(summary) && $datalen >= $SWITCH(summary)} {
- puts -nonewline " [plural $fcnt file files]"
- if {$fcnt>0} {puts -nonewline " ([kmg $totalsize OFF])"}
- if {$dotcnt > 0} {puts -nonewline " + [plural $dotcnt dotfile dotfiles [color . warning]]"}
- if {$igcnt>0} {puts -nonewline " + [plural $igcnt ignored ignored]"}
- if {$dcnt>0} {puts -nonewline ", [plural $dcnt directory directories] ([plural $totaldir file files])"}
- #if {$dir!="/"} {puts -nonewline ", .. ([plural [llength [glob $dir/../*]] file files])"}
- puts ""
- }
-
- if {$SWITCH(log) && [llength $LOG] > 0} {puts "log: [join $LOG {, }]"}
-
-
- if {$SWITCH(expando) && $fcnt<=$SWITCH(expando) && $dcnt==1 && $level < 10} {
- foreach tuple $l {
- if {"directory"==[lindex $tuple 2] && [lindex $tuple 10]>0} { ;# && [lindex $tuple 10] < ? ... this is in place of original anemic dir
- set subdir [lindex $tuple 0]
- puts [format "\nautoexpand [color %s/ dir]:" $subdir]
- sl [file join $dir $subdir] [expr $level+1]
- break
- }
- }
- }
-}
-
-
-
-#
-# organization: grouping
-#
-
-proc groupbyname {dir tsw type} {
- global EQ SWITCH
-
- # distinguish types from wrappers, so doc.txt and photo.jpg are different groups, but doc.txt and doc2.txt.gz are same group
- #if [catch { set tail [file tail $f] }] {return "other"} ;# "home.insightbb.com/~fourcolorheros" as a path
- if {[regexp -nocase $EQ(WRAPPER) $tsw]} {set ts [file rootname $tsw]} else {set ts $tsw}
- if {"file"==$type && [set t [file rootname $ts]]!=""} {set sfx [string tolower [file extension $ts]]} else {set t $ts; set sfx ""}
-
- if {[info exists EQ($ts)]} {set g $EQ($ts) ;# can't beat exact
- } elseif {[regexp $SWITCH(B) $ts]} {set g ignore ;# regex take precedence over suffix since reclassifying to more specific, ignore first
- } elseif {"."==[string index $ts 0]} {set g dotfile
- } elseif {[regexp $EQ(DEV) $ts] && ($sfx=="" || $sfx==".txt")} {set g dev
- } elseif {[regexp $EQ(MANDIR) $dir] && [regexp $EQ(MANPAGE) $ts]} {set g doc
- } elseif {[info exists EQ($sfx)]} {set g $EQ($sfx)
- } else {set g other}
-
- if {"-"==[string index $g 0]} { set g [string range $g 1 end]; set iglevel 3
- } elseif {"dotfile" == $g} { set iglevel 2
- } elseif {"ignore" == $g} { set iglevel 1
- } else { set iglevel 4 }
-
- return [list $g $iglevel $ts $t $sfx]
-}
-
-proc external {dir f} {
- global SWITCH
- set link [file link $f]
- if {[string equal -length [string length $dir] $dir $link]} { return 0 }
- foreach d $SWITCH(mounts) {
- if {[string equal -length [string length $d] $d $link]} { return 1 }
- }
- return 0
-}
-
-proc classifydir {f show l} {
- global EQ GROUP SWITCH
- set MAXEXT [string length ".torrent"]
- # 1. by content
- # a. counts
- foreach {g ignore1 ignore2} $GROUP {set cc($g) 0}
- set llen 0; set softignore 0
- foreach c $l {
- lassign [groupbyname $f $c "file"] g iglevel ts t sfx
- if {$iglevel <= 2} continue elseif {$iglevel==3} {incr softignore}
- if {$g=="other"} { ;# guess at dir w/o stat (which would be slow on sometimes 1000s of files)
- set extlen [string length $sfx]
- if {$extlen==0 || $extlen > $MAXEXT} {set g dir}
- }
- incr cc($g); incr llen
- }
-
- # b. analyze
- set known [expr $llen - $cc(other)] ;# within types that we can identify (though id by suffix is weak)
- set max 0; set maxg "dir"
- unset cc(other) cc(A2) cc(ignore)
- foreach g [array names cc] {
- set len $cc($g)
- if {$len > $max} {set max $len; set maxg $g}
- }
- set relen 0; foreach c {video audio image WWW} {incr relen $cc($c)}
-
- # c. reclassify
- set g "dir"
-#puts "[array names cc] $cc(video) + $cc(audio) + $cc(image) + $cc(WWW) > $llen/2"
- if {"."==[string index $show 0]} {set g dotfile
- } elseif {[lsearch -nocase -glob $l "VIDEO_TS*"] >= 0} {set g video ;# VIDEO_TS/ or VIDEO_TS.VOB
- } elseif {$cc(dir) >= 3} {
- # hierarchy
- } elseif {$relen>0 && ($relen >= $llen/2 || $llen<=4)} { ;# media special case
- if {$cc(video) > 0} {set g video
- } elseif {$cc(audio) > $cc(image)} {set g audio
- } elseif {$cc(WWW) >= 2 || ($cc(WWW)==1 && $relen<=2) || ($cc(WWW)>=1 && [lsearch -nocase -glob $l "index.htm*"]>=0)} {
- #set g WWW -- keep as a group so not image/, but don't reclassify b/c gets mixed in with other .html in site
- } else {set g image}
- } elseif {$max >= [expr $known/2]} { ;# other groups
- if {$maxg!="WWW"} {set g $maxg}
- }
-
- # 2. by name
- if {[regexp $EQ(DEVDIR) $show]} {set g dev}
- # Xelseif {"doc"==$show || "man"==$show} {set g doc} => by content
-
-#puts "$show -> $g $cc($g) of $known/$llen"
- set size [expr $llen-$softignore]; if {$SWITCH(TAP) && $g!="dir" && 0 < $cc($g)&&$cc($g) < $size} {set size "$cc($g)+[expr $size-$cc($g)]"}
- return [list $g $size]
-}
-
-proc normalizekey {t sfx} {
- # Xset s [string tolower $s] => no, some sorts case sensitive
- set s $t
- if {[regexp {^(\.)} $s all pfx]} {set s [string range $s [string length $pfx] end]} {set pfx ""}
- regsub -all {#(\d)} $s {\1} s; ;# "#<number>" vs plain "<number>"
- regsub -all {(\D)(\d+)} $s {\1~\2} s ;# if compare something w/number vs something without put number afterward, because first movie in series not given a number
- regsub -all {[-\. _\t]+} $s { } s ;# normalize word separators into single space -- maybe all punctuation?
- regsub -all -nocase {(^| )(the|a|an) } $s {\1} s ;# "a" and "an" may be too aggressive, but try it and let experience guide
-
- if {$s==""} {set s $t} else {set s "$pfx$s$sfx"} ;# oops, we were too aggressive
-#puts "$t -> $s"
- return $s
-}
-
-
-
-#
-# data: relatively recent, relatively large, version control
-#
-
-proc relative {l} {
- global STYLE TIME SWITCH
-
- # recent, both absolute and relative
- set abscutoff [expr $TIME(now)-$SWITCH(abschange)]
- set sizel {}; set mtimel {}; set atimel {}
- foreach tuple $l {
- lappend sizel [expr [lindex $tuple 10]] ;# yeah compares sizes of files and dirs, but works out OK
- lappend mtimel [lindex $tuple 11]
- if {[lindex $tuple 2]!="directory"} {lappend atimel [lindex $tuple 12]}
- }
- log "NOW = $TIME(now)"
- lassign [cutoff relsize $sizel 1] cutoffsize repz
- lassign [cutoff relchange $mtimel [expr 5*$TIME(MINUTE)]] cutoffm repm ;# e.g., time to rip a CD
- lassign [cutoff relread $atimel [expr 5*$TIME(MINUTE)]] cutoffa repa
-
- set l2 {}
- foreach tuple $l {
- lassign $tuple tail sfx type group sortkey style pre display warning post size mtime atime; set size [expr $size]
- #if {$size > $cutoffsize} {set style "[lindex $tuple 5];1"}
- #if {$mtime >= $abscutoff} {set pre " " ;# # other chars like "*" and '^" blend in too much, colorize too much
- #} elseif {$mtime > $cutoffm} {set pre "."} ;# ">" so run of same across border ignores all (prefer few to too many)
- # bold visually stronger than indent and recent more important than largest, so bold on recent... but bold unreliable?
- # often recent also notable (index.html), bold dir blend in with groups
- set sub [expr {$type=="directory"?"dir":"file"}]
- if {$SWITCH(relsize) && ($size > $cutoffsize || ($size==$cutoffsize && $repz))} {
- set pre " " ;# indent, could also outdent if single space char
- if {"file"==$type} {append post " " [kmg $size post]} ;# dir already has size
- set repz 0 ;# choose only one as the representative
- }
-
- if {$SWITCH(abschange) && $mtime >= $abscutoff} {
- append style $STYLE(recent,$sub)
- if {$mtime > $cutoffm || $TIME(now)-$mtime < $TIME(HOUR)} {append post " <" [ago $mtime]; set repm 0}
- } elseif {$SWITCH(relchange) && ($mtime > $cutoffm || ($mtime==$cutoffm && $repm))} {
- append style $STYLE(relrec,$sub); append post " <" [ago $mtime]; set repm 0
- }
-
- # can't do absolute (last day, say) due to OS X Spotlight, access since last mod, not dir b/c we do a glob our own damn selves
- # Spotlight: no benefit b/c continuously touching and not resetting, but doesn't cause bad results b/c touches everything so no few most recent and if <=4 files we don't mark anyhow
- if {"directory"==$type} {
- # don't do dirs -- always being touched
- } elseif {$SWITCH(atime) > 0} { ;# if explicitly turned on absolute, don't confuse with relative
- if {$TIME(now) - $atime <= $SWITCH(atime)} {
- set pre "->[string range $pre 2 end]"; append post " -" [ago $atime]; set repa 0 ;# "=>" so different than relative? no, won't have both kinds at the same time, string range in case also relsize
- }
- } elseif {$SWITCH(relread) && ($atime > $cutoffa || ($atime==$cutoffa && $repa)) && $atime > $mtime + $TIME(MINUTE)} {
- set pre "->[string range $pre 2 end]"; append post " -" [ago $atime]; set repa 0
- }
-
- lappend l2 [list $tail $sfx $type $group $sortkey $style $pre $display $warning $post $size $mtime $atime]
- }
- return $l2
-}
-
-proc cutoff {what l by} {
- global SWITCH
- set pct $SWITCH($what)
- set llen [llength $l]; if {$llen==0 || $pct==0} return elseif {$llen==1} {return [list [lindex $l 0] 1]}
- set l [lsort -command bybignum -decreasing $l]; set max [lindex $l 0]
- set cut [expr (($llen*$pct)/100)+1]; if {$cut>$pct} {set cut $pct}
- set val [lindex $l $cut]; set rep 0
- if {$llen > 4} { ;# have to differ by a meaningful amount -- if all downloaded within a couple minutes a year ago, that's just random
- set median [expr $llen/2]
- set beat [expr [lindex $l $median] + $by]
- if {$val < $beat} {set val $beat}
- }
- if {$max <= $val} {
- set val $max
- if {!$SWITCH(only)} {set rep 1; log "$what rep=$val"} ;# single representative value to characterize, but if -only prefer nothing
- } else {log "$what > $val @ $cut"}
- return [list $val $rep]
-}
-
-
-proc peculiar {mode type} {
- # 1. anything peculiar?
- # zots
- if {($mode & 0777) == 0} { return [color "---------" warning post] }
-
- set h 0
- set powner [expr ($mode>>6)&0x7]; set pgroup [expr ($mode>>3)&0x7]; set pothers [expr $mode&0x7]
-
- # owner/w/x but no r
- if {($powner&4)==0} {set h [expr $h|(4<<6)]}
- if {(1 <= $pgroup&&$pgroup <= 3)} {set h [expr $h|(4<<3)]}
- if {(1 <= $pothers&&$pothers <= 3)} {set h [expr $h|4]}
-
- # more important but lower perm
- set p [expr $powner & $pgroup]; if {$p != $pgroup} {set h [expr $h|(($pgroup - $p)<<6)]}
- set p [expr $powner & $pothers]; if {$p != $pothers} {set h [expr $h|(($pothers - $p)<<6)]}
- set p [expr $pgroup & $pothers]; if {$p != $pothers} {set h [expr $h|(($pothers - $p)<<3)]}
-
- # directory: r,w but not x
- if {"directory"==$type} {
- if {($powner&6)!=0 && ($powner&1)==0} {set h [expr $h|(1<<6)]}
- if {($pgroup&6)!=0 && ($pgroup&1)==0} {set h [expr $h|(1<<3)]}
- if {($pothers&6)!=0 && ($pothers&1)==0} {set h [expr $h|1]}
- }
- if {$h==0} {return ""}
-
- # 2. if so, then highlight that bit
- set post " "; set rwx [perm $mode]
- for {set bit 8} {$bit>=0} {incr bit -1} {
- set c [string index $rwx [expr 8-$bit]]
- if {((1<<$bit)&$h) != 0} {append post [color $c warning post]
- } else {append post $c}
- }
- return $post
-}
-
-
-
-# version control
-
-proc vc-rcs {dir l} {
- set rep "$dir/RCS"; if {![file readable $rep]} {return $l}
-
- set l2 {}
- foreach tuple $l {
- lassign $tuple tail sfx type group sortkey style pre display warning post size mtime atime
-
- set fv [file join $rep "$tail,v"]
- if {[file exists $fv] && [file mtime $fv] < $mtime} {append warning [l10n warn-vc-need]}
- # RCS repository is local, so theoretically can't have stale local copy
-
- lappend l2 [list $tail $sfx $type $group $sortkey $style $pre $display $warning $post $size $mtime $atime]
- }
- return $l2
-}
-
-proc vc-cvs {dir l} {
- global TIME
-
- set cvs "$dir/CVS"; if {![file readable $cvs] || ![file readable $cvs/Root]} {return $l}
-
- set fid [open "$cvs/Root"]; set root [read -nonewline $fid]; close $fid
- set fid [open "$cvs/Repository"]; set rep [read -nonewline $fid]; close $fid
- set fid [open "$cvs/Entries"]; set entries [read $fid]; close $fid
- set data {}
- foreach line [split $entries "\n"] {
- lassign [split $line "/"] pre name ver date options tagdate
- lappend data [list $name $ver $date]
- }
- set rep [file join $root $rep]
- if {![file exists $rep]} {puts [color "can't find CVS repository: $rep" warning]} ;# maybe even blink
-
- set l2 {}
- foreach tuple $l {
- lassign $tuple tail sfx type group sortkey style pre display warning post size mtime atime
-
- set rec [lsearch -exact -index 0 -inline $data $tail]
- if {$rec==""} {
- # not under version control
- } elseif {[regexp {^[-0]} [set civer [lindex $rec 1]]]} {
- # locally added (version 0) or removed (negative version number), not yet checked in
- append warning [l10n warn-vc-need]
- } else {
- # repository updated, local stale
- set citime [clock scan [lindex $rec 2] -gmt true]
- if {![file exists [set fv [file join $rep "$tail,v"]]]} {
- #append warning [l10n warn-link-broken] -- maybe not local
- } elseif {[file mtime $fv] > $citime+$TIME(epsilon)} { ;# preliminary check to reduce #files that have to read
- set fid [open $fv]; set head [read $fid 4096]; close $fid
- if {[regexp {head\s[(\d\.)]+;} all repver] && $civer != $repver} {
- append warning [l10n warn-vc-stale]
- }
- }
- # local changed since last
- if {$mtime > $citime+$TIME(epsilon)} {
- append warning [l10n warn-vc-need]
- }
- }
-
- lappend l2 [list $tail $sfx $type $group $sortkey $style $pre $display $warning $post $size $mtime $atime]
- }
- return $l2
-}
-
-
-
-#
-# display: filter, series, prefix, layout
-#
-
-
-proc distinctive {tuple} {
- global STYLE
- lassign $tuple tail sfx type group sortkey style pre display warning post size mtime atime
- set show 0
- if {$type=="file"} {
- if {$style!=$STYLE(file) || $pre!=" " || ($warning!="" && $warning!="*" && $warning!="*<-") || $post!=""} {set show 1}
- } elseif {$type=="directory"} {
- if {$style!=$STYLE(dir) || $pre!=" " || ($warning!="" && $warning!="1") || [llength $post]>1} {set show 1}
- }
- return $show
-}
-
-
-proc prettyname {d type group} {
- # SWITCH(title) is not on by default because you have type the exact name in,
- # but it's OK if you use file completion and are aware that the filename display may not be exactly the same.
-
- regsub -all {([a-z])([A-Z])} $d {\1 \2} d ;# camelCase -> camel Case
- regsub -all {[\._ ]+} $d { } d
-
- return $d
-}
-
-
-# requires list sorted in dictionary order
-proc series {l} {
- global SWITCH
- set KEY 0 ;# 0=tail, 4=sortkey, 7=displayname
- set RX_RELZ { [\.\d]{1,4}[BKMGTPEZY]}
- set seriesl {}; set cnt 0
- for {set i 0; set imax [llength $l]} {$i<$imax} {} {
- set tuple [lindex $l $i]
- set relz 0
- for {set j $i} {$j<$imax} {incr j} {
- set tuple2 [lindex $l $j]
- if {[lindex $tuple2 2]=="directory"} {break ;# dir already kind of a series
- } elseif {$j==$i} {
- } elseif {![serieseq [lindex $t $KEY] [lindex $tuple2 $KEY]]} {break
- } elseif {[lindex $tuple2 8] != ""} {break ;# keep warnings
- } elseif {[set post2 [lindex $tuple2 9]] != ""} {
- if {[regexp $RX_RELZ $post2 all] && [string length $post2]==[string length $all]} {incr relz} else break
- }
- set t $tuple2 ;# progressive
- }
- set slen [expr $j-$i]
- if {$slen >= 3} {
- set post [lindex $tuple 9]
- if {$relz && ![regexp $RX_RELZ $post]} {append post " " [kmg [lindex $tuple 10] post]} ;# maybe also do this if length of series >100 or would qualify by relsize percentage (set zlen [expr 100/$SWITCH(relsize)]), size of first tuple is representative but maybe instead show largest in series or sum
- append post "..." $slen
- lset tuple 9 $post
- set i $j ;# j at first mismatch, which is next start
- incr cnt [expr $slen-1]
- } else {incr i}
- lappend seriesl $tuple
- }
- return [list $seriesl $cnt]
-}
-
-proc serieseq {f1 f2} {
- set len [string length $f1]
- if {[string length $f2] != $len} {return 0}
-
- set i 0
- # if [string length [file rootname]] >= 10 then assume must be a common prefix
- if {$len >= 6+4} { ;# [string length "999999"]==6 + 4 for suffix (though some groups have no suffix)
- for {} {$i < 4} {incr i} {
- if {[string index $f1 $i] != [string index $f2 $i]} { return 0 }
- }
- }
-
- set alphaseries 0
- for {} {$i<$len} {incr i} {
- set c1 [string index $f1 $i]; set c2 [string index $f2 $i]
- if {$c1==$c2} {
- } elseif {[string is integer $c1] && [string is integer $c2]} {
- } elseif {$alphaseries} {break
- } elseif {[string is alpha $c1] && [string is alpha $c2]} {
- #scan $c1 "%c" v1; scan $c2 "%c" v2
- #X if {$v1+1==$v2} {set alphaseries 1} else {return 0} => too strict
- set alphaseries 1
- } elseif {[string is integer $c1] && [string is alpha $c2]} { ;# 0..9 over to a..z
- set alphaseries 1
- } else break
- }
- return [expr $i==$len]
-}
-
-# requires list in display order
-proc prefix {l} {
- global SWITCH
- set l2 {}; set lastd ""; set lastlen 0
- set cnt 0; set cntch 0
- foreach tuple $l {
- set d [lindex $tuple 7]; set len [string length $d]
- set pfx 0
- set i 0; set imax [expr $lastlen<$len?$lastlen:$len]
- for {set lastc ""} {$i<$imax} {incr i; set lastc $c2} {
- set c1 [string index $lastd $i]; set c2 [string index $d $i]
- # breakpoints -- computed on text to be elided not on base
- if {$c2==" "} {set pfx [expr $i+1] ;# not A-Za-z0-9
- } elseif {[string is punct $c2] || $c2=="\u001b"} {set pfx $i ;# not A-Za-z0-9
- } elseif {$lastc==""} {
- } elseif {[string is lower $lastc] && [string is upper $c2]} {set pfx $i ;# camelCase
- } elseif {[string is alpha $lastc] && [string is digit $c2]} {set pfx $i ;# letter->number
- }
- # elseif >=3 chars x >= 4 names ?
-
- if {$c1 != $c2} break
- }
- if {$i==$imax && ![string is digit $lastc]} {set pfx $i}
- if {$pfx >= $SWITCH(prefix)} {
- lset tuple 7 "[format {%*s} $pfx { }][string range $d $pfx end]" ;# $pfx-1 spaces and ~ not appealing
- incr cnt; incr cntch $pfx
- }
- lappend l2 $tuple
- set lastd $d; set lastlen $len
- }
- return [list $l2 $cnt $cntch]
-}
-
-
-# sorting comparators
-proc byascii {f1 f2} { return [string compare $f1 $f2] }
-proc by1 {f1 f2} { return [string compare -length 1 $f1 $f2] }
-# would like to use lsort -integer but handle a file size larger than 4GB on 32-bit machines, (return non-bignum result!)
-proc bybignum {n1 n2} { return [expr {$n1<$n2?-1:[expr $n1==$n2?0:1]}] }
-
-
-# "[l10n file]" vs "$l10n(file)" -- same number of chars
-proc l10n {word} {
- global I18N
- if {[info exists I18N($word)]} {set word $I18N($word)}
- return $word
-}
-
-
-set COLSEP 1 ;# min space between columns, but effectively +2=3 from 'pre', almost always appears to be much more due to different name lengths and markings. ls is 1
-set NAMEMIN 40 ;# so 2 cols on 80-col screen
-
-proc column {l gtitcnt} {
- global TERMINFO NAMEMIN COLSEP SWITCH
-
- # 1. compute widths needed by tuples, with max that allows >=COLSMIN
- set llen [llength $l]
- set width [expr $TERMINFO(width)-1] ;# right margin (get left margin for free with pre==" ")
- if {$SWITCH(1)} {set width 1}
- set colsmin [expr $width / $NAMEMIN]
- if {$colsmin <= 1 || !$SWITCH(shorten) || $llen < $SWITCH(shorten)} {set namemax 1000000 ;# if few to show give them what they want
- } else {set namemax [expr ($width - $COLSEP * ($colsmin-1)) / $colsmin]}
-
- set ll {} ;# "ll" = list + lengths
- set maxlen 0
- foreach tuple $l {
- lassign $tuple tail sfx type group sortkey style pre display warning post size mtime atime
- set len [expr [displaylen $display]+[string length $pre]+[displaylen $post]+[string length $warning]]
- if {$len > $maxlen} {set maxlen $len}
- if {$len > $namemax} {set lenx $namemax} {set lenx $len}
- lappend ll [list $tail $sfx $type $group $sortkey $style $pre $display $warning $post $size $mtime $atime $len $lenx]
- }
- if {$maxlen > $namemax} {log "|cell| $maxlen->$namemax"}
- set maxw [expr $maxlen<$namemax?$maxlen:$namemax]; set colsuni [expr $width/($maxw+$COLSEP)]; if {$colsuni==0} {set colsuni 1}
- log "uni $width/$maxw=${colsuni}x[iceil $llen $colsuni]"
-
- # 2. compute col x row, and cols widths
- # a. max out columns
- set rows $llen ;# possible that tail > screen width, in which case wraparound
- for {set c $colsuni} {1} {set c [iceil $llen [expr $r-1]]} {
- set r [iceil $llen $c]
- lassign [tailor $ll 0 $llen $r] sumx wxl
- if {$sumx > $width} { log "X[llength $wxl]x$r = $sumx"; break }
- set rows $r; if {$r==1} break
- }
-
- # b. aesthetic refinements
- if {$rows==1 && $gtitcnt>=1} {incr rows; log "rows 1->2 for [plural $gtitcnt title titles]"}
- lassign [bump $ll $rows $width] ll1 bot1; set cols1 [iceil [llength $ll1] $rows]
- log "tailored ${cols1}x$rows bot=$bot1"
- if {$SWITCH(maxcol) || $rows == 1 || $cols1 == 1} { ;# single row quite readable so keep, need a col to take away or bump into
- set ll $ll1
- } else { ;# if title at bottom then bump, swap +1 row for -1 col (easier to read, but don't drastically reshape)
- set rows2 [expr $rows+1]; lassign [bump $ll $rows2 $width] ll2 bot2; set cols2 [iceil [llength $ll2] $rows2]
- if {$bot2 < $bot1} {set ll $ll2; incr rows; log "row+1 ${cols2}x$rows2 (< bot=$bot1->$bot2)"
- } elseif {$bot2==$bot1 && $cols2+1==$cols1 && $cols2>=4} {set ll $ll2; incr rows; log "row+1 ${cols2}x$rows2 (< cols)"
- } else {set ll $ll1; log "X${cols2}x[expr $rows2] bot=$bot2"}
- }
-
- # if extra space, give to maxed out columns (only -- don't space out columns across screen like ls)
- set llen [llength $ll]; lassign [tailor $ll 0 $llen $rows] sumx wxl wl; set cols [llength $wxl] ;# final #cols based on #rows
- set slack [expr $width - $sumx]; set slack0 $slack
- for {set ate 1} {$ate} {} {
- set ate 0
- for {set i 0} {$i<$cols && $slack>0} {incr i} {
- if {[set x [lindex $wxl $i]] < [lindex $wl $i]} {lset wxl $i [expr $x+1]; incr slack -1; incr ate}
- }
- }
- if {$slack < $slack0} {log "+[expr $slack0-$slack] widths"}
-
- # if extra space, space out columns a little more (but not a lot)
- set gut [expr $cols-1]
- if {$slack >= $gut} { log "+1*$gut gutter" ;# just +1 space, not until exhaust slack
- for {set i 0} {$i<$gut} {incr i} {
- lset wxl $i [expr [lindex $wxl $i]+1]
- }
- }
-
- # if prefix on and all names in column have prefix, then close up gap
- set pfxl {}
- for {set i 0; set j 0; set pfx 1000} {$i<$llen} {incr i} {
- if {![string is space -failindex p [lindex $ll $i 7]] && $p < $pfx} {set pfx $p}
- if {$j+1==$rows || $i+1==$llen} {lappend pfxl $pfx; set j 0; set pfx 1000} else {incr j}
- }
-#puts "col pfx: $pfxl for ${cols}x$rows <= $llen"
-
- # 3. display
- if {$SWITCH(1)} {set wxl [list $namemax]}
- set shortened 0
- for {set i 0} {$i<$rows} {incr i} {
- for {set j 0; set n $i} {$n<$llen} {incr j; incr n $rows} {
- set tuple [lindex $ll $n]
- set w [lindex $wxl $j]; set hasnextcol [expr $n+$rows < $llen] ;# stricter than $j+1<$cols
- if {[llength $tuple]==0} {
- set gap [expr $w-$COLSEP]
- } else {
- lassign $tuple tail sfx type group sortkey style pre display warning post size mtime atime len lenx
- puts -nonewline [color $pre pre]
- if {$style!=""} {puts -nonewline [colorcode $style]}
-
- set availw $w
- if {$hasnextcol} {incr availw -$COLSEP} elseif {$j+1<$cols} {incr availw [lindex $wxl [expr $j+1]]} ;# spill into next column
- if {$availw >= $len} {set show $display; set gap [expr $availw-$len]
- } else {
- incr availw -[expr [string length $pre]+[displaylen $post]+[string length $warning]]
- set show [shorten $display $availw]; set gap [expr $availw-[displaylen $show]]; incr shortened
- }
- # have to do autosearch at the last second, possible that proc shorten stripped text that would have matched
- set auto $SWITCH(autosearch); if {$auto!="" && $auto!=0 && [regexp -nocase -indices $auto $show num]} {
- lassign $num n1 n2
-#puts "$show: $num -> $n1 $n2"
- set a [string range $show 0 $n1-1]; set b [string range $show $n1 $n2]; set c [string range $show $n2+1 end]
- set show "$a[color $b autosearch $style]$c"
- }
-
- #puts -nonewline [string range $show [lindex $pfxl $j] end]
- puts -nonewline $show
-
- if {$style!=""} {puts -nonewline [colorcode OFF]}
- if {$warning!=""} {puts -nonewline [color $warning warning]}
- puts -nonewline [color $post post]
- }
-
- if {$hasnextcol} {puts -nonewline [format "%-*s" [expr $gap+$COLSEP] " "]}
- }
- puts ""
- }
- if {$shortened>0} {log "$shortened names shortened"}
-}
-
-proc tailor {l i llen rows} {
- log "tailor"
- global COLSEP
- # width of cols = width of widest cell
- set sumx 0; set wl {}; set wxl {}
- for {} {$i<$llen} {incr i $rows} {
- set max 0; set maxx 0
- for {set j $i; set jmax [expr $i+$rows]} {$j<$jmax && $j<$llen} {incr j} {
- set len [lindex $l $j 13]; set lenx [lindex $l $j 14]
- if {$len > $max} {set max $len}
- if {$lenx > $maxx} {set maxx $lenx}
- }
- if {$i+$rows < $llen} {incr max $COLSEP; incr maxx $COLSEP}
- incr sumx $maxx; lappend wl $max; lappend wxl $maxx
- }
-
- # X if have extra space, give to maxed out columns => premature. Maybe could bump title, but can't b/c allowed to expand.
-
- return [list $sumx $wxl $wl] ;# #cols == [llength $wl]
-}
-
-# if group title at bottom of column, bump to next column, if we can (free cell, widths OK)
-proc bump {ll rows width} {
- set llen [llength $ll]; set bot 0
- for {set i [expr $rows-1]; set imax [expr $rows>1?$llen:0]} {$i<$imax} {incr i $rows} {
- if {"GROUP"==[lindex $ll $i 2]} {incr bot}
- }
- if {$bot==0} {return [list $ll $bot]}
-
- lassign [tailor $ll 0 $llen $rows] sumx wxl
- set bumpl {}; set botbump 0
- for {set i 0; set c 0; set r 0; set left 0} {$i<$llen} {incr i} {
- set tuple [lindex $ll $i]
- if {$r==0} {incr left [lindex $wxl $c]}
-
- if {$r+1==$rows && "GROUP"==[lindex $tuple 2]} {
- # bump?
- lassign [tailor $ll $i $llen $rows] sumx wxl2
- if {$left + $sumx <= $width} {
- lappend bumpl {}
- set wxl $wxl2; set c 0; set r 0; incr left [lindex $wxl 0]
- } else {incr botbump} ;# puts "can't: [lindex $tuple 0] $left + $sumx !<= $width"
- }
- lappend bumpl $tuple
- if {$r+1 < $rows} {incr r} else {incr c; set r 0}
- }
-
- # possible that bumping gives more titles at bottom
- # (unimplmented: edge case where first bumps OK then if stop would win)
- if {$botbump < $bot} {
- log "bot $bot->$botbump after bump @ rows=$rows"
- set ll $bumpl; set bot $botbump
- }
-
- return [list $ll $bot]
-}
-
-
-
-#
-# generally useful utility procs: color, pretty print (size, time, shorten text)
-#
-
-
-# (color info gleaned from GNU ls.c)
-# 0=end, 1=bold or brighter, 2=darker, 3=italics, 4=underline, 5=slowly blink, 6=rapidly blink, 7=rev, 8=hidden, 9=crossout
-# 3x=foreground, 4x=background, where x is 0=black, 1=red, 2=green, 3=yellow, 4=blue, 5=magenta, 6=cyan, 7=white
-set COLORIZE 1; #if {![info exists env(TERM)] || ![string match "*color*" $env(TERM)]} {set colorize 0} -- assume color
-proc color {txt {style ""} {context OFF}} {
- if {[string is space $txt]} {return $txt}
- set on [colorcode $style]; if {$on==""} {return $txt}
- set off ""; if {$context!="OFF"} {set off [colorcode OFF]}
- set reset [colorcode $context]
- return "$on$txt$off$reset"
-}
-
-proc colorcode {style} {
- global COLORIZE; if {!$COLORIZE || $style==""} {return ""}
- if {![regexp {^[;0-9]+$} $style]} {global STYLE; set style $STYLE($style)}
- return "\u001b\[[string range $style 1 end]m"
-}
-
-
-proc plural {n singular plural {pfx ""}} {
- if {$n<=0} {set txt "---"
- } elseif {$n==1} {set txt "$n $pfx[l10n $singular]" ;# set txt "a $singular" ?
- } else {set txt "$n $pfx[l10n $plural]"}
- return $txt
-}
-
-
-proc displaylen {s} {
- set len 0
- for {set i 0; set imax [string length $s]} {$i<$imax} {incr i} {
- scan [string index $s $i] "%c" c
- if {(0x300 <= $c&&$c <= 0x36f) || (0x1dc0 <= $c&&$c <= 0x1dff)} {
- # combining (accent)
- } elseif {0x1b==$c} {
- # color code
- while {[string index $s $i] != "m"} { incr i }
- } else {
- incr len
- }
- }
- return $len
-}
-
-
-set K 1024 ;# maybe you prefer 1000
-
-proc kmg {size context} {
- global K
- set SFX "BKMGTPEZY"
-
- if {$size < $K} {return "${size}B"}
- set pretty "(too big)"
- for {set po2 1.0; set i 0} {$i<5} {set po2 $next; incr i} {
- set next [expr $po2*$K]
- if {$size < $next} {
- set dsize [expr $size/$po2]
- set fmt [expr {$dsize<10? "%.1f": "%.0f"}]
- set pretty [format $fmt $dsize]; if {[string match "*.0" $pretty]} {set pretty [string range $pretty 0 end-2]} ;# e/f/g not right
- #append pretty [string index $SFX $i]
- append pretty "[color [string index $SFX $i] relsize $context]"
- break
- }
- }
- return [string trim $pretty]
-}
-
-
-proc ago {time} {
- global TIME
- set s ""; set u ""
- set age [expr $TIME(now) - $time]
- if {[expr abs($age)] < 10} {set s "NOW"
- } elseif {$age < 0} {set s "FUTURE"
- } elseif {$age < 2*$TIME(MINUTE)} {set s $age; set u "second"
- } elseif {$age < 2*$TIME(HOUR)} {set s [expr $age/$TIME(MINUTE)]; set u "minute"
- } elseif {$age < 2*$TIME(DAY)} {set s [expr $age/$TIME(HOUR)]; set u "hour"
- } elseif {$age < $TIME(YEAR)} {set s [expr $age/$TIME(DAY)]; set u "day"
- } elseif {$age < 3*$TIME(YEAR)} {set s [expr ($age+2*$TIME(MONTH))/$TIME(YEAR)]; set u "year" ;# format "%3.1f" would be too precise
- } else {set s [clock format $time -format "%Y"]}
- if {$u != ""} {append s [string index [l10n $u] 0]}
- return $s
-}
-
-
-proc perm {mode} {
- set txt ""
- for {set s 6} {$s >= 0} {incr s -3} {
- set m [expr $mode>>$s]
- append txt [expr ($m & 4)?"r":"-"]
- append txt [expr ($m & 2)?"w":"-"]
- append txt [expr ($m & 1)?"x":"-"]
- }
- return $txt
-}
-
-proc shorten {txt w} {
- set len [displaylen $txt]; if {$len <= $w} { return $txt }
-
- # throw out articles and prepositions
- regsub -nocase -all {(^|\W)(a|an|the)\W} $txt " " txt ;# can't remove from start of word b/c have to type in
- regsub -nocase -all {\W(of|from|for|in|to|and|volume|vol)\W} $txt "/" txt ;# [l10n "..."] ?
- set len [displaylen $txt]; if {$len <= $w} { return $txt }
-
- # short name: <start>...<num or long suffix>.<sfx><typec> <warning> <post>. 8 chars at end take care of yyyy.sfx
- # most important: files (start of name, numbers, suffix), dirs (start of name, numbers, end / and little more)
- # could give up a couple of chars of gutter in this case... or not now that down to 3 and ->, pass in slack? how different than w?
- global SWITCH
- set e [l10n "..."]; set elen [string length $e] ;# don't use three character "..." b/c already space constrained and may use twice
- set mid $e; set tail 3 ;# three-letter suffix not four to double count '.' for ellipsis and sfx
- if {$w <= 10} { ;# emergency mode, usually have NAMEMIN==38 so even with COLSEP/pre/post/warning don't execute very short cases (here and below)
- set tail 0
- } elseif {[regexp -indices {(\d+)} $txt all num]} {
- lassign $num n1 n2; set num [string range $txt $n1 $n2]; set numlen [string length $num]
- if {5+1+$numlen+1+$tail > $w} {
- # number too many digits, give up showing number
- } else {
- if {8+$numlen+8 <= $w} {set tail 8}
- if {$n2 <= $w-$elen-$tail-1} {
- } elseif {[set x [expr $len-$tail-$elen-$numlen-1]] <= $n1} {set tail [expr $len-($x>$n1?$x:$n1)]
- } else { ;# no overlap
- set mid "$e$num$e"
- if {$len-$tail-1 <= $n2} {set tail [expr $len-($n2+1)]} ;# don't show number and again partially when could make head longer
- }
- }
- } else {
- if {$w >= 20} {set tail 8}
- }
-
- if {$tail>0 && ([set c [string index $txt end-[expr $tail-1]]]==" " || $c==".")} {incr tail -1} ;# space char not informative, give real estate to head
- # if chop on combining accent then should +1, but I'm not going to worry about that
- set head [expr $w-[string length $mid]-$tail]
- set a [string range $txt 0 $head-1]
- if {[string last "\u001b\[" $a] > [string last "\u001b\[0m" $a]} {global STYLE; append a [color $STYLE(OFF)]}
- #if {[string index $txt $a]==" "} {incr tail}--tail may be trying to stay away from number
- return "$a$mid[string range $txt end-[expr $tail-1] end]"
-}
-
-# map numeric user id to symbolic
-proc uid2name {uid} {
- global n2scache
- if {![info exists n2scache($uid)]} {
- catch { regexp {uid=\d+\(([^)]+)\)} [exec id $uid] all sym }
- if {![info exists sym]} {set sym $uid} ;# can't resolve
- set n2scache($uid) $sym
- }
- return $n2scache($uid)
-}
-
-# integer ceiling function
-proc iceil { num den } {
- return [expr ($num + $den - 1) / ($den)]
-}
-
-
-
-#
-# execution
-#
-
-proc log {ev} {
- global LOG
- #if {$ev ni $LOG} ...
- lappend LOG $ev
-}
-
-proc commandline {argv} {
- global TERMINFO SWITCH
-
- set argi 0; set argc [llength $argv]
- for {} {$argi<$argc} {incr argi} {
- set opt [lindex $argv $argi]
- if {[string match "--?*" $opt]} {set opt [string range $opt 1 end]}
- switch -glob -- $opt {
- -only {array set SWITCH { only 1 relsize 0 series 0 prefix 0 title 0 summary 0 } }
- -atime {set SWITCH(atime) [lindex $argv [incr argi]]}
- -a {set SWITCH(ignore) 0}
- -1 {set SWITCH(1) 1}
-
- -startup {set SWITCH(startup) [lindex $argv [incr argi]]}
- -help {puts "see $SWITCH(site)"; exit 0}
- -version {puts "sl $SWITCH(version)"; exit 0}
-
- -width {set TERMINFO(width) [lindex $argv [incr argi]]}
- -log {set SWITCH(log) 1}
- -- { incr argi; break }
- -* { puts stderr "sl: no such option: $opt"; exit 1 }
- default { break }
- }
- }
- return $argi
-}
-
-proc main {argv {argi 0}} {
- set l [lrange $argv $argi end]; if {[llength $l]==0} {set l [list .]}
-
- # validate, group together files by dir
- set g(0) 1; unset g(0) ;# make 0-length array
- set dirl {}
- foreach f $l {
- if {![file exists $f]} {puts stderr "$f: no such file or directory"
- } elseif {![file readable $f]} {puts stderr "$f: not readable"
- } elseif {[file isdirectory $f]} {lappend dirl $f
- } else {lappend g([file dirname $f]) $f}
- }
- set fl [array names g]
- # OK if empty b/c all invalid
-
- # iterate
- set fcnt [expr [llength $fl]+[llength $dirl]]; set first 1
- foreach dir [lsort $fl] {
- if {!$first} {puts ""}
- if {$fcnt > 1} {puts "$dir:"}
- sl2 $dir $g($dir) $g($dir) 0 ;# no raw names
- set first 0
- }
- foreach dir $dirl {
- if {!$first} {puts ""}
- if {$fcnt > 1} {puts "$dir:"}
- sl $dir
- set first 0
- }
-}
-
-
-
-#
-# extensions: change global variables, per-file property control, redefine procs (like vc, shorten, even main)
-#
-
-proc perfile {dir tuple} { return $tuple }
-
-
-
-if {![info exists META] || $META(sl)=="run"} {
- set argi [commandline $argv]
- if {[file readable $SWITCH(startup)]} {source $SWITCH(startup)}
- main $argv $argi
-}