-- Leo's gemini proxy

-- Connecting to thrig.me:1965...

-- Connected

-- Sending request

-- Meta line: 20 text/gemini

OpenBSD vi Backwards Search Bug


The OpenBSD vi† has a bug that prevents a backwards search for "?"—but only with "extended" regular expressions enabled—as "?" is special to both the command (which uses the delimiter "?" to indicate a backwards search) and to extended regular expressions (which use the "?" to indicate that the previous portion of the expression is optional. The command "??" indicates that the previous regular expression should be used, but the command "?\?" is an error because the search delimiter parser removes the "\" which leaves a search of "?" which is an invalid extended regular expression. After sleeping on this I figured that a search of "[?]" would also work; hiding characters in character classes also sees use to e.g. prevent a grep from seeing itself in the process table—but you probably instead want to use `pgrep foo` and not `ps ... | grep '[f]oo'`. Anyways the command "?[\?]" works; the internal "?" must be escaped to prevent the outer command delimiter search from yielding the invalid expression of "[".


Finding this was pretty fun, as many vi commands simply call into some ex function (with a "but this is vi, not ex!" flag set)‡, and from there eventually one may find common/search.c, and maybe add code to "do the (hopefully) right thing" with the input "?\?" when extended regular expressions are enabled. Or how often do you search backwards for a literal "?" in a file with extended regex? How old is this bug?


Other weird things found along the way:


":\/forward" or ":\?back" can be specified, in addition to the probably more typical ":/forward" or ":?back" forms for this.

";" can be used to start a search from somewhere. So in addition to "1G/foo" to go to the top of the file and search, ":1;/foo" will do much the same, only as an ex command. (Yet another way is via wrapscan.) ";" allows for sub-searches via ":/foo/;/bar" which would search forward for "foo", and from that point then search for "bar". Of course this could also be ":?foo?;/bar" or other such combinations to search backwards for "foo", and then forwards for "bar", or ":/foo/+1;/bar", etc.

some commands—line offsets or "z"—can follow the closing delimiter in a vi search, so "/foo/z." will put the search result in the middle of the screen, or "/foo/0" will move the cursor to the beginning of the line with the match.

I already knew about "/foo/-1" to get to the line before a search; this is more typical in an ex command like ":,/foo/-1d" that saves the current line, searches for "foo", backs up a line, and then deletes all lines between the saved line and the new line (possibly asking do you want to reverse the range? or failing, depending on whether you have a fancy version of vi, or not).


xkcd://1053


IDE


It also helps if you can see what you are searching for, or to have the text formatted in a predictable way, which automatic code formatters may help with. For C functions I use the following form, formatted by way of the ex command ":,/^`/-1!clang-format"


    int
    main(int argc, char *argv[])
    {
        ;
    }

so that "/^main" can easily find the function name; this is somewhat more difficult if the function name is hiding somewhere after the return type on the same line. Probably one of these years I should learn ctags, but I have other ways to accomplish "show me what files contain this here extended regular expression in this directory tree (or git repository)" and from there can launch into vi with those files. The startup and shutdown time of vi is negligible, so it's easy to quit out to some other command or shell to find a new set of files. You do lose marks, as vi doesn't have a .viminfo to store those in across instances.


It may also help to have a command that can associate process table entries with the relevant tmux pane as the tty, if you forgot where exactly you were editing triangle.go from a few days ago. Implementing something like the "j" command (or finding the various implementations of it) is left as an exercise to the reader.


    $ ps axo pid,tty,command | grep '[v]i'
    55438 ttyp4    /usr/bin/perl /home/jmates/bin/blog-about vi-backwards-se
     6533 ttyp4    vi -c5 /home/jmates/tmp/blog-about.4CnLWsa225.gmi
     6463 ttypa    vi triangle.go
    54238 ttypb    vi vi/v_search.c ex/ex_cmd.c common/search.c
    $ tmux list-windows '-aF#{pane_tty} #S:#I'
    /dev/ttyp7 ci:0
    ...
    $ j
       PID  TTY     CMD
      6463  pa:2    vi triangle.go
     54238  re:1    vi vi/v_search.c ex/ex_cmd.c common/search.c
      6533  re:0    vi -c5 /home/jmates/tmp/blog-about.4CnLWsa225.gmi
    $ grep votcana ~/.cwmrc
    bind-key 4-1 "xdotool search --name votcana-pa windowactivate"
    bind-key 4-2 "xdotool search --name votcana-re windowactivate"
    bind-key 4-3 "xdotool search --name votcana-ci windowactivate"
    bind-key 4-4 "xdotool search --name votcana-vo windowactivate"
    command votcana /home/jmates/libexec/votcana

tmux screen "pa" (the upper left xterm, clockwise to "re", "ci", and "vo") and window 2 thereof is a bit easier to get to than "where the heck is ttypa?" especially as the number of tmux screens and windows increases.


tags #vi #openbsd #ex


† Descended from "Version 1.79 (10/23/96) The CSRG, University of California, Berkeley" though modified since.


‡ Actually more complicated than described; vi sometimes injects a fake command into some system I haven't fully grokked and then...

-- Response ended

-- Page fetched on Tue May 21 21:47:32 2024