Some of you older folks like me may remember the ‘what’ program. Actually, most software developers probably know about it, but aren’t aware of its history. It came from SCCS, a now ancient source code control system.
The gist of the ‘what’ program: it searches files for strings that start with ‘@(#)’. It is generally available in the base install of all UNIX systems, including OS X.
I’ve continued to use it for some things. Why? There are occasions where a user of my software is using a binary whose origin they do not know. Typically this happens when I give them something from a trunk for testing, but it also happens when they compile and install from a tag but don’t use any package management for the installation (perhaps they installed in their home directory on a host where they don’t have administrator privileges).
Since I embed ‘@(#)’ strings in my source code that get compiled into binaries, I can usually get a pretty good idea of what version a user is having problems with by having them run ‘what’ on the binary, even if it’s from a trunk.
However, I’ve never been fond of the output of ‘what’. The way to get useful information into all compiled files with a typical revision control system (CVS in the old days, Subversion today) is via expanded keywords. I don’t care to see the keywords themselves, and in most cases I don’t care to see the whole repository path. Not to mention the decorations (@(#), $, etc.). I need tag/branch/trunk information, and revision numbers.
When I was using CVS as my repository system, I embedded “@(#) $Name:$ $Id:$” strings in my compilable source files. I actually used this string as the argument to the constructor of my CvsTag class, which allowed me to programmatically retrieve version information for the compilation unit. Today, using Subversion, I do something similar using my SvnTag class. However, Subversion has no equivalent to $Name:$. So in my sources, I’m now converting to using a custom keyword called DwmPath (very unoriginal), whose expansion is used in the constructor of SvnTag instances. It contains the relative path of the file in the repository and the revision number. Custom keywords are available in Subversion 1.8 and above.
What my ‘what’ replacement does is similar to ‘what’ in that it searches for strings that start with ‘@(#)’. I still embed these start characters to remain compatible with ‘what’. But if I find the string contains a Subversion keyword DwmPath or Header or HeadURL, I pluck out just that information for display, minus the keywords themselves. And if the path is typical (under a trunk, branches or tags directory), I will do more processing. For a trunk path, I’ll show the revision number of the last revision of the file. For a tag path, I’ll show the tag name. For a branch, I’ll show the branch name and ‘(branch)’ to distinguish it from tags of the same name.
With a ‘-s’ (short) argument on the command line, I’ll shorten the file path to just its basename.
Some example output from the trunk of libDwm:
dwm@kiva:/home/dwm/svn/dwm/libDwm/trunk/apps/dwmwhat% ./dwmwhat dwmwhat
trunk (8407) dwm/libDwm/trunk/apps/dwmwhat/dwmwhat.cc
trunk (8388) dwm/libDwm/trunk/src/DwmOptArgs.cc
trunk (8388) dwm/libDwm/trunk/src/DwmStringUtils.cc
trunk (8402) dwm/libDwm/trunk/src/DwmSvnTag.cc
And the shortened output on the dwmwhat program itself:
dwm@kiva:/home/dwm/svn/dwm/libDwm/trunk/apps/dwmwhat% ./dwmwhat -s dwmwhat
trunk (8407) dwmwhat.cc
trunk (8388) DwmOptArgs.cc
trunk (8388) DwmStringUtils.cc
trunk (8402) DwmSvnTag.cc
And of course it still works on programs that don’t have Subversion keywords:
% dwmwhat /usr/bin/php
@(#)$File: apprentice.c,v 1.191 2013/02/26 21:02:48 christos Exp $
@(#)ADF Database
@(#)SunPHIGS
@(#)$File: apptype.c,v 1.13 2011/09/07 21:57:15 christos Exp $
@(#)$File: ascmagic.c,v 1.85 2012/08/09 16:33:15 christos Exp $
@(#)$File: cdf.c,v 1.53 2013/02/26 16:20:42 christos Exp $
@(#)$File: cdf_time.c,v 1.12 2012/05/15 17:14:36 christos Exp $
@(#)$File: compress.c,v 1.70 2012/11/07 17:54:48 christos Exp $
@(#)$File: encoding.c,v 1.7 2012/01/24 19:02:02 christos Exp $
@(#)$File: fsmagic.c,v 1.67 2013/03/17 15:43:20 christos Exp $
@(#)$File: funcs.c,v 1.61 2012/10/30 23:11:51 christos Exp $
@(#)$File: is_tar.c,v 1.37 2010/11/30 14:58:53 rrt Exp $
@(#)$File: magic.c,v 1.78 2013/01/07 18:20:19 christos Exp $
@(#)$File: print.c,v 1.76 2013/02/26 18:25:00 christos Exp $
@(#)$File: readcdf.c,v 1.33 2012/06/20 21:52:36 christos Exp $
@(#)$File: readelf.c,v 1.97 2013/03/06 03:35:30 christos Exp $
@(#)$File: softmagic.c,v 1.165 2013/03/07 02:22:24 christos Exp $
It’s probably worth noting that to make this speedy, I resorted to using mmap(). My first pass read the file using getdelim(), which worked but was 4 times slower than using mmap(). I consider it very usable now. On a fairly large library file, it’s speedy enough for me. It is single threaded, but on my dual Xeon L5640 machine:
dwm@kiva:/home/dwm/svn/dwm/libDwm/trunk% ls -l lib/libDwm.a
-rw-r--r-- 1 dwm dwm 18616988 Apr 18 00:40 lib/libDwm.a
dwm@kiva:/home/dwm/svn/dwm/libDwm/trunk% /usr/bin/time dwmwhat -s lib/libDwm.a
trunk (8383) DwmBZ2IO.cc
trunk (8384) DwmCgi.ll
trunk (8386) DwmConditionVariable.cc
trunk (8387) DwmCvsTag.cc
trunk (8388) DwmDaemonUtils.cc
trunk (8388) DwmDateTime.cc
trunk (8388) DwmDirectoryEntry.cc
trunk (8388) DwmDescriptor.cc
trunk (8388) DwmEngFormat.cc
trunk (8388) DwmExecutor.ll
trunk (8388) DwmFileLogger.cc
trunk (8388) DwmFileRoller.cc
trunk (8388) DwmGZIO.cc
trunk (8388) DwmGroup.cc
trunk (8388) DwmHostPinger.cc
trunk (8388) DwmIO.cc
trunk (8388) DwmIpv4Address.cc
trunk (8388) DwmIpv4IcmpDestinationUnreachable.cc
trunk (8388) DwmIpv4IcmpEchoReply.cc
trunk (8388) DwmIpv4IcmpEchoRequest.cc
trunk (8388) DwmIpv4IcmpMessage.cc
trunk (8388) DwmIpv4IcmpRedirect.cc
trunk (8388) DwmIpv4IcmpSourceQuench.cc
trunk (8388) DwmIpv4IcmpTimeExceeded.cc
trunk (8388) DwmIpv4PacketHeader.cc
trunk (8388) DwmIpv4Prefix.cc
trunk (8388) DwmIpv4TcpHeader.cc
trunk (8388) DwmIpv4TcpPacket.cc
trunk (8388) DwmIpv4TcpPayload.cc
trunk (8388) DwmIpv4UdpHeader.cc
trunk (8388) DwmIpv4UdpPacket.cc
trunk (8388) DwmIpv4UdpPayload.cc
trunk (8388) DwmIpv4Utils.cc
trunk (8388) DwmIpv6Address.cc
trunk (8388) DwmIpv6Prefix.cc
trunk (8388) DwmLocalInterfaces.cc
trunk (8388) DwmMacAddress.cc
trunk (8388) DwmMplsLabel.cc
trunk (8388) DwmMplsLabelStack.cc
trunk (8388) DwmMutex.cc
trunk (8388) DwmOptArgs.cc
trunk (8388) DwmPacer.cc
trunk (8388) DwmPassword.cc
trunk (8388) DwmPathUtils.cc
trunk (8388) DwmPcap.cc
trunk (8388) DwmPingDestination.cc
trunk (8388) DwmPingDestinationEntry.cc
trunk (8388) DwmPthreadLocker.cc
trunk (8388) DwmPthreadReadWriteLock.cc
trunk (8388) DwmPthreadSignal.cc
trunk (8388) DwmRegistry.ll
trunk (8388) DwmRusage.cc
trunk (8388) DwmSignal.cc
trunk (8388) DwmSocket.cc
trunk (8388) DwmStringUtils.cc
trunk (8388) DwmStrptime.cc
trunk (8402) DwmSvnTag.cc
trunk (8388) DwmSysLogger.cc
trunk (8388) DwmTermios.cc
trunk (8388) DwmTimeInterval.cc
trunk (8388) DwmTimeUtil.ll
trunk (8388) DwmTimeValue.cc
trunk (8388) DwmTypeName.cc
trunk (8388) DwmUnitAssert.cc
trunk (8388) DwmXmlAttribute.cc
trunk (8388) DwmXmlElement.cc
trunk (8388) DwmProcessInfo.cc
trunk (8388) DwmProcessTable.cc
0.08 real 0.08 user 0.00 sys