Writing

Software, technology, sysadmin war stories, and more. Feed
Saturday, March 23, 2013

Another disconnect between data and code

I came up with another example of the divide between dead text files and living code. It's the gap in my understanding which persisted for a few years until I took a compilers class. Now I have some tools with which to better understand how to handle those situations.

In the earlier post, I gave the example of having config files be relatively simple and free of assignments, variable references, loops, and most conditionals because I lacked the means to treat them as something "living" like actual code. This time around, I'm talking about the reverse.

Let's say you want to write a tool which will configure a USB device for a user. It's a helper which aims to take care of a multi-step process for them. It's supposed to be relatively portable and flexible so as to support a bunch of different Unixy operating systems and types of hardware.

One of the things you have to do is use magic values which are defined by the operating system or hardware. These typically show up in a bunch of C header files under /usr/include. If you're talking about serial ports, there are magic values like TIOCM_RTS and TIOCM_DTR. When you do things with files, you see values like O_DIRECT and O_NOATIME.

When you're actually writing a program in C, this is no big deal. You just #include whatever header files you need to get the right #define values, and then you use those symbols directly in your program. The preprocessor takes care of the rest at compile-time, and you use the actual magic values which are appropriate for that system, whatever they may be.

Now imagine you're coming at it from the perspective of a rather generic system, and you have a config file where a user says "use O_NOATIME when opening this file". Just think about what you'd have to do to actually handle that sanely. One way would be to have a big lookup table where you essentially map "O_NOATIME" (a string) to O_NOATIME (the #define), and then look for matches when parsing the config file.

The problem is: what happens when the user asks for something which isn't in already in your lookup table? Maybe your system has a feature you want to use through this program, but since the program doesn't have a mapping of "O_SHINYTHING" to the actual O_SHINYTHING symbol, you can't access it.

This is still one of those places where none of the solutions are very good. You probably will wind up writing a glorified "eval" just to extract the value of something. That is, assuming a C/C++ type compiled language, I expect most people will solve it by writing something which implements the following pseudocode:

  // create temp.c for writing
 
  // write to temp.c:
  // (include whatever .h files here)
  // "int main() { printf("%d\n", O_SHINYTHING); return 0; }"
 
  // close temp.c
 
  // compile temp.c to temp
 
  // popen temp, read output, parse to numeric value
 
  // remove temp and temp.c
 
  // associate numeric value with "O_SHINYTHING" internally

Come to think of it, that seems like the kind of thing which happens in autoconf-generated configure scripts. If there's no compiler on the system, I guess you're out of luck.

This probably seems like a bit of a stretch, and it mostly is, but there are still some times when you want a user to be able to switch something on without having a whole bunch of special cases or your own mappings. It's the flip side of the split between data and code.