sd -- a drop-in replacement for `cd'

Check-in [78020ab235]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:replace piping of `print' output into commands by `<<<' here strings whereever possible. some other minor edits.
Timelines: family | ancestors | descendants | both | ksh
Files: files | file ages | folders
SHA1: 78020ab2352a0064746f10cb6a4bd10de09591a9
User & Date: vdh 2019-12-22 16:42:37
Context
2019-12-22 17:13
take over the tidy up from mksh branch. check-in: a28d3150c6 user: vdh tags: ksh
2019-12-22 16:42
replace piping of `print' output into commands by `<<<' here strings whereever possible. some other minor edits. check-in: 78020ab235 user: vdh tags: ksh
2019-12-22 16:20
replace piping of `print' output into commands by `<<<' here strings whereever possible. some other minor edits. check-in: 630cfacdfe user: vdh tags: mksh
2019-12-13 14:09
minor adjustments to quickstart.md. check-in: a7c6e1b635 user: vdh tags: ksh
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to sd.ksh.

611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
...
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
...
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
...
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706

707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
...
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
...
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
...
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887

888
889
890
891
892
893
894
...
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
...
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
....
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
# count linefeed separated lines in string `buffer'.
# we append a trailing \n (implying `buffer' is assumed
# to not contain a trailing empty line) via `sdprint'
# since `wc -l' actually counts the number of \n.
# if buffer is empty we return 0
#-------------------------------------------------------
   typeset -i lc=0    #using `-i' suppresses spurious blanks in `wc -l' output.
   [[ -z $1 ]] || lc=$($sdprint "$1" | wc -l)
   $sdprint $lc
}

function sdlogread {
   sdlog=$(<$dirv)
   sdmagicline=${sdlog%%$'\n'*}
   sdlog=$($sdprint "$sdlog" | grep -v "^$sdmagicstring")
   sdlist=$(sdstack)
   sdnew=""
}

function sdlogwrite {
   $sdprint $sdmagicstring-- time stamp: $(date)$'\n'"$sdlog" >| $dirv
   sdlist=$(sdstack)
................................................................................
   lines=$(sdlinecount "$sdlog")

   if (( lines > sdmax )); then
      typeset -i limit

      ((limit = sdmax*9/10))
      ((limit = sdlines > limit ? sdlines:limit))
      sdlog=$($sdprint "$sdlog" | tail -$limit)
      mv -f $dirv ${dirv}.bak
      sdlogwrite
   fi
}

function sdremove {
#----------------------------------------
................................................................................
   typeset dname check
   typeset IFS=$'\n'

   sdl=$sdlines
   sdirs -l $sdmax #look at whole database
   sdlines=$sdl

   dname=$($sdprint "$sdlist" | $awk -F"\t" '$NF ~ /'"$pat"'/ {print $NF}')
   sdirs -l $sdlines  #important to reset before possibly returning...
   [[ -n $dname ]] || { $sdprint no match; return 1; }

   $sdprint "$dname"
   $sdprint -n "remove these entries from the logfile? [y/N] "
   read check
   check=${check:-"n"}
................................................................................
   fi

   # note to self: the '|' quoting '|' is not really required here.
   # its done just to silence `ksh -n'..
   pat="${dname//$IFS/\|}"
   pat="^(${pat//\//\\/})\$"

   sdlog=$($sdprint "$sdlog"|egrep -v "$pat")

   sdlogwrite
}

function sdclean {
#----------------------------------------------
#remove stale entries (and `/') from sdlog/dirv
#----------------------------------------------
   typeset IFS=$'\n'
   typeset entries stale


   for dname in $sdlog; do
      [[ -d ${dname/#\~/$HOME} ]] && {
         entries+=$dname$'\n'
      } || stale+=$dname$'\n'
   done
   [[ -n $stale ]] && {
      stale=$($sdprint -n "$stale" | sort | uniq )
      $sdprint "$stale"
      $sdprint -n "eliminate these stale directories from the logfile? [y/N] "
      read check
      check=${check:-"n"}
      if [[ "$check" == "y" ]]; then
         sdlog=${entries%$'\n'}
         sdlogwrite
................................................................................
   lines=$(sdlinecount "$sdlog")

   most="top ten on stack :"
   ((lines > 0)) && {
      stacksize=$(sdlinecount "$sdlist")
      mostlen=${#most}
      for ((i=1; i<= indent0+mostlen; i++)); do indent+=" "; done
      top=$($sdprint "$sdlist" | head -10 | $awk -F'\t' '{print $(NF-1) "\t" $NF}')
      top=${top//$'\n'/$'\n'$indent}
   } || stacksize=0

   vdir=${dirv/#$HOME/\~}


   report="
................................................................................
# given unique entry/directory is used as the resulting weight for
# that entry when sorting. note, that the visit frequencies are used
# as a secondary sort key since in extreme cases (very large `sdpower'
# values) weights can become exactly zero at the bottom of the sorted
# stack and the primary key does not suffice for unambiguous sorting.
# generally this will not happen, though.
#-----------------------------------------------------------------------------
   $sdprint "$sdlog" | tail -$sdlines |\
   $awk '
      BEGIN {
         OFS     = "\t"
         sdlines = '$sdlines'
         sdpower = '$sdpower'
      }
      {
................................................................................
      if [[ $pat == "$sdpattern" ]]; then
         ((sdcount++))
      else
         sdcount=1
      fi
      sdpattern=$pat
      ((sdcount > 1)) && {
         rank=$($sdprint "$sdlist" |\
            $awk -F"\t" "$awkpat"' {
               if (++count == '$sdcount') {
                  print $(NF-1)
                  exit
               }
            }'
         )
         [[ -n $rank ]] && awkpat="NR == $rank" || { sdcount=1; $sdprint starting over ... >&2 ;}
      }
   fi

   matches=$($sdprint "$sdlist" |\
      $awk -F"\t" "$awkpat"' {
         sub(/^~/, "'"$HOME"'", $NF)
         print $NF
      }')


   # select first actually existing directory
   typeset IFS=$'\n'
   for match in $matches; do
      [[ -d $match ]] && {
         dname=$match
         break
................................................................................
               ((sdsilent == 1)) || {
                  $sdprint "using first valid match from:"
                  $sdprint "$matches"
               }
               break
            } || {
               [[ -d "$dname" ]] && {
                  print $errmsg
                  return $status
               } || {
                  print "No matching directory"
                  return 1
               }
            }
         } || { print $errmsg; return $status; }  # should be the "not in logfile and no such file or directory" case
      }
   done

   # avoid logging `cd' to one of (home|current|root)-dir, since otherwise
   #
   # -- $HOME would always be at the top of the stack (really undesirable),
   # -- an accidental/reaffirmative `cd .' would artificially increase
................................................................................
}

function sdchoose { ## dname
   # possibly called by `sdirs' to allow cd to directory selected from
   # list of matches. note to self: sort alphabetically?
   typeset dname="$1"
   typeset -i hits num
   $sdprint "$dname" | $awk -F"\t" '
      BEGIN {
         print "rank\tindex\tname"
      }
      {
         print $(NF-1) "\t" NR "\t" $NF
      }' >&2
   hits=$($sdprint "$dname"|wc -l)
   $sdprint -n "choose index (<CR> = 1; CTRL-C = abort): " >&2
   read -r num
   ((num == 0)) && num=1
   ((num > 0 && num <= hits)) && {
      dname=$($sdprint "$dname" | $awk -F"\t" 'NR == '$num' {sub(/^~/,"'$HOME'",$NF); print $NF}')
      cd "$dname"
      # care is taken that a call to `sdirs' does not send anything to stdout elswhere
      # and here only if we believe that we are operating under tcsh (indicated
      # by `sdistcsh == 1'). this output is then needed by the `sdirs' executable
      # with tcsh.
      ((sdistcsh == 1)) && $sdprint $dname
   }
................................................................................
   shift $((OPTIND - 1))

   if [[ $# == 0 ]]; then
      [[ -n $sdlog ]] || return 1
      dname=$sdlist
   else
      pat="${@//\//\\/}"
      dname=$($sdprint "$sdlist" | $awk -F"\t" '$NF ~ /'"$pat"'/')
      [[ -n $dname ]] || return 1
   fi

   dname=$($sdprint "$dname" | $dump)
   if ((sdselect == 0)); then
      $sdprint $'frec\tfreq\trank\tname'
      $sdprint "$dname"
   else
      sdchoose "$dname"
   fi
}







|






|







 







|







 







|







 







|










>







|







 







|







 







|







 







|





|





|



|
>







 







|


|



|







 







|





|
|




|







 







|



|







611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
...
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
...
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
...
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
...
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
...
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
...
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
...
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
...
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
....
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
# count linefeed separated lines in string `buffer'.
# we append a trailing \n (implying `buffer' is assumed
# to not contain a trailing empty line) via `sdprint'
# since `wc -l' actually counts the number of \n.
# if buffer is empty we return 0
#-------------------------------------------------------
   typeset -i lc=0    #using `-i' suppresses spurious blanks in `wc -l' output.
   [[ -z $1 ]] || lc=$(wc -l <<< "$1")
   $sdprint $lc
}

function sdlogread {
   sdlog=$(<$dirv)
   sdmagicline=${sdlog%%$'\n'*}
   sdlog=$(grep -v "^$sdmagicstring" <<< "$sdlog")
   sdlist=$(sdstack)
   sdnew=""
}

function sdlogwrite {
   $sdprint $sdmagicstring-- time stamp: $(date)$'\n'"$sdlog" >| $dirv
   sdlist=$(sdstack)
................................................................................
   lines=$(sdlinecount "$sdlog")

   if (( lines > sdmax )); then
      typeset -i limit

      ((limit = sdmax*9/10))
      ((limit = sdlines > limit ? sdlines:limit))
      sdlog=$(tail -$limit <<< "$sdlog")
      mv -f $dirv ${dirv}.bak
      sdlogwrite
   fi
}

function sdremove {
#----------------------------------------
................................................................................
   typeset dname check
   typeset IFS=$'\n'

   sdl=$sdlines
   sdirs -l $sdmax #look at whole database
   sdlines=$sdl

   dname=$($awk -F"\t" '$NF ~ /'"$pat"'/ {print $NF}' <<< "$sdlist")
   sdirs -l $sdlines  #important to reset before possibly returning...
   [[ -n $dname ]] || { $sdprint no match; return 1; }

   $sdprint "$dname"
   $sdprint -n "remove these entries from the logfile? [y/N] "
   read check
   check=${check:-"n"}
................................................................................
   fi

   # note to self: the '|' quoting '|' is not really required here.
   # its done just to silence `ksh -n'..
   pat="${dname//$IFS/\|}"
   pat="^(${pat//\//\\/})\$"

   sdlog=$(egrep -v "$pat" <<< "$sdlog")

   sdlogwrite
}

function sdclean {
#----------------------------------------------
#remove stale entries (and `/') from sdlog/dirv
#----------------------------------------------
   typeset IFS=$'\n'
   typeset entries stale
   typeset dname check

   for dname in $sdlog; do
      [[ -d ${dname/#\~/$HOME} ]] && {
         entries+=$dname$'\n'
      } || stale+=$dname$'\n'
   done
   [[ -n $stale ]] && {
      stale=$(sort <<< "$stale" | uniq)
      $sdprint "$stale"
      $sdprint -n "eliminate these stale directories from the logfile? [y/N] "
      read check
      check=${check:-"n"}
      if [[ "$check" == "y" ]]; then
         sdlog=${entries%$'\n'}
         sdlogwrite
................................................................................
   lines=$(sdlinecount "$sdlog")

   most="top ten on stack :"
   ((lines > 0)) && {
      stacksize=$(sdlinecount "$sdlist")
      mostlen=${#most}
      for ((i=1; i<= indent0+mostlen; i++)); do indent+=" "; done
      top=$($awk -F'\t' 'NR <= 10 {print $(NF-1) "\t" $NF}' <<< "$sdlist" )
      top=${top//$'\n'/$'\n'$indent}
   } || stacksize=0

   vdir=${dirv/#$HOME/\~}


   report="
................................................................................
# given unique entry/directory is used as the resulting weight for
# that entry when sorting. note, that the visit frequencies are used
# as a secondary sort key since in extreme cases (very large `sdpower'
# values) weights can become exactly zero at the bottom of the sorted
# stack and the primary key does not suffice for unambiguous sorting.
# generally this will not happen, though.
#-----------------------------------------------------------------------------
   tail -$sdlines <<< "$sdlog" |\
   $awk '
      BEGIN {
         OFS     = "\t"
         sdlines = '$sdlines'
         sdpower = '$sdpower'
      }
      {
................................................................................
      if [[ $pat == "$sdpattern" ]]; then
         ((sdcount++))
      else
         sdcount=1
      fi
      sdpattern=$pat
      ((sdcount > 1)) && {
         rank=$(
            $awk -F"\t" "$awkpat"' {
               if (++count == '$sdcount') {
                  print $(NF-1)
                  exit
               }
            }' <<< "$sdlist"  
         )
         [[ -n $rank ]] && awkpat="NR == $rank" || { sdcount=1; $sdprint starting over ... >&2 ;}
      }
   fi

   matches=$(
      $awk -F"\t" "$awkpat"' {
         sub(/^~/, "'"$HOME"'", $NF)
         print $NF
      }' <<< "$sdlist"
   )

   # select first actually existing directory
   typeset IFS=$'\n'
   for match in $matches; do
      [[ -d $match ]] && {
         dname=$match
         break
................................................................................
               ((sdsilent == 1)) || {
                  $sdprint "using first valid match from:"
                  $sdprint "$matches"
               }
               break
            } || {
               [[ -d "$dname" ]] && {
                  $sdprint $errmsg
                  return $status
               } || {
                  $sdprint "No matching directory"
                  return 1
               }
            }
         } || { $sdprint $errmsg; return $status; }  # should be the "not in logfile and no such file or directory" case
      }
   done

   # avoid logging `cd' to one of (home|current|root)-dir, since otherwise
   #
   # -- $HOME would always be at the top of the stack (really undesirable),
   # -- an accidental/reaffirmative `cd .' would artificially increase
................................................................................
}

function sdchoose { ## dname
   # possibly called by `sdirs' to allow cd to directory selected from
   # list of matches. note to self: sort alphabetically?
   typeset dname="$1"
   typeset -i hits num
   $awk -F"\t" '
      BEGIN {
         print "rank\tindex\tname"
      }
      {
         print $(NF-1) "\t" NR "\t" $NF
      }' <<< "$dname" >&2
   hits=$(wc -l <<< "$dname")
   $sdprint -n "choose index (<CR> = 1; CTRL-C = abort): " >&2
   read -r num
   ((num == 0)) && num=1
   ((num > 0 && num <= hits)) && {
      dname=$($awk -F"\t" 'NR == '$num' {sub(/^~/,"'$HOME'",$NF); print $NF}' <<< "$dname")
      cd "$dname"
      # care is taken that a call to `sdirs' does not send anything to stdout elswhere
      # and here only if we believe that we are operating under tcsh (indicated
      # by `sdistcsh == 1'). this output is then needed by the `sdirs' executable
      # with tcsh.
      ((sdistcsh == 1)) && $sdprint $dname
   }
................................................................................
   shift $((OPTIND - 1))

   if [[ $# == 0 ]]; then
      [[ -n $sdlog ]] || return 1
      dname=$sdlist
   else
      pat="${@//\//\\/}"
      dname=$($awk -F"\t" '$NF ~ /'"$pat"'/' <<< "$sdlist")
      [[ -n $dname ]] || return 1
   fi

   dname=$($dump <<< "$dname")
   if ((sdselect == 0)); then
      $sdprint $'frec\tfreq\trank\tname'
      $sdprint "$dname"
   else
      sdchoose "$dname"
   fi
}