• This repository has been archived on 26/Apr/2024
  • Stars
    star
    215
  • Rank 179,695 (Top 4 %)
  • Language
    Shell
  • License
    MIT License
  • Created almost 4 years ago
  • Updated almost 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

file manager written in posix shell
shfm
________________________________________________________________________________

file manager written in posix shell

screenshot: https://user-images.githubusercontent.com/6799467/89270554-2b40ab00-d644-11ea-9f2b-bdabcba61a09.png


features
________________________________________________________________________________

* no dependencies other than a POSIX shell + POSIX [, printf, dd and stty ***
* tiny
* single file
* no compilation needed
* correctly handles files with funky names (newlines, etc)
* works with very small terminal sizes.
* cd on exit
* works when run in subshell $(shfm)

*** see portability notes towards bottom of README.


keybinds
________________________________________________________________________________

j - down
k - up
l - open file or directory
h - go up level
g - go to top
G - go to bottom
q - quit
: - cd to <input>
/ - search current directory <input>*
- - go to last directory
~ - go home
! - spawn shell
. - toggle hidden files
? - show help

Also supported:

down arrow  - down
up arrow    - up
left arrow  - go up level
right arrow - open file or directory

backspace   - up
enter       - open file or directory


todo
________________________________________________________________________________

- [x] sanitize filenames for display.
- [ ] print directories first (hard).
- [x] fix buggy focus after exit from inline editor.
- [ ] maybe file operations.
- [x] add / to directories.
- [x] going up directories should center entry.
- [x] abstract over sequences.
- [x] look into whether tput is feasible.


cd on exit
________________________________________________________________________________

On exit, the utility will print the path to working directory to <stdout>. To
disable this behavior, run with 'shfm >/dev/null'. Usage of this output is
rather flexible.

    # cd to directory on exit
    cd "$(shfm)"

    # store pwd in var on exit
    var=$(shfm)

    # store pwd in a file on exit
    shfm > file

For ease of use, a wrapper function can be added to your .shellrc (.bashrc, etc).

    shfm() {
        cd "$(command shfm "$@")"
    }


opener
________________________________________________________________________________

Opening files in different applications (based on mime-type or file extension)
can be achieved via an environment variable (SHFM_OPENER) set to the location of
a small external script. If unset, the default for all files is '$EDITOR' (and
if that is unset, 'vi').

The script receives a single argument, the full path to the selected file.
The opener script is also useful on the command-line. The environment variable
is set as follows.

    export SHFM_OPENER=/path/to/script

Example scripts:

    #!/bin/sh -e
    #
    # open file in application based on file extension

    case $1 in
        *.mp3|*.flac|*.wav)
            mpv --no-video "$1"
        ;;

        *.mp4|*.mkv|*.webm)
            mpv "$1"
        ;;

        *.png|*.gif|*.jpg|*.jpe|*.jpeg)
            gimp "$1"
        ;;

        *.html|*.pdf)
            firefox "$1"
        ;;

        # all other files
        *)
            "${EDITOR:=vi}" "$1"
        ;;
    esac


    #!/bin/sh -e
    #
    # open file in application based on mime-type

    mime_type=$(file -bi)

    case $mime_type in
        audio/*)
           mpv --no-video "$1"
        ;;

        video/*)
            mpv "$1"
        ;;

        image/*)
            gimp "$1"
        ;;

        text/html*|application/pdf*)
            firefox "$1"
        ;;

        text/*|)
            "${EDITOR:=vi}" "$1"
        ;;

        *)
            printf 'unknown mime-type %s\n' "$mime_type"
        ;;
    esac


portability notes
________________________________________________________________________________

* SIGWINCH and the size parameter to stty are not /yet/ POSIX (but will be).

  - https://austingroupbugs.net/view.php?id=1053
  - https://austingroupbugs.net/view.php?id=1151


* VT100/ANSI escape sequences (widely available) are used in place of tput. A
  few non-VT100 sequences /are/ needed however.

  - IL      vt102  \033[L:         upwards scroll.    (required)
  -         xterm  \033[?1049[lh]: alternate screen.  (optional)
  - DECTCEM vt520  \033[?25[lh]:   cursor visibility. (optional)

  Why avoid tput?

  POSIX only specifies three operands for tput; clear, init and reset [0]. We
  cannot rely on anything additional working across operating systems and tput
  implementations.

  Further, a tput implementation may use terminfo names (example: setaf) or
  termcap names (example: AF). We cannot blindly use tput and expect it to
  work everywhere. [1]

  We could simply follow terminfo and yell at anyone who doesn't though I'm
  also not too keen on requiring tput as a dependency as not all systems have
  it. I've found that raw VT100/VT102 sequences work widely.

  Neofetch uses them and supports a wide array of operating systems (Linux,
  IRIX, AIX, HP-UX, various BSDs, Haiku, MINIX, OpenIndiana, FreeMiNT, etc.
  YMMV

  [0] https://pubs.opengroup.org/onlinepubs/009695399/utilities/tput.html
  [1] https://invisible-island.net/ncurses/man/tput.1.html#h2-PORTABILITY


implementation details
________________________________________________________________________________

* Draws are partial!

  The file manager will only redraw what is necessary. Every line scrolled
  corresponds to three lines being redrawn. The current line (clear highlight),
  the destination line (set highlight) and the status line (update location).


* POSIX shell has no arrays.

  It does however have an argument list (used for passing command-line arguments
  to the script and when calling functions).

  Restrictions:

  - Can only have one list at a time (in the same scope).
  - Can restrict a list's scope but cannot extend it.
  - Cannot grab element by index.

  Things I'm thankful for:

  - Elements can be "popped" off the front of the list (using shift).
  - List size is given to us (via $#).
  - No need to use a string delimited by some character.
  - Can loop over elements.


* Cursor position is tracked manually.

  Grabbing the current cursor position cannot be done reliably from POSIX shell.
  Instead, the cursor starts at 0,0 and each movement modifies the value of a
  variable (relative Y position in screen). This variable is how the file
  manager knows which line of the screen the cursor is on.


* Multi-byte input is handled by using a 2D case statement.

  (I don't really know what to call this, suggestions appreciated)

  Rather than using read timeouts (we can't sleep < 1s in POSIX shell anyway)
  to handle multi-byte input, shfm tracks location within sequences and handles
  this in a really nice way.

  The case statement matches "$char$esc" with "$esc" being an integer holding
  position in sequences. To give an example, down arrow emits '\033[B'.

  - When '\033?' is found, the value of 'esc' is set to '1'.
  - When '[1'    is found, the value of 'esc' is set to '2'.
  - When 'B2'    is found, we know it's '\033[B' and handle down arrow.
  - If input doesn't follow this sequence, 'esc' is reset to '0'.


* Filename escaping works via looping over a string char by char.

  I didn't think this was possible in POSIX shell until I needed to do this in
  KISS Linux's package manager and found a way to do so.

  I'll let the code speak for itself (comments added for clarity):

      file_escape() {
          # store the argument (file name) in a temporary variable.
          # ensure that 'safe' is empty (we have no access to the local keyword
          # and can't use local variables without also using a sub-shell). This
          # variable will contain its prior value (if it has one) otherwise.
          tmp=$1 safe=

          # loop over string char by char.
          # this takes the approach of infinite loop + inner break condition as
          # we have no access to [ (personal restriction).
          while :; do
              # Remove everything after the first character.
              c=${tmp%"${tmp#?}"*}

              # Construct a new string, replacing anything unprintable with '?'.
              case $c in
                  [[:print:]]) safe=$safe$c ;;
                  '')          return ;; # we have nothing more to do, return.
                  *)           safe=$safe\? ;;
              esac

              # Remove the first character.
              # This shifts our position forward.
              tmp=${tmp#?}
          done
      }

      # Afterwards, the variable 'safe' contains the escaped filename. Using
      # globals here is a must. Printing to the screen and capturing that
      # output is too slow.


* SIGWINCH handler isn't executed until key press is made.

  SIGWINCH doesn't seem to execute asynchronously when the script is also
  waiting for input. This causes resize to require a key press.

  I'm not too bothered by this. It does save me implementing resize logic which
  is utter torture. :)

More Repositories

1

pure-bash-bible

๐Ÿ“– A collection of pure bash alternatives to external processes.
Shell
35,948
star
2

neofetch

๐Ÿ–ผ๏ธ A command-line system information tool written in bash 3.2+
Shell
20,984
star
3

pywal

๐ŸŽจ Generate and change color-schemes on the fly.
Python
8,114
star
4

pure-sh-bible

๐Ÿ“– A collection of pure POSIX sh alternatives to external processes.
Shell
6,293
star
5

fff

๐Ÿ“ A simple file manager written in bash.
Shell
4,028
star
6

pfetch

๐Ÿง A pretty system information tool written in POSIX sh.
Shell
2,018
star
7

sowm

An itsy bitsy floating window manager (220~ sloc!).
C
894
star
8

wal

๐ŸŽจ Generate and change colorschemes on the fly. Deprecated, use pywal instead. -->
Shell
724
star
9

pxltrm

๐Ÿ–Œ๏ธ pxltrm - [WIP] A pixel art editor inside the terminal
Shell
642
star
10

dotfiles

๐Ÿ™ dotfiles
Vim Script
541
star
11

writing-a-tui-in-bash

How to write a TUI in BASH
499
star
12

pash

๐Ÿ”’ A simple password manager using GPG written in POSIX sh.
Shell
336
star
13

birch

An IRC client written in bash
Shell
312
star
14

torque

๐Ÿš‚ A TUI client for transmission written in pure bash.
Shell
222
star
15

wal.vim

๐ŸŽจ A vim colorscheme for use with wal
Vim Script
214
star
16

promptless

๐Ÿš€ A super fast and extremely minimal shell prompt.
Shell
186
star
17

paleta

Change terminal colors on-the-fly independent of terminal emulator.
C
182
star
18

bin

๐Ÿ—‘๏ธ scripts
Shell
145
star
19

bum

๐ŸŽต Download and display album art for mpd/mopidy tracks.
Python
134
star
20

openbox-patched

PKGBUILD and patches for Openbox with Rounded Corners
111
star
21

fff.vim

A plugin for vim/neovim which allows you to use fff as a file opener.
Vim Script
103
star
22

bareutils

A coreutils written in pure bash.
Shell
88
star
23

k

kiss pkg man written in c
C
55
star
24

clutter-home

clutter your home directory!
50
star
25

eiwd

iwd without dbus
C
46
star
26

barsh

Use your terminal as a bar
Shell
38
star
27

nosj

a json parser written in pure bash
Shell
36
star
28

bush

This is an experiment to see how many standard tools and functions we can re-implement in pure bash.
Shell
33
star
29

crayon

๐ŸŽจ A dark 16 color colorscheme for Vim, Gvim, and Nvim
Vim Script
29
star
30

startpage

๐Ÿ”— Simple start page written in HTML/SCSS
CSS
29
star
31

ryuuko

๐ŸŽจ A colorscheme~
Vim Script
28
star
32

wiki

KISS Linux - Wiki (The wiki is now a part of the website)
27
star
33

hello-world.rs

๐Ÿš€Memory safe, blazing fast, configurable, minimal hello world written in rust(๐Ÿš€) under 1 line of code with few(774๐Ÿš€) dependencies๐Ÿš€
Rust
25
star
34

pow

Shell
24
star
35

root.vim

๐ŸŒด Automatically set directory to your project's root based on a known dir/file.
Vim Script
22
star
36

nfu

Neofetch Utils - A set of C programs to print system information.
C
22
star
37

wm

xcb wm
C
20
star
38

pkg

Package Manager for Kiss Linux
Shell
18
star
39

dylanaraps

17
star
40

neofetch-branding

Logos for Neofetch
15
star
41

codegolf

my bash code golfs
15
star
42

okpal

okpal - Swap on the fly between a bunch of palettes
Shell
15
star
43

eiwd_old

SEE: https://github.com/dylanaraps/eiwd
C
14
star
44

taskrunner.nvim

๐Ÿƒ Runs Gulp/Gruntfiles in terminal splits
Vim Script
14
star
45

discord-selena

Log all Discord messages for transparency
Python
12
star
46

libdbus-stub

stub libdbus to appease
C
12
star
47

str

C
11
star
48

dylan-kiss

Dylan's KISS repository
Objective-C
11
star
49

kiss-flatpak

flatpak for kiss
Shell
11
star
50

blog

Shell
10
star
51

kiss-initramfs

[WIP] initramfs tool for KISS (help wanted!)
Shell
9
star
52

sowm-patches

READ: https://github.com/dylanaraps/sowm/pull/57
8
star
53

golfetch

simple fetch script for Linux.
Shell
8
star
54

reddit-sidebar-toggle

๐Ÿ‘ฝ Toggle the sidebar on reddit.com
JavaScript
8
star
55

coal

๐Ÿš‚ A bash script that takes a list of colors and outputs them in various formats for use in other programs.
Shell
8
star
56

dylan.k1ss.org

HTML
7
star
57

wayland-experiment

Shell
6
star
58

uncompress

6
star
59

xyz-redirect

simply a cheeky way to 301 redirect https to another domain leveraging netlify to handle the SSL cert.
HTML
5
star
60

repo

๐Ÿ“ฆ Dylan's Cydia Repo
Shell
5
star
61

dlink-ssid-bypass

๐Ÿ“ก Bypass SSID validation on D-Link DSL-2750B
4
star
62

languages

Shell
4
star
63

pywal-branding

Logos for pywal
Shell
4
star
64

blag

blag
HTML
3
star
65

oldyiayias

Old website for Yiayias Greek Cafe
CSS
3
star
66

kisslinux-irc-logs

Freenode #kisslinux IRC logs (2019-2021)
3
star
67

pascal_lint.nvim

๐Ÿ† Show fpc compiler output in a neovim split.
Vim Script
3
star
68

dylanaraps.github.io-old

๐Ÿ”— My personal website.
HTML
2
star
69

2211

mnml trmnl using vte
C
2
star
70

kiss-submodule-links

Shell
2
star
71

eww-static-test

Rust
1
star
72

yiayias

Recreating Yiayia's website 6 months later
HyPhy
1
star
73

google

Remake of Google.com for an assignment
HTML
1
star