Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.

Channels ▼


C Programming

Source Code Accompanies This Article. Download It Now.


Myst, CD-ROMs, and CEnvi

Okay, the column's late again. Blame it on two brothers named Robyn and Rand Miller. They built Myst, a computer game marketed by Broderbund Software. I am not usually a game player, preferring to spend my idle hours away from the computer. But someone told me to look at Myst to get an idea of the potential for computer graphics and visual simulation using contemporary hardware. I did and got drawn into Myst's strange universe of islands, ages, sights, and sounds. It has interfered with my dreams and altered my usually relaxed view of reality. Try this thing only if you have plenty of spare time. It is compelling. I think I'll go play it now.

Information Retrieval

My attention returns to static information-retrieval engines. Long an interest of mine, the subject is particularly relevant now because of the plethora of information-based CD-ROMs. There are good ones and bad ones, both with respect to the information itself and to the search-and-display engines. As a developer, I am more concerned with the engines.

The Microsoft Developer Network (MSDN) CD-ROM is a particularly good research-and-development tool, and every DOS and Windows programmer should have it. It uses a search-and-display engine that resembles the Windows Help system, complete with graphics and hypertext links. It contains most of Microsoft's developer documentation along with the full text of several Microsoft Press books. There are free tools and source code, too.

The Borland C++ compiler CD-ROM contains all its documentation in Adobe Acrobat format, which displays the pages of books on the screen in their printed format along with search functions. The display engine is excellent. The search engine is primitive. There are no hypertext links like the ones in the MSDN CD-ROM. I searched Borland's online DOS manual for the single word "TSR," and the program took about a minute to find the keep function documentation. The same search of the MSDN CD-ROM takes less than a second to find and lists 65 topics. That's the way a text-search engine is supposed to work.

Recently I got the J.F.K. Assassination: A Visual Exploration CD-ROM, which contains the text of two books, the complete Warren Commission Report, several animated simulations, and film clips, including the Zapruder film. The search and display engine seems to be the same one or similar to that of the MSDN CD-ROM.

The JFK product has a proconspiracy bias, which is obvious when you compare its treatment of conspiracy books to its treatment of Case Closed, by Gerald Posner (Random House, 1993). Case Closed concludes that Oswald acted on his own and was such a flake that no respectable Federal agent, Communist, or mafioso would have associated with him, much less trusted him with such a responsibility. Posner's work is a monumental piece of research that delivers its conclusions with sound technical and literary responsibility and without emotion, hysteria, or agenda. The book does not appeal to closed minds. It does, however, deserve more respect than the JFK CD-ROM gives it.

Bias notwithstanding, the JFK CD-ROM is a model of information retrieval. I use it here as an example because it has everything--text, sound, video, animation, graphics, hypertext, and an intuitive user interface. It uses big graphic buttons to jump between major topics, and the graphics, animations, and photos are excellent. The search engine is comprehensive. I did a search for the word "Monroe" and instantly got references to four topics: the Monroe doctrine, Kennedy's alleged relationship with Marilyn, a reading test that Oswald took, and an acknowledgment in the Warren Commission Report. Hypertext links are everywhere and are comprehensive. A lot of preparation went into this work. If you are interested in electronic publishing, this product from Medio Multimedia Inc. (Redmond, WA) is one to study.

Several years ago in this column, I published the source code to a text-indexing system that included a Boolean query engine. More recently, I applied those concepts to a shareware product for an associate. We implemented a Windows program that retrieves and displays verses of the King James version of the Bible. His interest in the project was to distribute the program as shareware. Mine was to further my research in static text storage and retrieval. The Bible project was particularly well suited to these objectives because the text is in the public domain and the Bible is organized into a hierarchical structure of testaments, books, chapters, and verses. The largest verse is relatively small (less than 600 characters), and the text is certainly static--it won't be changing anytime soon.

The project included tasks common to any such project--development of the system itself, a Windows Help database, and a Windows Setup program. To build a text database, you must apply a certain amount of data analysis to determine the organization of the database and its presentation, the distribution and frequency of words, and the best methods for compression, indexing, and retrieval. I wrote C programs that analyzed the raw text and subsequent C programs to build the database. The retrieval engine is a Windows DLL written in C. The user interface is written in Visual Basic. The shareware venture did not pan out, and the program is now in the public domain. I'll be discussing the software aspects of this project and providing source code over the next few columns.


Last month, I described Quincy's architecture and the front end of Quincy's translator. It is time to get into the hard parts--the compiler and interpreter. It is time, yes, but I demur. Quincy's techniques for compiling and interpreting source code defy description. They do not follow traditional translator logic, and they are difficult to work with and explain. When I added the ANSI extensions to the original K&R interpreter, I found out just how true that was. A language translator needs to be driven by an unambiguous grammar, but Quincy's parsing logic is brute force, following a thread that reflects my understanding of the language rather than a grammar.

Quincy's overhaul was to prepare it for use in my C tutorial book (Al Stevens Teaches C, M&T Books, 1994), and that work is done. Throughout the project a persistent notion nagged at me. When Visual Basic came out, I was sure that Visual C would soon follow. It did not. All contemporary C- and C++-based visual development environments that I have seen launch compilers. They are not interpreters that work like Visual Basic. It seems to me that such a program would be useful and, if done properly, wildly successful. First you would need a reasonably efficient C interpreter upon which to base the visual tool. Quincy is not reasonably efficient, sacrificing execution speed for a rapid development cycle to support its tutorial role. Quincy could, however, be improved.

Lately I have been working on a more conventional translator/interpreter using the grammar in the ANSI Standard C specification as a guide. The same grammar is published in the second edition of Kernighan and Ritchie. Do not interpret this as an announcement of a visual C project_but that notion keeps nagging.

There are some things about the Standard C grammar that I do not understand. Why does the parameter-declaration specification consist of declaration-specifiers, which permit storage-class-specifiers, instead of the specifier-qualifier-list, which does not? According to the grammar, you can code typedef, extern, static, auto, and register as part of the parameter declarations in a function prototype or header. Of course, no compiler allows that. The grammars in Kernighan and Ritchie's second edition and the C++ Annotated Reference Manual have the same seeming anomaly, so there must be something that I am missing.

I shall defer further discussion of Quincy's existing translator and interpreter until I decide its future. For now, the source code is on CompuServe, DDJ Online, and ftp.mv.com, and, as usual, I am available on CompuServe to answer questions.


This is the December issue, coming when programmers are looking for stocking stuffers, and I've found a good one. Every now and then a programming tool serves a particular need better than anything else. CEnvi, a shareware product from Nombas (P.O. Box 875, Medford, MA 02155, 617-391-6595, [email protected], $45.00 for the registered version) is such a tool. It is a language interpreter that implements a subset of C.

The subset language is called "Cmm," standing for C-minus-minus, which implies that some parts of C are missing. They are, indeed. Cmm does not have type declarations or pointers. Imagine a C program with no declarations, no pointers, pointer notation, or address constants, and you have a Cmm program, which CEnvi interprets. Cmm variables are implicitly typed by the context in which the program uses them. Basic programmers are familiar with such implicit, dynamic typing. Cmm programs do not have pointers and addresses because arguments are passed by reference unless you specifically tell CEnvi to pass an argument by value. You can even have structures without structure declarations. Structures evolve as the program assigns values to implicitly declared structure members. CEnvi's author maintains that keeping track of memory is what makes C hard to understand for the newcomer, so he has removed everything from the language that deals with memory. The programmer is unaware of the address or format of anything. Even so, CEnvi supports arrays, structures, and strings through implicit typing.

Now, what good is it? Although designed primarily as a program-development environment, Cmm is better as a batch language. CEnvi executes command-line batch files written in the Cmm dialect of C. That is good news for C programmers. Until now, OS/2 programmers had to learn ReXX to build complex command files, and DOS programmers were stuck with the clumsy BAT language. CEnvi provides almost full batch capabilities but with a subset of C. The only thing I can see that it is not good for is loading TSRs. CEnvi is not a shell program. Its executable terminates when the interpreting completes. Any TSRs that it spawned would be loaded above CENVI.EXE.

There are three versions of the interpreter: DOS, Windows, and OS/2. Having used the DOS and OS/2 versions, I rarely use the DOS batch language or ReXX. I got the OS/2 shareware version from a CD-ROM and learned that CEnvi nicely solves one of my OS/2 problems. I prefer to open DOS and OS/2 command-line windows with a specific font and cannot find a way to override the defaults from the OS/2 desktop. CEnvi includes access to the PM kernel and examples that open DOS and OS/2 sessions with different positions, window sizes, and fonts. CEnvi uses features of both batch environments that allow you to embed Cmm code into .BAT and .CMD files. Most of the file is Cmm code, but users execute the command file just as they would any other. By using this feature and the examples that Nombas provides, I was able to build a command file that opens DOS and OS/2 command-line windows just the way I like them.

CEnvi's shareware version has an annoying nag screen that begs you to pay up and register. It runs too often and takes too long. Nombas says that they have eased up on it in newer versions. I suspect that the policy has caused potential registered users to scrap the program in exasperation without exploring it enough to see its potential. On the other hand, programmers are notorious for not registering shareware unless there is a compelling reason to do so.

CEnvi can generate a stand-alone executable for distribution to users who do not have the run-time interpreter. To enhance its batch script capabilities, CEnvi uniquely supports operating-system environment variables. An identifier of all uppercase letters is treated as an environment variable. If you use a special version of the run-time module, your program can set environment-variable values that persist beyond the execution of the program.

I was curious about how easy it was to convert a running C program into the Cmm dialect. One of the tutorials that Quincy runs is a simple tic-tac-toe program. Since it uses a number of C idioms to demonstrate language features, including multiple-dimensioned arrays, I decided to port it to Cmm. The job took about ten minutes. Mostly what I did was remove declarations and pointer notation and substitute CEnvi's screen and cursor library functions for my own. The only problem I had was one that the CEnvi documentation warns about. (As usual, I waded in without RTFM.) If a function modifies one of its parameter variables, by default it modifies the caller's argument variable if the argument is an l value. After I realized that, I fixed all such parameter references, and the program ran fine. Listing One is ttt.cmm, the Cmm version of the game program.

CEnvi has a substantial subset of the Standard C library and some extra functions to support writing Windows programs and DOS and OS/2 command-line programs. The documentation is typical shareware with a cottage-industry look and feel, but with enough information to get programming underway in short order. The package installs easily in any of the three environments and comes with an abundance of example programs. When you call Nombas, you speak to Brent Noorda, the programmer, CEO, and guy who sweeps floors at Nombas. His mother has never seen his name in print, so here it is.

Naturally, Cmm code is incompatible with C. The name, pronounced "C-Envy," reflects the developer's reason for building the interpreter. He says that he was envious working on small systems that do not have big C development environments. (Others might see an analogy to a well-known Freudian complex, but we at DDJ are above making such tawdry observations, so I shall decline to do so, thank you very much.) I'm not sure that the argument holds up if you still have an old copy of Turbo C 2.0. I used to run the tcc command-line compiler on a slow 8088 laptop with 640K and only one 720K diskette drive, and I had room left over for an editor and some source code.

CEnvi certainly does not need as much disk space as contemporary C/C++ compilers. There are no huge libraries or long lists of header files. Everything is built into the interpreter. It may be the smallest development environment for Windows programming ever, although the Windows programs that it runs tend to be slow even when compared to those of Visual Basic. Besides not having the typical bloat, CEnvi also does not have an integrated editor or debugger. You have to return to the old ways, using the editor of your choice and inserting getchar and printf calls in the code to form breakpoints and examine variables.

The future for CEnvi is not so much as a substitute batch language, at which it shines, nor as a tiny development environment, for which there may be no real market, but in its potential as an application script language. Nombas plans to release a programmer's toolkit that lets you link the interpreter into your application and use Cmm as a script language very much along the lines of Word Basic or the Brief macro language. Its success will depend on whether users can be expected to use a C dialect for macros. Certainly programmers can do that, but if the example text editor is any indication of the interpreter's performance, CEnvi needs to get a lot faster before you would use it to run macros of any consequence. I understand the problem. Quincy is about as slow, although that was a conscious design decision to trade off the tutorial's run time for compile-time efficiency. Nombas is aware of the problem and is looking into it. In the meantime, CEnvi has virtually replaced ReXX and the DOS batch language in my office.

"C Programming" Column Source Code

Quincy, D-Flat, and D-Flat++ are available to download from the DDJ Forum on CompuServe, DDJ Online, and on the Internet by anonymous ftp. See page 3 for details. If you cannot get to one of the online sources, send a diskette and a stamped, addressed mailer to me at Dr. Dobb's Journal, 411 Borel, San Mateo, CA 94402. I'll send you a copy of the source code. It's free, but if you want to support the Careware charity, include a dollar for the Brevard County Food Bank.

Listing One

/* ------------------------------ ttt.cmm -------------------------- */
/* A simple game of tic-tac-toe written in Cmm  */
/* ------------------------------------------------------------------*/
#define TRUE 1
#define FALSE 0
#define BELL 7
/* ---- board markers ---- */
#define PLAYER 'X'
#define COMPUTER 'O'
#define FREE ' '
/* --- game position on screen --- */
#define LEFT 10
#define TOP   5
/* --- game board --- */
board = "         ";
/* --- winning combinations --- */
wins = {
    /* --- winning rows --- */
    { 1,2,3 },
    { 4,5,6 },
    { 7,8,9 },
    /* --- winning columns --- */
    { 1,4,7 },
    { 2,5,8 },
    { 3,6,9 },
    /* --- winning diagonals --- */
    { 1,5,9 },
    { 3,5,7 }
    ch = 'y';
    while (ch == 'y')    {
        memset(board, FREE, 9);
        /* --- get player's first move --- */
        if ((mv = getmove()) == 0)
        /* --- set computer's first move --- */
        if (mv != 5)
            setpiece(5, COMPUTER);    /* center if available  */
            setpiece(1, COMPUTER);  /* upper left otherwise */
        moves = 2;
        while (moves < 9) {
            getmove();            /* player's next move */
            if (won())    {
                message(1, "You win");
            if (moves == 9)
                message(1, "Tie");
            else    {
                /* --- find computer's next move --- */
                if ((mv = canwin(COMPUTER)) != 0)
                    /* --- win if possible --- */
                    setpiece(=mv, COMPUTER);
                else if ((mv = canwin(PLAYER)) != 0)
                    /* --- block player's win potential --- */
                    setpiece(=mv, COMPUTER);
                if (won())    {
                    message(1, "I win");
        message(2, "Play again? (y/n) ");
        ch = getch();
/* --- find next available open space for a dumb move --- */
    lmv = -1;
    for (i = 0; i < 9; i++)
        if (board[i] == FREE) {
            lmv = i+1;
            setpiece(=lmv, COMPUTER);
            if (canwin(COMPUTER))
            setpiece(=lmv, FREE);
    if (lmv != -1)
        setpiece(=lmv, COMPUTER);
/* --- get the player's move and post it --- */
    mv = 0;
    while (mv == 0)    {
        message(0, "Move (1-9)? ");
        mv = getch();
        mv -= '0';
        if (mv < 1 || mv > 9 || board[mv-1] != FREE)    {
            mv = 0;
    setpiece(=mv, PLAYER);
    return mv;
/* ------ test to see if the game has been won ------- */

    for (i = 0; i < 8; i++) {
        pl = wins[i][0]-1;
        if (board[pl] == FREE)
        for (k = 1; k < 3; k++)
            if (board[pl] != board[wins[i][k]-1])
        if (k == 3)
            return TRUE;
    return FALSE;
/* --- test to see if a player (n) can win this time; 
       return 0 or winning board position --- */
    for (i = 0; i < 8; i++)
        if ((w = trywin(n, i)) != 0)
            return w;
    return 0;
/* ---- test a row, column, or diagonal for a win;
        return 0 or winning board position --- */
trywin(n, wn)
    nct = 0;
    zct = 0;
    for (i = 0; i < 3; i++)    {
        pl = wins[wn][i]-1;
        if (board[pl] == FREE)
            zct = i+1;
        else if (board[pl] == n)
    if (nct == 2 && zct)
        return wins[wn][zct-1];
    return 0;
/* ------ display the tic-tac-toe board ------ */
    ln1 = "   \xb3   \xb3";
    ln2 = "\xc4\xc4\xc4\xc5\xc4\xc4\xc4\xc5\xc4\xc4\xc4";
    for (y = 0; y < 5; y++)    {
        printf((y&1) ? ln2 : ln1);
/* ---- set a players mark (O or X) on the board ---- */
setpiece(pos, mark)
    board[--pos] = mark;
    col = pos / 3;
    row = pos % 3;
    ScreenCursor(LEFT+row*4+1, TOP+col*2);
/* ---- message to opponent ---- */
message(y, msg)
    ScreenCursor(LEFT, TOP+8+y);

Copyright © 1994, Dr. Dobb's Journal

Related Reading

More Insights

Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.