Displaying Newsletter
Issue 13, 10 Mar 2002


  In This Issue:
 
13 by Daniel Reinhold 
On this, our thirteenth issue, I thought I'd take a look at a curious idiosyncrasy of the human mind:
fear of the number 13.

As Lucy told Charlie, this is called Triskaidekaphobia. It's widespread enough that high-rise buildings routinely omit the thirteenth floor -- you go from the 12th floor directly to the 14th floor. Of course, the 14th floor is really the 13th floor, just renamed to avoid spooking people. Really dumb, huh?

Well, we're not having any of that foolishness around here. We are not going to label this newsletter as issue 14. Rest assured, gentle reader, there is no cause for alarm on your part. No harm will befall you by reading this issue (although I did accidentally bang my knuckle while typing up this article...hmmm...)

So why does the number 13 have such a bad reputation? Well, there are lots of reasons cited, including some famous examples of bad tidings linked to 13 in some way -- e.g. the Apollo 13 debacle. But the origins are likely rooted in the fact that 13 is just one past the greatly favored number 12. Twelve is a convenient number, easily divisible by 2, 3, 4, and 6. It's a useful amount for holding so many things such as eggs, roses, and months of the year. Being that 13 is one more than 12, it greatly upsets the beauty and usefulness that would have resulted if the value were only one less.

Mathematicians might explain this by noting that 13 is a prime number. But there are other common prime numbers that don't share 13's infamy. For example, 7 is a prime number, but also one of the most revered numbers. Ah yes, lucky 7. Everyone wants to number things in sevens. Seven days in a week, the seven seas, the seven wonders of the world. And in between 7 and 13 is another prime, 11. But eleven is ignored for the most part. So you have three consecutive small primes, each with wildy different perceptions: 7 is revered as lucky, 11 is largely ignored, and 13 is reviled as unlucky and a portender of doom. Go figure.

Fear of the number 13 is a variant of the more general foolishness called numerology. Numerologists attempt to bind all kinds of associations between numbers and future events. A common example would be to add up the letters of your name and judge your future happiness on the resulting number. Astrology is another example, since your birth date is basically a number. Do the twelve signs of the zodiac hold the key to your future path in life?

The essential silliness of the whole thing is that numbers are meaningless by themselves. They are only useful within the context they operate. Originally, they were only a mapping (one-to-one association) for counting. The number concept has been further extended by mathematicians to include fractions, negatives, real, complex, infinitesimal, and other, even more esoteric mappings that the average joe will never use, let alone understand. But the appeal of the basic counting integer remains.

People get so used to using numbers in their daily affairs that they attach a certain significance to them. Certain common numbers become like...friends (or enemies). How many of us have "favorite" numbers? Personally, I don't engage in such nonsense (although I've always been rather fond of 17).

As programmers, we are (or should be) greatly aware of the arbitrary nature of numbers. What is called coding is really encoding. Everything is assigned a number. A byte is just a number. An opcode is just a number. All our software machinery is a kind of complex arithmetic that juggles a huge collection of numbers according to a set of specific rules. Without the accompanying rules, the values themselves mean nothing. That fact that the letter 'A' is the number 65 (in ascii) is important, but purely coincidental -- it could have been mapped otherwise. Our code is even filled with "magic" numbers, whereby it is understood, in a somewhat ironic, tongue-in-cheek way, that the "magic" is anything but -- usually just an arbitrary number (or group of numbers) to act as an identifying tag.

What's happening here is that the tremendous capacity of the human brain to ascertain patterns is working overtime. Our ability is detect patterns is nothing short of amazing. We see horses and bunnies when we look at clouds. We see faces when we look at potatoes. We see a smile (and generate associations of warmth and friendliness) when we see a colon followed by a right parenthesis in an email. But this talent can be over-extended. We look for and find patterns even when they're not there. We get fooled.

This amazing "pattern discerning" mechanism we all possess is surely useful. The ability to extract meaningful conclusions from the tiniest drop of evidence can be vital. In earlier days, it probably often meant the difference between life and death (was that shadow lurking in the night the movement of a tiger?). But when overdone, it leads to some pretty spectacular nonsense. Need I mention the year 2000 scare?

But I am confident that rationality will win out in the end. After all, we are sensible, intelligent creatures, capable of dealing with life's difficulties with measured and thoughtful resolve. In fact, I am feeling particularly good about it at the moment, because I am wearing my lucky T-shirt, my rabbit's foot is nearby, and I am now finished with the third newsletter article (3 being the perfect number of articles). Barring any unforseen problems (knock on wood), all should be fine!

 
Implementing a general INI class by Tyler Dauwalder 
The standard library bundled with Delphi, Borland's visual Pascal programming environment for Windows, includes a handy little class named TIniFile. TIniFile provides a simple but useful interface for accessing and manipulating ini files, a text-based file format for storing structured data.

Using TIniFile, one can read, write, load, and store things like user preferences or even your program's main data structures in a user-editable text-based format with relative ease. Without something like TIniFile, accomplishing such a feat can be a chore. Oftentimes this results in the use of a proprietary, binary format that is uneditable by end users. I missed having TIniFile around when I started programming in C++, so I decided to write my own.

Borland's TIniFile class supported the reading and writing of integers, floating point numbers, boolean values, and character strings. My IniFile class supports all four of those data structures, and also allows the reading and writing of arbitrary binary data. This data is encoded into an unintelligible mess of ASCII characters, so storing data this way eliminates the likelihood that anyone will be able to manually edit it with any sort of ease. This usually isn't desirable, but being able storing arbitrary chunks binary data can be quite handy at times.

My BIniFile class, a subclass of IniFile, also supports the reading and writing of BMessages to and from ini files. Such BMessages are simply flattened and written out as ASCII encoded binary data. Thus, they too are effectively manually uneditable.


How it works

(If you're curious, you can click here to view the public interfaces of IniFile and BIniFile before going into more detail).

IniFile stores the ini file data structure directly in memory. An ini file is composed of a number of named sections, each of which contains a number of named keys. With each key is associated a unique value. Thus, any value in the ini file may be uniquely identified by its section and key.

Load() and Store() are used to load and store an entire ini file from or to a given file. A filename can also be passed into the constructor if you wish to load a file when you create the object. If a file error occurs while attempting to load the given file, the constructor will throw an IniFile::EFileError exception. By default, Load() and Store() simply return false when such an error occurs, but if specified they will also throw an IniFile::EFileError. Also, all IniFile and BIniFile functions will throw an IniFile::EInsufficientMemory exception if a dynamic memory allocation fails.

Assignment and copy construction are implemented to make complete copies.

Clear() restores the object to an empty state.

For each data type IniFile supports, there is a corresponding pair of WriteTYPE() and ReadTYPE() functions (some types have two or three ReadTYPE() functions to support different dynamic memory allocation methods). The first argument is always the name of the section that owns the key/value pair you wish to read to or write from. The second argument is always the name of the key whose value you wish to read or write. The remaining arguments are type dependent.

Dynamic memory allocation failures aside, all IniFile::WriteTYPE() functions are guaranteed to succeed. BIniFile::WriteMessage() will only fail if unable to flatten the given message.

Reading is a different matter. All data is stored internally as 8-bit character strings, so for most data types, some sort of conversion is necessary on a call to ReadTYPE(). Different measures are taken for different data types to account for times when this conversion fails (or when the requested Section/Key/Value tuple does not exist):

ReadInt(), ReadFloat(), ReadBool(), ReadString(), ReadMessage(): All of these functions accept a default value that is returned in the event of a conversion error or missing value. In the case of ReadString() and ReadMessage(), a copy of the given default value is returned (unless the given default value is NULL, in which case NULL is returned).

ReadData(): These functions return the number of bytes that were copied into the result. If 0 is returned, the result may also be NULL.


ReadTYPE() pairs and triplets:

As you may have noticed, there are two versions of ReadString() and ReadData(), and three versions of ReadMessage().

For ReadString() and ReadData(), the first version of each allows you pass in a pre-allocated string or data buffer into which the function reads as much of the requested data as possible. The second version returns a newly allocated string or data buffer (thanks to malloc()) for which you are now responsible. When you're through using it, it's your job to "free(result)" the pointer returned to you. The upside of the second type of function is that the IniFile object knows how much data there is to read, and thus can allocate a data structure of sufficient size.

For the first two versions of ReadMessage(), the Message parameter is a reference to a pre-allocated BMessage to Unflatten() into. The reference returned by these functions is always just a reference to the Message parameter (provided as a convenience). The four argument version of ReadMessage() requires a reference to a default value, whereas the three argument version simply calls the four argument version with Message as both the Message parameter *and* the Default parameter. If the function is unable to Unflatten() into Message the data read from the given section and key, then Message is assigned the value of Default.

The third version of ReadMessage(), i.e. the one that returns a pointer to a BMessage, always returns a newly allocated BMessage (or NULL). Thus, even when the default value is returned, it's a new copy of the BMessage pointed to by Default, not Default itself. Therefore it's always safe (and necessary) to "delete result" whatever the third version of ReadMessage() returns once you're through using it.


How is it used?

A normal use of IniFile or BIniFile might be something like the following:

When an application is started, it loads a specific ini file used to store user preferences into an IniFile object. To account for the first time the program is run (or any time the ini file may be improperly edited or deleted by the user), the program supplies some sort of default value to be returned in the event of a read failure for each key it attempts to read from the IniFile object (or for ReadData() calls, the number of bytes read is monitored, and a default chunk of data substituted if the wrong amount of data is read, or if the data is improperly formatted).

The user preferences of the program are set based on the values read in, and the program runs as normal. When the user requests that the application close itself, the application creates a new ini file object, writes all of its current settings into it, and saves it to disk. Thus, the next time the user runs the program, his/her settings from the last session will be restored.


What does an ini file look like?

For those who aren't familiar with ini files, here's a short example of what an ini file might look like:

[ This is the first section ]
   Where's the data?     =     Here's the data      ; This is a comment
This line will fail  =  ; because it has no value to match the key
  = This line also fails because it has no key before the first = 
This one's okay, though   =   Properly Formatted == Properly Loaded
[Section2]
Key=Value
Key2=12345
3=3.0
[ This Is An Odd Name For A Section [I think]]
___ = ___

This would be parsed into the following IniFile structure:
"This is the first section"
    "Where's the data?" >> "Here's the data"
    "This one's okay, though" >> "Properly Formatted == Properly Loaded"
"Section2"
    "Key" >> "Value"
    "Key2" >> "12345"
    "3" >> "3.0"
"This Is An Odd Name For A Section [I think"
    "___" >> "___"
Note that embedded whitespace is allowed, but leading and trailing whitespace is removed.


What file format does IniFile recognize?

Should you ever feel like manually editing an ini file to be loaded by the IniFile class, it's also useful to know just what the class is expecting. Here's a quick rundown:

  • Ini files are read from the top down.
  • All lines before the first section line are ignored.

  • A section line is a line where the first non-whitespace character is a left square bracket "[".
  • The text in between the first left square bracket and the first right square bracket "]" designates the section's name, which may contain left square brackets.
  • Whitespace may be embedded in section names, but leading and trailing whitespace is removed.
  • A section owns all the data lines that follow it up until a new section line is encountered.
  • A given section may appear multiple times in a given file.

  • A data line designates a key/value pair.
  • The key is all text to the left of the first equal sign "=" in the line.
  • Whitespace may be embedded in the key, but leading and trailing whitespace is removed.
  • The value is all text to the right of the first equal sign in the line, and may contain equal signs.
  • Whitespace may be embedded in the value, but leading and trailing whitespace is removed.

  • Comments begin with a semicolon ";" and extend to the end of the line.
  • All text following a comment is ignored up until the end of the line.
  • A backslash preceding a semicolon has no effect; the semicolon still starts a comment.

  • Any line that does not match as a section line or a data line is ignored.


Limitations

There are a few limitations in the current implementation (there may also be many more I'm not thinking of right at this moment ;-):

  1. Only 8-bit character sets are supported. To the best of my knowledge, it should handle UTF-8 just fine, because the only chacters with any special meaning are all part of the ASCII character set. Basically, any 8-bit character not in the set { \n \r \t \s ; [ = } of ASCII characters is a valid section, name, or value character. "[" and "=" are also valid in certain places, as described previously.

  2. These classes are not inherently thread safe. It's up to you to handle any synchronization necessary if you wish to use them concurrently in a multi-threaded manner.

  3. Strings written with WriteString() may not contain newlines, carriage returns, or semi-colons. Or rather, nothing is stopping you from including said characters in a string passed to WriteString(), but if the ini file is stored and then reloaded, only text up to the first newline, carriage return, or semi-color will be returned when you call ReadString(). I'd like to support automatic conversion to escaped '\n', '\r', and '\;' sequences, but I didn't have time to do so for this version.

  4. If you re-read values you've written before storing and reloading the ini file, you may get different results than you would otherwise. For example, if you write the string
    "    This is a string      ; it sure is....     "
    and then read it back before storing and re-loading, you'll get back just what you put in. But if you first store the ini file and then read it back in, ReadString() will return "This is a string". I'd like to get rid of the inconsistency someday, but again, there wasn't enough time for this version.

  5. The set of sections in an ini file and the set of keys in a section are currently stored as linked lists. This makes searching through large ini files with many sections and/or many keys a slow process. Someday I'll replace the linked lists with something a little more scalable.

  6. I'm not sure if these classes will work properly on big endian architectures without modification. Make sure you run the verification program in the test/ subfolder of the archive if you decide to try the classes out on a PPC machine.

  7. Being able to write out a flattened BMessage is handy, but it'd be a lot nicer to support writing out of built-in types to a user-editable format. I.e. I'd like to have pairs of functions like WriteRect(BRect rect) and ReadRect() that would write out a string
    "(rect.left, rect.top, rect.right, rect.bottom)"
    and parse it back into an actual BRect. Again, something to add in the future.


What's in the package?

The root of the archive contains the header and source files for IniFile and BIniFile, a base Jamfile and Jamrules file, and a number of subfolders.

The example/ subfolder contains an example BeOS GUI app that saves and restores its state in ~/config/settings/IniExample.ini using the BIniFile class. The window's position and size, as well as the contents of the app's text control and slider, are saved and restored in between sessions. In addition, the program has a special control that accepts drag-and-drop parcels. When you drop something on the control, it prints out the contents of the corresponding BMessage, and then holds on to the message until you drag it back off or drop a new message. Dropped messages are also saved between sessions to the program's ini file.

The test/ subfolder contains a test program that verifies the functionality of the IniFile and BIniFile classes.

The CppUnitShell/ subfolder contains a subset of the CppUnit testing library written by Eric Sommerlade, Michael Feathers, and Jerome Lacoste. The complete version is available from http://cppunit.sf.net/. The CppUnitShell/ folder also contains CppUnitShell, a generic command-line testing interface class built around CppUnit. Both are used by the test program in test/. CppUnit is released under the LGPL, and thus, for the sake of convenience, so is CppUnitShell. Everything else in the package is released under the same license as OpenBeOS, the less-restrictive MIT license.

The IniDump/ subfolder contains a small command line program called DumpIni that reads in an ini file and dumps its contents out to standard out. The file Example.ini is just like the example ini file shown above.

There are Jamfiles included for every folder. Running jam from the root of the archive should compile and link the entire package. I've included a precompiled version of jam built by our very own buildmeister Ithamar, since it's likely many of you won't have a copy handy.

And finally, this archive is targeted at BeOS specifically, but the IniFile class is designed to be portable between platforms. The verification program in /test compiles and runs (successfully) on Linux with no modifications (other than removing the BeOS specific BIniFile tests). I haven't had a chance to try it on other platforms yet, although the original version of IniFile was written on a Windows machine, so I would expect this version would work as well.


Conclusion

Ini files are a handy way to store structured data in a user-editable format. IniFile and BIniFile make it possible to take advantage of ini files without having to handle any of the necessary parsing, data structure management, or type conversion.

Those who are interested in ini files might also want to check out the InitFile class that's part of Allen Brunson's TropicLib, available from http://www.beosdevelopers.org/brunsona/tropiclib.html. It is similar to the classes presented here, but takes a slightly more structured approach to handling improperly formatted or missing values when loading an ini file from disk.


Source Code:
IniFile_v1.00_BeOS.zip

 
Time for a CEO reality check by Michael Phipps 
Yeah, this is going to be one of those editorials. When I am not working on OBOS, I work in the telecom industry. And, as a part of my job, I read the telecom trade rags. For those of you who haven't had the dubious pleasure, there are only a few stories that ever get run:
  • Company A is filing for bankruptcy
  • Company B is merging with company C
  • Company D is borrowing a gazillion dollars
  • Company E is suing company F for a gazillion dollars

and, my "favorite"

  • Company G is laying off N% of their work force.

You will notice, perhaps, what fundamental items are missing from the above list. Things like technology breakthroughs. Or better service plans. Anything useful. No, instead they act like spoiled teenagers. The dot coms have NOTHING on the telco's. These companies have billions of dollars, each, to build infrastructure that no one needed so that they could make people feel less guilty about the Baby Bells being a monopoly. These companies are going bankrupt, selling off assets and laying people off because it never occured to them to be financially responsible. They saw hundreds of millions of dollars as their "birthright", blew money like it was going out of style, and then must lay people off.

They fail to take into account the lives and efforts of the people that work for them. They treat people as expenses. Whole departments are terminated or outsourced. Never the management, who decided that they needed to be in that business. Never the executives, who have immense salaries and BMWs. Those people, who MADE the decisions, couldn't be wrong. The people who carried them out were obviously the problem. How many line animals (like myself) does it take to match the money that one executive makes? Why, when people do what they are told, are they dispensible, while the decision makers are immune?

Where are the Vulture Capitalists? It is *THEIR* money being flushed down the toilet. In big buckets. They seem to have no interest in overseeing companies to ensure that they are successful. Then again, the VC's don't seem to have any more intelligent ideas than the management they should oversee.

Let me just make a few humble suggestions to any CEOs out there who might be listening:

1) Success is probably in spite of you, not because of you. Skip the fat bonus and instead...

2) ... put money in the bank. Yeah, I know, this isn't popular advice. Money in the bank doesn't make as much as invested money. But when your infallible wisdom suddenly goes south, have a safety cushion so that you can...

3) never lay people off. There are a dozen good reasons for this. One is that it decimates the morale of those who are left. Another is that it is a bad sign to the market. You don't have a *clue* how to manage, otherwise you wouldn't have been in that business. Finally, it hurts people, if that matters to you.

4) Hold decision makers accountable. Quit punishing the innocent. Make every manager repeat after me: "The buck stops here."

5) Meetings are not doing something. They are planning, not implementation. The best way to make a late project later is to micromanage it.

6) Don't forget your bread and butter. So many companies run after every trend because some exec read about it in the Delta Airlines Flight Magazine. XML is a good example. Java was another. Both are technologies with a place. Rarely the place that people tried to put them in.

I don't know, maybe I oversimplify. Mom taught me to live pretty simply. Put aside for lean times. Don't go into debt, unless you really, really have to. And don't marry someone until you are really, really sure. It seems like CEOs should try a little more of Mom's advice and a lot less of the professional money manager's.