I’ve done quite a lot of source level blocks in org-mode, but I think I have a failure scenario if called functions inside the source code blocks are recursive.
The ifconfig
program displays all the important information, just in the wrong format. Let’s fix that. First, let’s retrieve the results and we’ll store that in a name, ifconfig_data
:
#+NAME: ifconfig_data
#+BEGIN_SRC sh :results drawer
ifconfig -a
#+END_SRC
Which spills out:
#+RESULTS: ifconfig_data
:RESULTS:
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
options=3<RXCSUM,TXCSUM>
inet6 ::1 prefixlen 128
inet 127.0.0.1 netmask 0xff000000
inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1
nd6 options=1<PERFORMNUD>
gif0: flags=8010<POINTOPOINT,MULTICAST> mtu 1280
stf0: flags=0<> mtu 1280
en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
ether 6c:40:08:b7:ce:b8
inet6 fe80::6e40:8ff:feb7:ceb8%en0 prefixlen 64 scopeid 0x4
inet 192.168.77.40 netmask 0xfffffe00 broadcast 192.168.77.255
nd6 options=1<PERFORMNUD>
media: autoselect
status: active
...
We can access that data easily in standard org-mode Babel thingie:
#+BEGIN_SRC elisp :var datastring=ifconfig_data :results value
(format "Results: %s" datastring)
#+END_SRC
However, the output format is awful, and I would like to have a list of tuples, where each tuple has the interface and the IP address. Since some interfaces may not have an IPv4 address (which is what I’m wanting).
To address this, I have a recursive function that calls itself for each regular expression match:
1. Matched the Interface
2. The name of the Interface
3. Matches the Address
4. Optional `addr:` section that we don’t care about
5. The value of the address
If the match is on an address, we assume that we passed in the interface name as a header
parameter, so that we can /build up/ the result.
#+NAME: ifconfig-display
#+BEGIN_SRC elisp :var datastring=ifconfig_data :results value
(defun split-ifconfig (datastr pos header)
"Assumes that the DATASTR is a string datastrput from ifconfig. The POS is position within the string to search. The HEADER is the current interface address."
(when (string-match "\\(^\\([^ :]+\\):?\\ \\)\\|\\( *inet \\(addr:\\)?\\([0-9.]+\\)\\)" datastr pos)
(cond
;; Interface:: Recurse with the name of this new header
((match-string 1 datastr)
(split-ifconfig datastr (match-end 0) (match-string 2 datastr)))
;; Address:: Return a list with our interface header, the
;; address, and the results of parsing the rest
((match-string 3 datastr)
(cons (list header (match-string 5 datastr))
(split-ifconfig datastr (match-end 0) nil))))))
(split-ifconfig datastring 0 nil)
#+END_SRC
This works if I evaluate the function stuff directly with C-x C-e
.
The last line, however, in the code block returns nil
. Always. The datastring
contains the data. The function, split-ifconfig
that was defined in the function is called. Still no go.
This doesn’t work, even if I pre-load the function into the global function space, and call it from the block:
#+BEGIN_SRC elisp :var out=ifconfig_data :results value
(split-ifconfig out 0 nil)
#+END_SRC
I’ve tried making the datastr
aspect a global variable, and not keep passing it. Still no go.