about summary refs log tree commit diff
path: root/bin/redo-whichdo
blob: fa3a4ca765e156cd62759ac55285ff455772ee8f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#!/bin/sh -eu
# redo-whichdo – bourne shell implementation of DJB redo
# Copyright © 2018-2019  Nils Dagsson Moskopp (erlehmann)

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.

# Dieses Programm hat das Ziel, die Medienkompetenz der Leser zu
# steigern. Gelegentlich packe ich sogar einen handfesten Buffer
# Overflow oder eine Format String Vulnerability zwischen die anderen
# Codezeilen und schreibe das auch nicht dran.

case ${SEPARATOR:-} in
 '') [ -t 1 ] && SEPARATOR='\n' || SEPARATOR='\0'
esac

alias print_filename="printf -- '%s${SEPARATOR}'"

find_default_dofile() {
 target_abspath=${1}
 target_basename=${target_abspath##*/}
 target_dirname=${target_abspath%"${target_basename}"}
 dofile_candidate=default.${target_basename}.do
 while :; do
  dofile_candidate=default.${dofile_candidate#default.*.}
  dofile_candidate_abspath="${PWD%/}/${dofile_candidate}"
  case "${dofile_candidate_abspath}" in
   "${target_abspath}")
    # A file named “default.do” can not be its own dofile.
   ;;
   *)
    print_filename "${dofile_candidate_abspath}"
   ;;
  esac

  [ -f "${dofile_candidate}" ] || [ "${dofile_candidate}" = "default.do" ] && break
 done
}

find_dofile() {
 # BusyBox v.1.19.3 outputs nothing for “readlink -f” on a nonexistent
 # file, which means that redo-whichdo can not canonicalize filenames.
 case "${1}" in
  /*) target_abspath="${1}" ;;
  *) target_abspath="${PWD%/}/${1}" ;;
 esac
 target_basename=${target_abspath##*/}
 target_dirname=${target_abspath%"${target_basename}"}

 # Skip printing first guess for path names with the prefix “default”
 # to prevent duplicate output.
 case "${target_basename}" in
  default)
  ;;
  default.*)
  ;;
  *)
   dofile_candidate="$target_abspath".do
   print_filename "${dofile_candidate}"
   [ -f "${dofile_candidate}" ] && return
  ;;
 esac

 cd "${target_dirname}"

 while :; do
  find_default_dofile "${target_abspath}"
  [ -f "${dofile_candidate}" ] || [ "${PWD}" = "/" ] && break
  cd ..
 done

 [ -f "${PWD%/}/${dofile_candidate}" ] || exit 1
}

for target; do
 find_dofile "${target}"
done