This is a collection of notes and thoughts that I hope will benefit my fellow software wizards.
Saturday, April 14, 2012
Basic for the 8080
Tuesday, August 23, 2011
If (!expression) Considered Harmful
Let me begin by saying that I don’t feel any need to apologize for adapting the well- known aphorism, “GOTO Considered Harmful,” because I think this little essay ranks with it in importance.
Since I began programming as an almost daily activity in 1977, I have learned and used, one way or another, and for an equally wide variety of reasons, many programming languages, including the following (in no particular order): Fortran, COBOL, IBM 307 Assembler, Intel/Microsoft Macro Assembler, Perl, Basic (everything from Bill Gates’ original Basic for the Intel 4004 through current day Visual Basic .NET 2010), C, C++, C#, SQL, the DataEase Query Language, and the Microsoft batch language in all its forms. Due to the nature of my work, I often use two or more languages in the span of a single work day, and they can run the gamut from basic DOS batch files to advanced C, back to back.
The advantage of this huge diversity is that I’ve seen and used dozens of idioms and design patterns, some of which appear in only one or a handful of the languages cited above. The case of interest today is the unless idiom, found only in Perl, so far as I know. There is a simple example in the section titled “and, or, & not” of Perl Idioms. Programming Perl, the Bible of Perl, covers it in 4.3. if and unless Statements.
In terms of demonstrating the power for clarification of the unless idiom, the examples usually cited to illustrate it are pretty lame. Nevertheless, since their aim is to demonstrate the idiom, itself, their simplicity is justified. Following are some examples from production Perl scripts.
if ( !$gmatches )
if ( !$_ ) { # Is line blank?
if ( !$paramisok ) { # Warn about invalid lines.
if ( !$appending )
if ( $_ =! /^.U / ) # Skip detail lines that list unencrypted files.
The last one is especially troublesome, because it joins the result of two expressions, both expressed in negative terms, with a logical and operator (&&), which has positive semantics.
Here are a few in C, which has a similar grammar.
if ( !CloseClipboard ( ) )
if ( !IsTextUnicode ( pBOMInfoP6C->lpCharBuff , ( int ) pBOMInfoP6C->dwNBytes , &intUnicodeTestMask ) )
if ( !( utpOutFile = fopen ( pszTestCipherTextFQFN , "wb" ) ) ) {
Wait a minute, I hear you saying. The whole idea of an if statement is to do something if a condition is true.
Exactly! That’s why the not operator (!) is so harmful.
Enter the unless Idiom
Now, let’s rewrite the above expressions, starting with the Perl examples.
unless ( $gmatches )
unless ( $_ ) { # Is line blank?
unless ( $paramisok ) { # Warn about invalid lines.
unless ( $appending )
if ( $_ =! /^.U / ) # Skip detail lines that list unencrypted files.
Next, let’s give the C statements the same treatment.
Unless ( CloseClipboard ( ) )
Unless ( IsTextUnicode ( pBOMInfoP6C->lpCharBuff , ( int ) pBOMInfoP6C->dwNBytes , &intUnicodeTestMask ) )
Unless ( ( utpOutFile = fopen ( pszTestCipherTextFQFN , "wb" ) ) ) {
Hold that thought. None of the above three statements is valid C. Indeed, out of the box, it is invalid in every version of C of which I am aware, which includes Microsoft Visual C++ 6.0 through Visual C# 2010, in addition to the open source standard, GCC.
Unless in C
Please excuse the pun, but the unless idiom can be implemented in C and (and C++).
All you need is a C preprocessor capable of expanding and substituting into a one-line macro.
Let’s have the drum roll, please. Here it comes.
#define Unless(pexpr) if ( !(pexpr) )
Make this one-line macro visible to all of your C and C++ code by including it into a header that you always include, and you can clarify the intent of your code, unless you really need all those obtuse if ( !expressions.
Wait, you say, my compiler doesn’t support macros. There is another way, which is just as straightforward, still fits on one line, and goes into that header that you always include.
__inline Unless(pexpr) { return ( !( pexpr ) ); }
Fight coding horrors, one expression at a time.
Wednesday, March 17, 2010
Better Batch Files Through Command Extensions
The first batch files arrived alongside the very first versions of MS-DOS and PC-DOS. If you have been around the personal computer for many years, you undoubtedly remember, perhaps not fondly, AUTOEXE.BAT. Power users probably had two or more of them, and some means by which to choose which one to use the next time they engaged their DOS boot diskette. Later came such features as batch files that displayed menus, read inputs from the beloved DOS prompt, and made processing decisions based on those inputs. More sophisticated batch jobs accepted command line arguments, just as did the commands that were built into DOS itself, and the dozens of utility programs, such as XCOPY, that came with it.
The Primitive Command Line Interface
While those early batch files accepted arguments, sometimes called parameters, the command parser was very picky. Testing the value of an argument was extremely limited, and followed a syntax that was familiar to C programmers, but alien to most other computer users, as shown in Listing 1.
- IF "%1" == "DE4DATA" GOTO CHECK_1
IF "%1" == "de4data" GOTO CHECK_1
IF "%1" == "De4data" GOTO CHECK_1
IF "%1" == "De4Data" GOTO CHECK_1 - Listing 1 shows the only way you could have any degree of case insensitivity in your command line argument tests in MS-DOS and PC-DOS.
Listing 2, below, shows a set of tests that gives more complete coverage.
- IF "%1" == "DE4DATA" GOTO CHECK_1
IF "%1" == "de4data" GOTO CHECK_1
IF "%1" == "DE4data" GOTO CHECK_1
IF "%1" == "DE4Data" GOTO CHECK_1
IF "%1" == "DE4DAta" GOTO CHECK_1
IF "%1" == "DE4DATa" GOTO CHECK_1 - IF "%1" == "dE4DATA" GOTO CHECK_1
IF "%1" == "de4DATA" GOTO CHECK_1
IF "%1" == "de4dATA" GOTO CHECK_1
IF "%1" == "de4daTA" GOTO CHECK_1
IF "%1" == "de4datA" GOTO CHECK_1 - IF "%1" == "De4Data" GOTO CHECK_1
- IF "%1" == "De4DaTa" GOTO CHECK_1
IF "%1" == "De4DatA" GOTO CHECK_1 - IF "%1" == "DE4data" GOTO CHECK_1
- IF "%1" == "DE4Data" GOTO CHECK_1
Listing 2 requires 16 tests to provide partial case sensitivity.
That is 16 lines of code to provide partial case sensitivity for one command line argument! Although the example above is extreme, it illustrates why batch programmers usually confined themselves to terse parameter values.
The Command Parser Grows Up
Windows NT brought with it a new command processor, CMD.EXE. Unlike its ancestor, COMMAND.COM, CMD.EXE is a full fledged 32 bit console mode Windows program. It can do everything that COMMAND.COM can do, and much more. One of its most useful new capabilities arises from command extensions, which are enabled by default. You can turn them off, but why?
- IF "%~1" EQU "" GOTO DO_ALL
- IF /I "%~1" EQU "DE4Data" GOTO CHECK_1
IF /I "%~1" EQU "Home_Office" GOTO CHECK_2
IF /I "%~1" EQU "Remote_IncrEase" GOTO CHECK_3
IF /I "%~1" EQU "Home_Office_IncrEase" GOTO CHECK_4
IF /I "%~1" EQU "UTIL" GOTO CHECK_5
IF /I "%~1" EQU "QB4_Source" GOTO CHECK_6 - Listing 3 does, in its second of only 7 lines, much more than the 16 lines shown in Listing 2, because it is fully case insensitive.
Listing 3, taken from a production batch file, illustrates several of the capabilities provided by command extensions.
- The second through seventh lines modify the IF command with a new /I switch, making its tests fully case insensitive.
- All seven lines take advantage of another command extension, the tilde (~), to strip away quotation marks around the command line argument ("%~1"). I'll say more about this shortly.
- The third command extension illustrated in Listing 3 is the mnemonic relational operator, EQU. Thanks to command extensions, the IF command now supports a complete set of relational operators.
Who Can Use Command Extensions?
By default, command extensions are enabled, and are available in all versions of Windows that derive from the Windows NT code base, starting with Windows NT 4. In addition to NT 4, this includes Windows 2000, Windows XP, Windows Server 2003 and 2008, Windows Vista, and Windows 7.
Why Keep the Quotation Marks?
Despite many improvements, the command parser still has a few quirks, one of which is that it sometimes gets confused by bare words that are neither internal commands, nor recognizable program or batch file names. Thus, it is usually best to keep the quotation marks around the text against which an argument is being compared. However, if the argument is enclosed in quotation marks because it contains embedded spaces, and the test, itself, is also enclosed in quotation marks, the result is that the comparison string is enclosed in two sets of quotation marks, as shown in Table 1.
| Command Line Argument | “Document to Process.doc” |
| Old Style Comparison | If “%1” == “This is the Document.doc” |
| Outcome of Old Style Comparison | If ““This is the Document.doc”” == “This is the Document.doc” |
| New Style Comparison | If “%~1” == “This is the Document.doc” |
| Outcome of New Style Comparison | If “This is the Document.doc” == “This is the Document.doc” |
Table 1 illustrates the behavior of old style comparisons, done without the benefit of command extensions, and the new style, which leverages them to make the test work as you would expect.
Conclusion
While graphical interfaces and tools are nice, and they have a valuable place in our tool sets, so does the lowly command line interface. Batch files are fairly easy to write, test, and debug, run fast, are ready to run without a compiler or specialized interpreter, run without fuss on any machine, and can go where a program with a graphical is a waste, such as in logon and logoff scripts and scheduled tasks, none of which has a visible user interface.
While I do my share of graphical programming, batch files remain an essential part of my production environment, and occasionally become part of packages that I deliver to clients.
Use the following resources to learn more about command extensions, and start building flexible, powerful, modern batch files.
References
- http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/if.mspx?mfr=true is the official documentation of the modern IF command.
- http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/cmd.mspx?mfr=true is nominally about the new command processor, CMD.EXE, but it includes basic information about command extensions, including several ways to enable and disable them.
- http://www.robvanderwoude.com/local.php is a discussion of the SETLOCAL and ENDLOCAL commands, which includes one of several nifty tests that you can use to verify that command extensions are enabled.
- http://technet.microsoft.com/en-us/library/bb490920.aspx lists and documents the full set of relational operators that become available with command extensions enabled.
Saturday, January 16, 2010
Formatting Windoes Explorer Detail Columns to Fit Contents
If you use Microsoft Excel, you may have discovered a shortcut that is undocumented, so far as I know, that allows you to auto-fit a single worksheet column to fit its contents by double-clicking on the divider between the column and its right hand neighbor.
Today, I discovered that, not too surprisingly, the same shortcut works in the Windows Explorer. Since I work in the Detail view most of the time, I am always adjusting the widths of one or another of its columns, depending on the information that is currently of most interest. Just for fun, I decided to test the double-click. Sure enough, the width adjusted, exactly as it would in Microsoft Excel!
In both cases, you must double-click on the border at the top of the window. In Excel, this is the part where you see the column labels, A, B. C ... etc.
Likewise, in the Windows Explorer, you must double-click on the area that contains its column labels, File, Size, Type, Date Modified, ... etc.
This new knowledge will save me lots of time!
Friday, January 8, 2010
To Copy A Directory Tree, Without Copying the Files
For the first time in a long while, I just needed to duplicate the structure of a directory tree, without copying the files.
The Problem
Duplicate the structure of the directories that hold my program source code, so that I can move the archives of completed projects out of the active development directories, which are backed up nightly to an offsite location, while keeping them organized as they currently are in the development directories.
The Solution
I began by starting a conventional copy, using the Windows Explorer. As it got underway, it occurred to me that there had to be a more efficient way. A quick Google search yielded instant results.
The search term was straightforward, "copy directory structure only."
The command that got the job done, in less than a minute, is equally straightforward, as shown in Listing 1, which I copied and pasted from my command prompt window.
- Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp. - C:\Common_Data\WinZip_archives\code_libs>XCOPY "C:\Documents and Settings\David\
My Documents\Programming" /T /E - C:\Common_Data\WinZip_archives\code_libs>
- Listing 1 - The command above, XCOPY "C:\Documents and Settings\David\My Documents\Programming" /T /E, leverages the sparsely documented T switch of the faithful XCOPY command, which has been a stalwart friend since at least 1989.
More Shortcuts
OK, so I cheated.
- I used a shell extension to open my command prompt window with the target directory already made current.
- I used the Windows Clipboard to get the name of the source directory right the first time.
This second shortcut is especially noteworthy, because it calls attention to another sparsely documented feature of Windows, which is that pretty much anything that you can highlight can be copied, using the CTRL-C keyboard shortcut.
Among other things, this means that you can copy from any of the following locations.
- The address bar of a Windows Explorer window.
- The file name text box of a file property sheet.
- Any other text box.
- Some other text on property sheets. Among others, I've had success with the following.
- File create, modify, and access dates on file property sheets.
- File sizes, also on file property sheets.
- Version strings, from the Version page of the property sheet for an EXE or DLL file.
If you need text from almost anywhere in a Microsoft Windows application, try to highlight it. If it's text on the flat surface of a dialog box, try dragging the mouse across it. If you can highlight it, a quick CTRL-C puts it into the clipboard.
For Geeks Only - Why This Works
Although it isn't obvious, selectable text lives in a Text Box control. It isn't obvious that it's a control, because of the way its properties were set at design time.
- Border is set to None.
- Background is set to Transparent, or its color is set to be the same as that of the dialog box surface.
- 3D effects are off.
How these settings are applied depends on the development environment, but all can be set in any environment that lets you build custom dialog boxes and other types of forms. Among others, this includes VBA (hosted in Microsoft Access, Word, Excel, PowerPoint, and Outlook, among others), Visual Basic (classic versions 1 through 6), VB.NET, and C#. This also includes the WinBatch dialog editor, and probably numerous others.
Friday, October 23, 2009
Mind Your Trailing Backslashes
When you are testing in the Visual C# IDE, and your test setup includes command line arguments that contain backshlashes, take caution.
The Problem
Take a fairly typical (for me) command argument string such as the following.
- "C:\Documents and Settings\David\My Documents\_CLIENTS\EMCERT\"
Enter that into the Command Line Arguments text box on the Debug tab of the Project Properties page of a Console program project, put a breakpoint anywhere in its Main() function (in Program.cs). Press F5, make the Locals window visible, and display the first element of the Args array.
- [0] "C:\\Documents and Settings\\David\\My Documents\\Visual Studio 2005\\Projects\\wwBldNbrMgr\\wwBldNbrMgr\"" string
Do you see the extra quote at the end?
Now, press Shift-F5 to stop the the debugger, and change the Command Line Arguments string as shown below.
- "C:\Documents and Settings\David\My Documents\Visual Studio 2005\Projects\wwBldNbrMgr\wwBldNbrMgr\ "
Not the space that I inserted between the backslash and the quote at the end of the string.
Repeat the debug session, and display the argument.
- [0] "C:\\Documents and Settings\\David\\My Documents\\Visual Studio 2005\\Projects\\wwBldNbrMgr\\wwBldNbrMgr\\ " string
I'll bet that is the string you expected to see the first time.
The Explanation
If you've never written and debugged console mode programs in C, using a tool such as Microsoft Visual C++, you might be scratching your head. So was I, for an embarrassingly long time.
After several hours of beating my head against the wall, I realized why the debugger was behaving in this seemingly bizarre way. Under the hood, the Microsoft .NET runtime must be using the scanf() function, or a derivative of it, to read the command line. Like its cousins, printf(), fprintf(), sprintf(), and others on the output side of the standard C library, scanf() interprets escape sequences, such as \", \r, \n, \t, and a host of others, in addition to the infamous printf() format strings.
The Solution
Once I understood what was happening under the hood, the solution is straightforward.
- If the last character in your Command Line Arguments string is a backslash, enter two of them.
- If the last two characters in your Command Line Arguments string are a backslash followed by a quotation mark, put a space between them.
- If you realize that your Command Line Arguments string contains an escape sequence, such as \", \r, \n, \t, take appropriate action to prevent it from being interpreted as an escape sequence. Usually, all that's needed is to double the backslash, so that \t becomes \t, for example.
- If the string you see in the debugger is missing a character or two that you entered into Command Line Arguments, check that part of the string for an escape sequence. Pay close attention to lower case characters that immediately follow a backslash, because all escape sequences involve lower case letters and numbers.
Escape sequences are covered in the help topics for the C# programming language in MSDN library, because you can use them in regular string literals. This topic closes with a handful of references, the first of which is to the definition of a regular string literal, as that term applies to the C# programming language.
References
- http://msdn.microsoft.com/en-us/library/aa691090(VS.71).aspx, "2.4.5 String Literals," C# Language Specification, contains an exhaustive list of the escape sequences recognized by the C# string parsers.
- http://www.cppreference.com/wiki/escape_sequences, "Constant Escape Sequences" contains a table of the most common escape sequences.
- http://en.csharp-online.net/CSharp_String_Theory%E2%80%94String_literals, "C# String Theory - String Literals" is a compact, well written overview of string literals, including a brief mention of escaping, and an explanation of a simple way to avoid the escaping problem, by using verbatim strings. It's too bad that the Command Line Arguments string can't be turned into one of these. As soon as I discovered them, I began using verbatim string in a lot of my C# code. I am unaware of an equivalent type of string literal in C and C++.
Wednesday, August 12, 2009
64 Bit Integer Literals and Microsoft Visual C++ 6.0
In the course of porting an implementation of the SHA-x series of message digest algorithms (SHA-224, SHA-256, SHA-384, and SHA-512) from Unix C to Microsoft C, using the Visual C++ 6.0 compiler, I needed to convert several hard coded initialization vectors, each composed of 64 bit integer literals, to a format acceptable to the Visual C++ compiler. This was my first serious foray into 64 bit integer math, which is essential to the higher order SHA-x algorithms.
In the Unix C code, the literals look like this.
- 0xcbbb9d5dc1059ed8ULL
The Microsoft Visual C++ compiler needs something like this.
- 0xcbbb9d5dc1059ed8
Following is the shortest of the initialization vectors.
- #if defined(_MSC_VER) defined(__BORLANDC__)
uint64 sha384_h0[8] =
{0xcbbb9d5dc1059ed8, 0x629a292a367cd507,
0x9159015a3070dd17, 0x152fecd8f70e5939,
0x67332667ffc00b31, 0x8eb44a8768581511,
0xdb0c2e0d64f98fa7, 0x47b5481dbefa4fa4};
#else
uint64 sha384_h0[8] =
{0xcbbb9d5dc1059ed8ULL, 0x629a292a367cd507ULL,
0x9159015a3070dd17ULL, 0x152fecd8f70e5939ULL,
0x67332667ffc00b31ULL, 0x8eb44a8768581511ULL,
0xdb0c2e0d64f98fa7ULL, 0x47b5481dbefa4fa4ULL}; - #endif
The second block, between #else and #endif, contains the original definition, which probably works fine on any C compiler that runs on Unix or any of its many variations, especially if the target CPU has a 64 bit word, but a half-dozen or so of these made Visual C++ hurl.
I suspected that I needed only to lose the ULL size suffixes, and almost tried the change without doing any research. Amazingly, the following query, fed into Google, promptly returned exactly one link, MatLab® 7: External Interfaces. When I saw the title, I felt certain that I had hit pay dirt.
"64 bit integer literal" "visual c++ 6.0"
Repeating the first of the two search tokens, "64 bit integer literal," within the returned "page," which is a massive Adobe PDF document, instantly took me to the following section, on page 186
Specifying Constant Literal Values
To assign signed and unsigned 64-bit integer literal values, use typedefinitions int64_T and uint64_T.
On UNIX systems, to assign a literal value to an integer variable where thevalue to be assigned is greater than 2 31-1 signed, you must suffix the valuewith LL. If the value is greater than 2 32-1 unsigned, then use LLU as thesuffix. These suffixes apply only to UNIX systems and are considered invalidon the Microsoft Windows systems.
Note The LL and LLU suffixes are not required for hardcoded (literal) valuesless than 2 G (2 31-1), even if they are assigned to a 64-bit int type.
The following example declares a 64-bit integer variable initialized with alarge literal int value, and two 64-bit integer variables:
void mexFunction(int nlhs, mxArray *plhs[], int nrhs,
const mxArray *prhs[])
#if defined(_MSC_VER) defined(__BORLANDC__) /* Windows */
int64_T large_offset_example = 9000222000;
#else /* UNIX */
int64_T large_offset_example = 9000222000LL;
#endif
int64_T offset = 0;
int64_T position = 0;
This was exactly the information that I needed. Application of the same technique to my source code quickly yielded code that compiles cleanly, without errors or warnings.
I've used hundreds of similar queries with Google and other search engines, followed by hours of wasted time reading irrelevant material. More often than not, they have yielded either nothing, or a hopelessly long list of mostly useless articles.
Persistence pays, and, sometimes, you really do get lucky.