home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Journal 1990 - 1995 / CUJ.iso / unix / 1990.txt next >
Encoding:
Text File  |  1996-02-07  |  3.3 MB  |  96,735 lines

Text Truncated. Only the first 1MB is shown below. Download the file for the complete contents.
  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12.  
  13.  
  14.  
  15.  
  16.  
  17.  
  18.  
  19.  
  20.  
  21.  
  22.  
  23.  
  24.  
  25.  
  26.  
  27.  
  28.  
  29.  
  30.  
  31.  
  32.  
  33.  
  34.  
  35.  
  36.  
  37.  
  38.  
  39.  
  40.  
  41.  
  42.  
  43.  
  44.  
  45.  
  46.  
  47.  
  48.  
  49.  
  50.  
  51.  
  52.  
  53.  
  54.  
  55.  
  56.  
  57.  
  58.  
  59.  
  60.  
  61.  
  62.  
  63.  
  64.  
  65.  
  66.  
  67.  
  68.  
  69.  
  70.  
  71.  
  72.  
  73.  
  74.  
  75.  
  76.  
  77.  
  78.  
  79.  
  80.  
  81.  
  82.  
  83.  
  84.  
  85.  
  86.  
  87.  
  88.  
  89.  
  90.  
  91.  
  92.  
  93.  
  94.  
  95.  
  96.  
  97.  
  98.  
  99.  
  100.  
  101.  
  102.  
  103.  
  104.  
  105.  
  106.  
  107.  
  108.  
  109.  
  110.  
  111.  
  112.  
  113.  
  114.  
  115.  
  116.  
  117.  
  118.  
  119.  
  120.  
  121.  
  122.  
  123.  
  124.  
  125.  
  126.  
  127.  
  128.  
  129.  
  130.  
  131.  
  132.  
  133.  
  134.  
  135.  
  136.  
  137.  
  138.  
  139.  
  140.  
  141.  
  142.  
  143.  
  144.  
  145.  
  146.  
  147.  
  148.  
  149.  
  150.  
  151.  
  152.  
  153.  
  154.  
  155.  
  156.  
  157.  
  158.  
  159.  
  160.  
  161.  
  162.  
  163.  
  164.  
  165.  
  166.  
  167.  
  168.  
  169.  
  170.  
  171.  
  172.  
  173.  
  174.  
  175.  
  176.  
  177.  
  178.  
  179.  
  180.  
  181.  
  182.  
  183.  
  184.  
  185.  
  186.  
  187.  
  188.  
  189.  
  190.  
  191.  
  192.  
  193.  
  194.  
  195.  
  196.  
  197.  
  198.  
  199.  
  200.  
  201.  
  202.  
  203.  
  204.  
  205.  
  206.  
  207.  
  208.  
  209.  
  210.  
  211.  
  212.  
  213.  
  214.  
  215.  
  216.  
  217.  
  218.  
  219.  
  220.  
  221.  
  222.  
  223.  
  224.  
  225.  
  226.  
  227.  
  228.  
  229.  
  230.  
  231.  
  232.  
  233.  
  234.  
  235.  
  236.  
  237.  
  238.  
  239.  
  240.  
  241.  
  242.  
  243.  
  244.  
  245.  
  246.  
  247.  
  248.  
  249.  
  250.  
  251.  
  252.  
  253.  
  254.  
  255.  
  256.  
  257.  
  258.  
  259.  
  260.  
  261.  
  262.  
  263.  
  264.  
  265.  
  266.  
  267.  
  268.  
  269.  
  270.  
  271.  
  272.  
  273.  
  274.  
  275.  
  276.  
  277.  
  278.  
  279.  
  280.  
  281.  
  282.  
  283.  
  284.  
  285.  
  286.  
  287.  
  288.  
  289.  
  290.  
  291.  
  292.  
  293.  
  294.  
  295.  
  296.  
  297.  
  298.  
  299.  
  300.  
  301.  
  302.  
  303.  
  304.  
  305.  
  306.  
  307.  
  308.  
  309.  
  310.  
  311.  
  312.  
  313.  
  314.  
  315.  
  316.  
  317.  
  318.  
  319.  
  320.  
  321.  
  322.  
  323.  
  324.  
  325.  
  326.  
  327.  
  328.  
  329.  
  330.  
  331.  
  332.  
  333.  
  334.  
  335.  
  336.  
  337.  
  338.  
  339.  
  340.  
  341.  
  342.  
  343.  
  344.  
  345.  
  346.  
  347.  
  348.  
  349.  
  350.  
  351.  
  352.  
  353.  
  354.  
  355.  
  356.  
  357.  
  358.  
  359.  
  360.  
  361.  
  362.  
  363.  
  364.  
  365.  
  366.  
  367.  
  368.  
  369.  
  370.  
  371.  
  372.  
  373.  
  374.  
  375.  
  376.  
  377.  
  378.  
  379.  
  380.  
  381.  
  382.  
  383.  
  384.  
  385.  
  386.  
  387.  
  388.  
  389.  
  390.  
  391.  
  392.  
  393.  
  394.  
  395.  
  396.  
  397.  
  398.  
  399.  
  400.  
  401.  
  402.  
  403.  
  404.  
  405.  
  406.  
  407.  
  408.  
  409.  
  410.  
  411.  
  412.  
  413.  
  414.  
  415.  
  416.  
  417.  
  418.  
  419.  
  420.  
  421.  
  422.  
  423.  
  424.  
  425.  
  426.  
  427.  
  428.  
  429.  
  430.  
  431.  
  432.  
  433.  
  434.  
  435.  
  436.  
  437.  
  438.  
  439.  
  440.  
  441.  
  442.  
  443.  
  444.  
  445.  
  446.  
  447.  
  448.  
  449.  
  450.  
  451.  
  452.  
  453.  
  454.  
  455.  
  456.  
  457.  
  458.  
  459.  
  460.  
  461.  
  462.  
  463.  
  464.  
  465.  
  466.  
  467.  
  468.  
  469.  
  470.  
  471.  
  472.  
  473.  
  474.  
  475.  
  476.  
  477.  
  478.  
  479.  
  480.  
  481.  
  482.  
  483.  
  484.  
  485.  
  486.  
  487.  
  488.  
  489.  
  490.  
  491.  
  492.  
  493.  
  494.  
  495.  
  496.  
  497.  
  498.  
  499.  
  500.  
  501.  
  502.  
  503.  
  504.  
  505.  
  506.  
  507.  
  508.  
  509.  
  510.  
  511.  
  512.  
  513.  
  514.  
  515.  
  516.  
  517.  
  518.  
  519.  
  520.  
  521.  
  522.  
  523.  
  524.  
  525.  
  526.  
  527.  
  528.  
  529.  
  530.  
  531.  
  532.  
  533.  
  534.  
  535.  
  536.  
  537.  
  538.  
  539.  
  540.  
  541.  
  542.  
  543.  
  544.  
  545.  
  546.  
  547.  
  548.  
  549.  
  550.  
  551.  
  552.  
  553.  
  554.  
  555.  
  556.  
  557.  
  558.  
  559.  
  560.  
  561.  
  562.  
  563.  
  564.  
  565.  
  566.  
  567.  
  568.  
  569.  
  570.  
  571.  
  572.  
  573.  
  574.  
  575.  
  576.  
  577.  
  578.  
  579.  
  580.  
  581.  
  582.  
  583.  
  584.  
  585.  
  586.  
  587.  
  588.  
  589.  
  590.  
  591.  
  592.  
  593.  
  594.  
  595.  
  596.  
  597.  
  598.  
  599.  
  600.  
  601.  
  602.  
  603.  
  604.  
  605.  
  606.  
  607.  
  608.  
  609.  
  610.  
  611.  
  612.  
  613. We Have Mail
  614. Dear Mr. Ward:
  615. I am not much of a letter writer, but after reading the July 89 issue of the C
  616. Users Journal I felt I could save some of your readers a lot of time tracking
  617. down a problem with the Microsoft C, version 5.10 memory allocation routines.
  618. Enclosed is a listing and the output from the program.
  619. This may help Steven Isaacson who is having memory allocation problems using
  620. Vitamin C. I found this problem after a week of tracking down a memory leak
  621. problem in a very large application. My final solution was to write my own
  622. malloc()/free() rountines that call DOS directly. This will let the DOS
  623. allocator do what it is supposed to do. No time penalty was noticed in our
  624. application.
  625. Note if you do write your own malloc()/free() routines, call them something
  626. else! MSC uses these routines internally and makes assumptions about what data
  627. is located outside the allocated area. I always use a malloc()/free() shell to
  628. test for things like memory leaks and the free of a non-allocated block. It
  629. also will give you an easy way to install a global 'out of memory' error
  630. handler.
  631. The code supplied by Leonard Zerman on finding the amount of free space in a
  632. system is simplistic and very limited. A better routine would build a linked
  633. list of elements and then the variable vptrarray could be made a single
  634. pointer to the head of the list. The entire routine becomes dynamic, much more
  635. robust, and there is no danger of overflowing a statically allocated array.
  636. See the supplied code for an example.
  637. The linked list implementation has the side effect that it will work on a
  638. virtual memory system. Why you would want to do this is beyond me, but it
  639. could be considered a very time consuming way to find out what swapmax is set
  640. to on a UNIX system.
  641. If you have any questions, please contact me. My phone number is (408)
  642. 988-3818. My fax number is (408) 748-1424.
  643. Sincerely yours,
  644. Jim Schimandle
  645. Primary Syncretics
  646. 473 Sapena Court, Unit #6
  647. Santa Clara, CA 95054
  648. Thanks for the information. We've included your code in Listing 1. -- rlw
  649. Dear Mr. Ward:
  650. I'm new to programming and need to extract information from old mainframe
  651. files. Each file has its own annoying attributes.
  652. Some files are reports for printing on 132 column paper with headers on each
  653. page along with errors in tabulation and decimal point alignment.
  654. I'd like to know enough about grep, awk, sed, and tr so I'm not reinventing
  655. the wheel with my C programs for file manipulation.
  656. Where can I find an understandable and brief overview of these UNIX tools? (I
  657. know nothing about regular expressions, scanning, and syntactic analysis.)
  658. Sincerely,
  659. Orion C. Whitaker, M.D.
  660. 400 Brookline Ave., #22F
  661. Boston, MA 02215
  662. I suggest The UNIX Programming Environment by Kernighan and Pike. This is a
  663. tidy little book that does more to explain how the tools work and work
  664. together than any other book I've seen. While it's insightful, it's also a
  665. good teaching text.
  666. You should also consider The Awk Programming Language by Aho, Kernighan and
  667. Weinberger (the A. W. K. in awk).
  668. If our readers know of other texts that do a good job of explaining how to use
  669. the UNIX language-oriented tools, I'd like to hear from you. -- rlw
  670. Thank you for your letter/brochure. First, I have some questions. I studied
  671. BASIC last Semester at Comm. College, and would now like to learn C. My major
  672. problem is MY computer. I have a Commodore 64 with 256K RAM expansion, and
  673. plan to use Abacus Software's Super C Compiler 64. I am a retiree with little
  674. prospect of buying a new computer.
  675. 1. Do you offer much in this format, or am I butting my head against a wall?
  676. 2. Would it be practical for me to attend a class where they are using,
  677. probably, IBM compatibles, and do my homework on my system? Would work
  678. developed on my system operate on "IBM"s? The disks are not compatible, but
  679. could my work be 'retyped' into the "IBM"?
  680. I have Standard C by Plauger & Brodie, and Transactor Magazine has articles
  681. which look like they will be useful when I learn more.
  682. Les Maynard
  683. P.O. Box 915
  684. Palmer, AK 99645
  685. Unfortunately, we can't write Commodore disks. However, it's my understanding
  686. that if you have the right Commodore drive you can get a program that will let
  687. you read MS-DOS disks directly.
  688. Whether you can do your C homework on your Commodore depends on several
  689. things:
  690. 1) Is your instructor willing to accept Commodore output. If you have to run
  691. your work on an MS-DOS host to make it acceptable, it probably won't work.
  692. 2) What subjects and exercises will the class focus upon? If writing direct to
  693. the IBM video display is one of the exercises, it probably isn't reasonable
  694. for you to try to work along on the Commodore. If, on the other hand, the
  695. class will confine itself to general, portable language features and concepts,
  696. you will have less trouble.
  697. 3) How adept are you at researching your own system? At some point (probably
  698. several points), a classroom illustration isn't going to work on your machine.
  699. It really isn't fair to expect the instructor to research the problem for you.
  700. Can you find your own way?
  701. 4) Is your Commodore implementation complete enough to support the scope of
  702. the class? Will you be asked to write programs that exceed the memory space?
  703. Will you need doubles? Will the exercises require elaborate pre-processor
  704. capabilities?
  705. At the very least you should have a serious talk with the instructor before
  706. you enroll.
  707. Whether work you develop will run on an IBM depends entirely upon the code. If
  708. you confine yourself to generic file processing and discipline yourself to
  709. avoid or at least properly hide any Commodore peculiarities, then your code
  710. should run in the IBM environment. (You might find some helpful ideas in
  711. Rainer Gerhard's story in this issue.) Please note these are major ifs even
  712. for very experienced C programmers. -- rlw
  713. Dear CUG,
  714. I am writing to warn you and other users of the problems I have found with LEX
  715. part 1 and 2 on disk number 172 and 173. The program generates code which
  716. crashes the system when run. The problem is in llstin(). If _tabp is NULL, it
  717. assigns it to the return of lexswitch(). lexswitch() returns a pointer to the
  718. previous table, which is NULL when first cared. The results is _tabp being set
  719. to NULL forever. Since this table contains pointers to functions, the program
  720. jumps off to an unknown address. The source code that was provided will NOT
  721. generate this code, indicating that the exe file was not built from this
  722. source! So, I rebuilt it and, in testing, found the new exe produced different
  723. tables than the release program did.
  724. There are various solutions to this problem. One is by setting _tabp to the
  725. location of the table in the .lxi. The solution is to edit the generated
  726. source file each time and removing the assignment statement to _tabp in
  727. llstin(). Or you could alternately change lexswitch() to return the new value.
  728. I don't like the last one because all the documentation states the return
  729. value is a pointer to the previous table. Since I am using the -s option, I
  730. edit the file as there is another problem with that option.
  731. The problem with the -s option may only exist with Microsoft C. llstin() is
  732. declared as a void at the beginning. The function itself is NOT. The compiler
  733. produces a diagnostic error. With the incorrect source, the only way around
  734. this is to edit the file. (A REAL PAIN if you are using a make file to build
  735. the final program.)
  736. I also have a copy of "Bison". It has worked very well with one exception. I
  737. found I had to include stdlib.h in simple.prs in order to get rid of several
  738. warning messages under certain conditions. One might include it inside the .y
  739. file, instead. By placing it inside simple.prs I don't have to remember to put
  740. it inside the .y. In general, I've found bison to be GREAT.
  741. Keep up the good work, and good luck.
  742. Sincerely,
  743. Frank Veenstra
  744. 24797 Metric Dr.
  745. Moreno Valley, CA 92387
  746. Yes, the .exe and source files are out of phase. We'll test your fix and
  747. remaster the volume with the fix. When we have a new master we'll announce an
  748. upgrade in the New Releases column. Thanks for the help. -- rlw
  749. Mr. Robert Ward:
  750. In the May, 1989 issue of the C Users Journal, Timothy Prince presented a
  751. rather eloquent and detailed article entitled "Efficient Matrix Coding in C".
  752. However, I would like to bring to your attention, excuse me if someone already
  753. has, an error in that article.
  754. Mr. Prince asserts the following to be true:
  755. a[i][j] = *( &a[0][0] + i * I + j )
  756. when given the declaration:
  757.  
  758. float a[I][J] ;
  759. C stores array elements in a row-major order and not in column-major order as
  760. suggested above. The valid condition is as follows:
  761. a[i][j] = *( &a[0][0] + i * J + j)
  762. for the given declaration. All the elements of row a[0][.] are located at a
  763. lower address than the first element of row a[1][.], which is stored right
  764. after the array element a[0][J-1]. Consequently, to access a[i][j], it is
  765. necessary to skip i rows, where each row contains J elements plus the j
  766. elements before the desired element.
  767. I would also like to take this opportunity to commend you and your staff on
  768. producing a Journal that is superior technically than all the other
  769. superficial computer magazines that I have read. That May issue was my first
  770. copy of C Users Journal and it certainly will not be my last.
  771. Sincerely yours,
  772. Girish T. Hagan
  773. 27401 Via Olmo
  774. Mission Viejo, CA 92691
  775. Ah yes, the hazard of too much FORTRAN and Pascal. Thanks for correcting our
  776. slip -- and thanks for the kind words. -- rlw
  777. Dear Robert,
  778. I have been a member of the C Users' Group for quite a long time now, around
  779. the seven to eight years mark. Over this period I have kept all of your
  780. newsletters and your present The C Users Journal publications. I have watched
  781. the evolution of the Journal with great interest.
  782. During your 'early days' I often reread some of the newsletters when I needed
  783. some information on a particular piece of code, or on a bug which another
  784. member had discovered. But time seems to compress as you get older. These days
  785. I rarely have the time to re-read articles, unless it is important that I do
  786. so.
  787. WHY is he telling me this...do I hear you ask? Well, I hope I have set the
  788. scene properly because I assume you have many more readers than just Phil
  789. Cogar who have difficulty in finding enough time to squeeze in their preferred
  790. reading. Professionals in any line of work tend to be busy people.
  791. Which brings me to the August issue of the Journal and, specifically, the
  792. article by Denis Schrader on the FOR_C Translator. Not that I am at all
  793. interested in FORTRAN_to_C translators but I always read the Journal from
  794. cover to cover and I hope my comments will assist in raising the standard of
  795. the Journal even further.
  796. With respect to Denis Schrader, who I hope does not take offence that I have
  797. selected his article to point out what I believe is wrong with some of the
  798. User Reports, I would like to direct your attention to this article with the
  799. plea that you consider setting certain standards for authors to write to for
  800. future User Reports.
  801. So, and without wishing to offend Denis, let me start by asking you to
  802. instruct your authors to make their reviews complete (or as complete as they
  803. can in the circumstance) as they stand. Don't presume the reader either has
  804. access to, or the inclination to look up, an earlier review. Of course rules
  805. are meant to be broken so you might give a reference to something written
  806. within the previous several months, but I suggest two years is a bit too long.
  807. I refer here specifically to the words-"...which I reviewed in the August 1987
  808. issue...However, comments in this review will point out improvements which
  809. have occurred since the release of earler verisons of the product."
  810. Point 2, back up specific comments with specific information. For example if
  811. you say--
  812. "The translator will pay for itself quickly in saved programmer hours."
  813. then you should also say how much it costs, both the List Price and, if you
  814. know it, the street price.
  815. Point 3, if we are talking about a specific product then either cut out or cut
  816. down on the generalisations. An example of this is the comment (statement?)--
  817. "The translator translates almost 100 percent of ANSI Standard FORTRAN as
  818. well...extensions."
  819. If the reader is reading the User Report because he or she wants to be better
  820. informed about the product (and isn't that the purpose of the User Report?)
  821. then, in this case, unless we are told-
  822. -Whether this (the non-translation of the FORTRAN code) is a transient thing.
  823. In other words do you have to check each piece of translated code for small
  824. errors (perhaps for large errors...I don't know and the Review doesn't say)
  825. which might translate to bugs in C; or
  826. -Whether this is systematic and the FOR_C translator only fails to properly
  827. translate certain pieces of FORTRAN code properly into C. In such a case does
  828. the translator 'flag' the offending pieces of code so they can be corrected
  829. using the recommended, known conversion; or
  830. -If your translated C code compiles without the compiler complaining to you,
  831. does this mean the code is a 1-for-1 translation of the FORTRAN routine, or
  832. not;
  833. and so on.
  834. It seems to me that a generalised comment of the type mentioned above does
  835. little (nothing?) to better inform the reader about the merits or otherwise of
  836. the product.
  837. Point 4, comparisons are odious (or so we are told) but they seem to abound in
  838. product reviews. My point is that partial comparisons tend more to mislead the
  839. reader than to inform him/her.
  840. In other words we are talking here about a product which translates FORTRAN
  841. code into C code. We are not told WHY it is desirable to do this if you
  842. already have good, de-bugged FORTRAN routines you wish to incorporate into C
  843. programmes. Please correct me if I have got it wrong but, as I understand the
  844. situation the Microsoft family of microcomputer languages allow you to
  845. generate files compiled in Basic, C Pascal, FORTRAN and assembler any and all
  846. of which can be linked into a run-time file as required.
  847. I am (most certainly) NOT an apologist for Microsoft but I do suggest a
  848. reviewer has not properly informed the reader as to the merits or otherwise of
  849. the product without at least canvassing other alternatives. If Microsoft, for
  850. example, have a family of languages which can do the job in another way
  851. (you'll notice I didn't say 'a better way' because I don't have a clue which
  852. is the better approach, the Review didn't tell me) then the Reviewer should at
  853. least mention this.
  854. In other words alert the reader to other possible alternatives, at least. The
  855. preferred option would be to make a comparison between the competing products
  856. and compare features, strengths and weaknesses.
  857. So there it is. In summary my four points are--
  858. Point 1- Make the review as complete as possible in the spacce allowed. Don't
  859. ask the reader to look up other references. We aren't dealing with a
  860. scientific paper, just a product review.
  861. Point 2- Give specific (factual) backup to specific comments. It's not that we
  862. don't trust reviewers to be objective, but we are discussing opinions here,
  863. and my opinion may well differ from the Reviewer's if I am given the
  864. opportunity to see what his\her opinion was based on.
  865. Point 3- Leave out generalisations, at least if we are discussing one,
  866. specific product. Generalisations are OK if we are discussing a 'family' of
  867. products. Who was it said--
  868. 'All generalisations are false.'
  869. Or perhaps I got that wrong?
  870. Point 4- If you believe comparisons (with products from other sources) make
  871. the review stronger, then by all means put in the comparisons...but at least
  872. try to cover the best alternatives to the product being reviewed. Anything
  873. less then you are misleading your readers.
  874. I know it has been tedious, but that's all I wish to say on the subject for
  875. the moment. Perhaps you will find something here to put before future Product
  876. Reviewers, when they submit their articles. My hope is that I have sparked a
  877. debate which will lead to an even higher standard for what is already a fine
  878. publication.
  879. Yours sincerely,
  880. Phil E. Cogar
  881. P.O. Box 364, Narrabeen,
  882. N.S.W. Australia 2101
  883. I find myself in complete agreement with your four points. I'm sorry the FOR_C
  884. article didn't measure up.
  885. Generally I'd just as soon do without "reviews". That's why we've used the
  886. label "User Reports". I don't really care if someone gave the product four
  887. stars -- I want to know what it's like to use the product. Will it require
  888. some changes in my work habits? Does it seem to fit a certain design style
  889. better than others? Are certain unobvious tricks necessary to certain goals.
  890. If someone has spent enough time with a product to be qualified to evaluate it
  891. for other experienced programmers, then that person has also learned several
  892. things that aren't in the manuals. Why should I have to relearn those items if
  893. I decide to buy the product? The writer should give me the full vicarious
  894. benefit of his experience.
  895. Here are some of my guidelines for anyone interested in writing a product
  896. related story.
  897. Don't try to sell the product or your philosophy of how products should be
  898. designed, tested, marketed, packaged ... whatever. Instead, tell us what it
  899. does and doesn't do.
  900. Keep the opinions to a minimum. If you give intelligent, experienced readers
  901. access to the facts that produced your opinion, they'll reach a similar (or at
  902. least reasonable) opinion on their own.
  903. Don't be cute. I don't care how entertaining you think your struggle to remove
  904. the shrink wrap was, I don't want to waste time reading about it.
  905. Don't guess. If you aren't certain about a particular issue, either find out
  906. or don't mention it.
  907. Don't just list features. That's the role of vendor literature.
  908. Do share all you learned in working with the product. If you include
  909. information inappropriate to my audience, I can edit it out. I can't edit in
  910. information.
  911. I'm acutely aware that we very seldom get product-releated copy that fully
  912. measures up to these guidelines. We're always working on getting better copy.
  913. -- rlw
  914. To The C Users Group:
  915. I am disheartened at the lack of truly advanced pioneering books in C
  916. programming. Particularly those of a scientific nature. Numerical Recipes in C
  917. and Numerical Software Tools in C are the only two that I have heard of, which
  918. are primarily argorithm'ic' books without instruction. Everyone seems to be
  919. publishing the same link lists, the same databases, and the same TODO lists.
  920. Just as in assembly language books one gets the same Ram disks, disk caches
  921. and clocks. That is not just book publishers either. Journals and magazines
  922. are doing the same thing. I cannot believe that the programming community
  923. lacks such expertise. When will publishers realize that enough is enough, and
  924. start producing books and articles of a truly advanced nature, like the one
  925. you had The Fast Walsh Transform. It is also time for a complete numerical
  926. methods book written for C programming in a common compiler (MSC TC) with full
  927. descriptions as one would receive in a course in numerical methods at a
  928. University.
  929. Sincerely,
  930. Jerry Rice
  931.  
  932. 504 Eastland St.
  933. El Paso, TX 79907
  934. Maybe some qualified author (with a willing publisher) will hear your plea.
  935. Why do publishers publish the same material over and over? Perhaps because it
  936. sells. One of our earlier issues (with several stories covering the
  937. fundamentals of device drivers) remains one of our most popular back issues.
  938. Perhaps device drivers are old-hat to you, but to many they remain a mystery.
  939. Most of our readers are expert programmers, they just aren't all expert in the
  940. same areas.--rlw
  941.  
  942.  
  943.  
  944.  
  945.  
  946.  
  947.  
  948.  
  949.  
  950.  
  951.  
  952.  
  953.  
  954.  
  955.  
  956.  
  957.  
  958.  
  959.  
  960.  
  961.  
  962.  
  963.  
  964.  
  965.  
  966.  
  967.  
  968.  
  969.  
  970.  
  971.  
  972.  
  973.  
  974.  
  975.  
  976.  
  977.  
  978.  
  979.  
  980.  
  981.  
  982.  
  983.  
  984.  
  985.  
  986.  
  987.  
  988.  
  989.  
  990.  
  991.  
  992.  
  993.  
  994.  
  995.  
  996.  
  997.  
  998.  
  999.  
  1000. Using Header Files To Enhance Portability
  1001.  
  1002.  
  1003. Rainer Gerhards
  1004.  
  1005.  
  1006. Rainer Gerhards specializes in systems programming and has a strong interest
  1007. in C. He has written some large-scale control systems and many small utilities
  1008. in C. He owns his own small software company in addition to managing the
  1009. computing center of a mid-sized company. He may be contacted at
  1010. Petronellastrasse 6, 5112 Baesweiler, West Germany.
  1011.  
  1012.  
  1013. C is known for its efficient code, rich set of features and portability. While
  1014. portability is not built in, you can avoid possible portability problems by
  1015. anticipating them. Let's look at a few problem areas, suggest some solutions,
  1016. and examine one method in detail.
  1017. One important portability issue is the C dialect that your compiler
  1018. implements. Although there have always been C language standards, until
  1019. recently they have been too imprecise to preclude varying interpretations.
  1020. Early, less powerful machines also forced compiler writers to limit features,
  1021. contributing additional variant dialects. Thus, some compilers can't
  1022. understand valid C-coding if it contains unsupported features.
  1023. Bit fields are a good example. A number of modern compilers still don't
  1024. support bit fields. Of course, you could avoid using bit fields, but what if
  1025. you write for one compiler which doesn't support structure and union
  1026. assignment and for several others which do? You might avoid these constructs
  1027. too, but would you prefer to learn while porting a 50,000 line program which
  1028. makes extensive use of structure assignment, that the environment to which
  1029. you're porting doesn't support structure assignment? The challenge is to know
  1030. which features to avoid.
  1031. Now nearly all commercially-used compilers support C in its entirety. But
  1032. these compilers offer extra features, especially in the preprocessor area.
  1033. Though you may simply avoid these features, you may not know which features
  1034. are non-standard, especially if you are new to C or if you work in just one
  1035. environment. Some compiler vendors don't flag such features.
  1036. Even an experienced C programmer determined to avoid the problems outlined
  1037. above by using only standardized constructs still faces the difficulty of
  1038. deciding which "standard" to use: the original Kernighan and Ritchie (K&R)
  1039. standard defined in The C Programming Language, or the forthcoming ANSI
  1040. standard.
  1041. The ANSI standard resolves many portability problems not addressed by K&R and
  1042. provides a good base for the future. The ANSI standard is mostly upwardly
  1043. compatible with K&R; most K&R programs can be moved to ANSI compilers without
  1044. any problems. But in order to move code in the opposite direction successfully
  1045. (from ANSI to K&R), compilers require special preprocessor tricks I'll
  1046. describe later.
  1047. The standard library poses similar problems. Compiler writers have restricted
  1048. and extended the library rather than the language. Some compilers don't even
  1049. have a standard library; many libraries include numerous extensions. MS-DOS
  1050. compilers in particular tend to offer extensions covering graphics,
  1051. interrupts, and operating system interfaces. Porting code which uses one
  1052. compiler's extensions to a different compiler can be very difficult.
  1053. Operating system differences, because they are the hardest to hide, are among
  1054. the hardest subjects to address. Moreover, operating systems differ greatly --
  1055. some do multi-tasking, some are multi-user, and some are single tasking
  1056. systems. The file-naming conventions are anything but standardized. These
  1057. problems are minor compared to the variations in file organization. For
  1058. example, while most operating systems consider text files to have variable
  1059. length records (if any), some use fixed-length records (if any). Records may
  1060. be delimited by \n, \n\r or record-length fields. Some OSs use special
  1061. blocking mechanisms, others don't.
  1062. Fortunately most standard libraries can hide these differences, but only by
  1063. distinguishing between text and binary mode, introducing subtle, non-standard
  1064. features.
  1065. In addition to processing files the operating system should have some kind of
  1066. interaction with the user, which leads to additional problems if you use
  1067. special system features like asynchronous communication or sophisticated
  1068. display manipulation.
  1069. Hardware differences can cause programs that compile and link without error
  1070. and run well in one environment, to crash in another. Often these problems are
  1071. caused by different word lengths. It's hard for a UNIX programmer working with
  1072. the portable C compiler (PCC) on 68xxx to learn that the same PCC on
  1073. 80x86-based machines uses 16 instead of 32 bits for integers. A 68xxx program
  1074. that uses integers to index some two million database records on a 68xxx
  1075. machine may require a major rewrite before it can access more than 32,767
  1076. records on the 80x86 machine.
  1077. Hardware differences can also affect the portability of pointer casts. Many
  1078. programmers assume that pointers can simply be cast from one type to another
  1079. -- a reasonable assumption on most byte machines. However, word machines'
  1080. (like the Unisys 1100) pointers to word-aligned items differ significantly
  1081. from pointers to non-aligned items. This is true for some so-called byte
  1082. machines too. Still other problems arise when you port code from machines with
  1083. a segmented address space to one with a linear address space.
  1084. The last problem is machine resources. Many programmers assume that if their
  1085. code is portable and standardized, their program will run on all machines
  1086. supporting a standard C- compiler. While this is basically true, some programs
  1087. require so much memory or processing time that they simply can't be run on
  1088. some smaller machines.
  1089.  
  1090.  
  1091. Designing For Portability
  1092.  
  1093.  
  1094. In spite of these problems, it is possible to write C programs that can be
  1095. compiled and executed in different environments. To be portable, a program
  1096. must be designed and coded in a fashion that hides environmental differences.
  1097. C's own design hides many environmental differences. The standard library is a
  1098. successful attempt to hide some very environment-specific information -- such
  1099. as the way in which file system (and some others) calls are done on the target
  1100. operating system. Without the standard library, every programmer would have to
  1101. write the interface coding himself. Even worse, he would have to rewrite it
  1102. again and again for each new environment.
  1103. You can hide other large environment differences by creating your own
  1104. "standard libraries" for other tasks: extract the non-portable operations to a
  1105. separate source module, define a general interface for this model and build a
  1106. different implementation for every environment you want to work with. Many of
  1107. the high quality portable support library products available do this for you.
  1108. Such a library provides "instant" portability, lower cost, and more
  1109. functionality than an equivalent product written by a single programmer.
  1110. While system-specific libraries are appropriate for horrible, non-portable
  1111. tasks like dealing with the user console, using a standardized function call
  1112. for smaller tasks which require only slightly different coding in limited
  1113. areas of the source code might not make sense. In this case it would not make
  1114. sense to define a one-line function to set a signal handler under one
  1115. environment only, especially if the signal-handler is called from inside a
  1116. tight loop where the calling overhead could cause performance problems.
  1117. The C preprocessor is the obvious tool for these smaller coding differences:
  1118. just use conditional compilation to enable the code which sets the signal
  1119. handler in the one environment where it's needed. You don't have to define a
  1120. large number of functions, and there is no unnecessary calling overhead.
  1121. The preprocessor can also help solve problems that arise simply because
  1122. different names are used for the same thing. For example, nearly every
  1123. compiler uses its own name for the machine-level i/o (port) functions of
  1124. MS-DOS compilers (for example inp and outp versus inportb and outportb).
  1125. Fortunately these functions have the same calling conventions. In this
  1126. situation, rather than use conditional compilation for every function call
  1127. parameterized, just use conditional compilation one time to define a macro
  1128. that in turn calls the function with the right name. Everywhere else, the code
  1129. uses the macro to call the function.
  1130. Macro and constant definitions can also completely hide slight differences in
  1131. standard library paramenters. For example, when working under two different
  1132. operating systems where the standard libraries have different open modes for
  1133. text and binary files, you could use the call to open a binary file for
  1134. writing
  1135. fp = fopen ("file", OPM_WM)
  1136. Under UNIX, OPM_WB would be defined "w" and the call would expand to
  1137. fp = fopen("file", "w")
  1138. Under MS-DOS (Microsoft C) OPM_WB would be defined "wb" and would expand to
  1139. fp = fopen("file", "wb")
  1140. Sometimes a simple define can also hide significant hardware differences.
  1141. Different data type sizes can be hidden by defining your own data types with a
  1142. guaranteed minimum and maximum precision. For example, type int32 (integer
  1143. containing at least 32 bits) would be mapped to int for 68xxx machines and to
  1144. long for 80x86 machines. If int32 has been used in every spot requiring a
  1145. 32-bit integer, nothing but the definition needs to be changed to adjust for
  1146. the alternate name. (Please note that a data type redefinition can be done
  1147. either with the preprocessor or a compiler typedef. While the former is
  1148. potentially more portable, so far I have not seen a compiler which does not
  1149. implement typedef. Thus I prefer using typedef because sophisticated compilers
  1150. can do better error checking with it. However, if you want to be absolutely
  1151. sure that your data type redefinition will be accepted by all old compilers,
  1152. you must use preprocessor defines.)
  1153. By now it is obvious that the preprocessor can help make programs more
  1154. portable. What would make more sense than to combine all these
  1155. preprocessor-based aids? This can be done in a single header file. For nearly
  1156. two years I have been using such a file, working mainly with four different
  1157. MS-DOS compilers and the UNIX PCC. The idea developed because of minor
  1158. standard-library differences between MS-DOS compilers, but it soon became
  1159. clear that the header file could help when porting to UNIX, too. The still
  1160. incomplete result will be described below.
  1161.  
  1162.  
  1163. environ.h
  1164.  
  1165.  
  1166. All necessary preprocessor statements and typedefs are included in one single
  1167. file named environ.h (Listing 1). It should be the very first file included.
  1168. Before including environ.h, you should define which other standard include
  1169. files you need. This is done by defining some preprocessor constants which
  1170. correspond to standard include file functionality. You read right,
  1171. functionality -- not names. For example, if you select the define INCL_ASSERT,
  1172. not only will the file assert.h be included but the necessary (for MS-DOS/MSC)
  1173. file process.h also. If you compile under UNIX, only assert.h is included.
  1174. Defining these constants in terms of functionality hides the include file name
  1175. differences -- an important feature that saves you many conditional directives
  1176. in the source modules. Microsoft uses a similar system for their OS/2 header
  1177. files in MSC 5.1.
  1178. When completely defined for your environment, environ.h should #include all
  1179. include files needed by your application. If you find it necessary to
  1180. explicitly include other files, you should extend the definitions in
  1181. environ.h. They are still incomplete (see lines 274 - 401).
  1182. environ.h begins by preventing the accidental inclusion of a header file more
  1183. than once. Multiple inclusion may cause damage to some preprocessor defines.
  1184. At best, it will cause additional overhead, and at worst, program errors may
  1185. occur. To prevent these problems environ.h checks preprocessor constant
  1186. ENVIRON_H. If this constant is defined, environ.h assumes that it has been
  1187. previously included and takes no further steps (via the #ifndef ENVIRON_H in
  1188. line 26). If ENVIRON_H is not defined, then this is the first inclusion of
  1189. environ.h and processing takes place. First ENVIRON_H is defined, ensuring
  1190. that no second inclusion will be possible.
  1191. Next, based on which compiler and operating system are active, ENVIRON_H
  1192. defines the target environment. Information about the environment is acquired
  1193. in a relatively straightforward way (lines 29 - 165). Operating-system
  1194. specific constants that may be defined automatically by the compiler are
  1195. purged -- they will be replaced with your own. The #undef of the default
  1196. definitions is not actually necessary, but it will prevent possible warning
  1197. messages from appearing when redefining the compiler default constants.
  1198. The #undefs are followed by defines which select the target OS. Only one may
  1199. be active at one time. Note the definition to 0 or 1. You could also define
  1200. only one OS constant and use #ifdef instead of
  1201. #if CONSTANT == 1
  1202. but this has the disadvantage that K&R compilers have no "#if
  1203. defined(CONSTANT)". Without this command it is hard to build complex
  1204. preprocessor-ifs using #ifdef and #ifndef because you can't use Boolean
  1205. operators. If you define the constants to 0 and 1, you can build normal
  1206. conditional expressions. This is an advantage if you consider that you must
  1207. often ask questions like
  1208. #if MSDOS && USE_BIOS
  1209. Following the OS definition there are some auxiliary definitions used only
  1210. under specific OS to identify the target machine. Currently these apply only
  1211. to certain generic MS-DOS machines within compatible hardware or BIOS
  1212. requiring actual MS-DOS calls (as opposed to BIOS calls or direct hardware
  1213. manipulation). The only common example is the early Wang PCs, for which there
  1214. is a separate definition.
  1215. The operating system definitions are followed by the compiler definitions. A
  1216. specific compiler selection is only necessary if more than one is available
  1217. under one OS. In my case this is only needed for MS-DOS. But as you can see in
  1218. environ.h there is only a definition for MSC. All other compilers I use
  1219. identify themselves by doing an automatic constant definition upon startup
  1220. (e.g., ___TURBOC___ for Borland's Turbo C). Note that the MSC constant is
  1221. overridden if one of the other predefined constants is detected or an OS other
  1222. than MS-DOS is active (lines 88 - 106). This feature simplifies proper
  1223. configuring of the header-file.
  1224. Separate constants for each compiler to allow conditional compilation for
  1225. small compiler differences. To avoid code like "#if MSC DLC LC ___TURBOC___
  1226. .... "we introduce some language set selection constants (lines 70 - 76). Each
  1227. define corresponds to one language feature. If the constant is equated to true
  1228. (1) that language feature can be used, otherwise it cannot. All other
  1229. decisions are based on these feature selection constants and are much more
  1230. readable. Now the example given above takes the more intelligible form
  1231.  
  1232. #if USE_VOID.
  1233. To avoid modifying all language selection constants each time you change
  1234. compilers, environ.h includes an automatic language set selection which
  1235. automatically redefines the language set constants based on the compilers' and
  1236. OS definitions. While auto selection is currently only functional in the
  1237. MS-DOS environment, it can easily be expanded to work under different
  1238. operating systems (lines 129 - 164).
  1239. To complete the environment definition, environ.h defines the constant ANSI_C
  1240. to 0 or 1 in respect to the compilers' C standard (K&R/ANSI) (lines 119 -
  1241. 127). This constant is currently set based on the state of a language feature
  1242. selection (like USE_VOID), but could become more important in the future.
  1243. The example header file still lacks one feature, a definition check. All
  1244. definitions are accepted as entered. If, for example, the programmer defines
  1245. two or more operating systems to 1 the behavior of environ.h is undefined but
  1246. clearly erroneous. This could be avoided by checking the entered definitions
  1247. to see if two or more definitions are true and aborting compilation if so:
  1248. #if MSDOS && UNIX
  1249. "Error: Both MSDOS and UNIX
  1250. selected"
  1251. #endif
  1252. This code ask for the error condition and generates a compile-time error if it
  1253. detects one. The error message generated by the compiler points at the real
  1254. error message in the source module. Examples can be found in CUG library
  1255. volume 227 (compatible graphics) in file graphics.h. This file contains
  1256. extensive definition checking.
  1257. So far environ.h has supplied definitions that allow conditional compilation
  1258. in the source units but no automatic porting aids. The balance of the file
  1259. addresses this second need. Different compiler data types and modifiers can be
  1260. hidden largely by preprocessor defines. For example, if the compiler doesn't
  1261. support the void keyword, just define void to nothing, and the void keyword
  1262. will disappear. Since you didn't use void originally when writing for that
  1263. compiler, this disappearance will cause no problems. Your coding can now be
  1264. used with compilers that support void without any additional work.
  1265. That is the key feature of modifier definition: you can hide all data type and
  1266. modifier differences by simply defining the data type in question to nothing
  1267. (as in lines 167 - 195 in environ.h).
  1268. Here's another example: if a compiler doesn't support the volatile modifier,
  1269. it normally doesn't do the strange optimizations that force you to use
  1270. volatile (or they can be turned off), so there is no problem in purging all
  1271. volatile modifiers in your source.
  1272. This kind of type redefinition allows you to use the types on machines
  1273. supporting them without losing backward compatibility. If an older compiler
  1274. doesn't support these type modifiers, their extra value is gone but your
  1275. program still runs without problems.
  1276. Most data types and modifiers can be treated in this manner. (In some cases
  1277. you may instead redefine the type to something different -- e.g. define void
  1278. to int instead of purging it). However, some types and modifiers, like enum,
  1279. can't simply be redefined to nothing or to some other value. If you try to
  1280. redefine these types, your program won't compile due to the syntax differences
  1281. between defining a "normal" data item and an enum one. Defining an enum is a
  1282. process nearly identical to defining a structure or union. Special definitions
  1283. are required. You can't hide them by one general define.
  1284. You still can use enum on supporting and non-supporting compilers, but you
  1285. must define all your enum types using conditional compilation. If the compiler
  1286. supports enum, you can use it without difficulty. If not, you define an int
  1287. type and use the preprocessor to define the enum tags:
  1288. "#if USE_ENUM
  1289. typedef enum { A, B } enumtype;
  1290. #else typedef int enumtype;
  1291. #define A 0
  1292. #define B 1
  1293. #endif"
  1294. This clearly entails more programming work but allows the use of extended
  1295. error checking features of compilers that support enum.
  1296. You can define your own data types to hide hardware differences, especially
  1297. machine word length differences. They ("personal types") have a guaranteed
  1298. minimum and maximum precision and are mapped to the actual hardware data type.
  1299. By relying on these "personal types," you can write programs that work on
  1300. different machines in an expected manner, and you can take memory requirements
  1301. into account because there is a guaranteed MAXIMUM precision.
  1302. This problem wasn't critical to me, so the example header file contains only
  1303. very limited support (lines 258 - 261). Please note that typedefs are used
  1304. instead of preprocessor defines.
  1305. The next problem area is that of standard library function names and calling
  1306. conventions. For example, calling exit() in C will commonly terminate your
  1307. program gracefully. Under the Starsys OS, exit() is an OS call something like
  1308. abort(). The real exit() function has been called dx_exit(). This causes
  1309. problems to all but a few programs and would normally require text
  1310. modifications. But that's exactly what the preprocessor can do for you: if
  1311. you're running under Starsys, just define a macro named exit which takes one
  1312. parameter (the return value). It will expand to a call to dx_exit() with that
  1313. given parameter (line 234 - 236).
  1314. A similar technique hides the variations among library functions with
  1315. different names but identical calling parameters and functionality. Example
  1316. macro definitions can be found a few lines above the exit() macro.
  1317. File open modes are addressed in lines 241 - 253. Please note that not all
  1318. open modes are supported, but the definitions can be easily expanded.
  1319.  
  1320.  
  1321. Function Prototyping
  1322.  
  1323.  
  1324. Unfortunately, ANSI function prototyping is not supported in every
  1325. environment. Rather than sacrificing the extended error checking features that
  1326. prototyping offers by not using it at all, you can use prototyping when the
  1327. compiler supports it and turn it off when it does not.
  1328. Turning off function prototyping is a little harder than turning off an
  1329. unknown modifier. First you must build two classes of function prototypes,
  1330. external and internal, corresponding to external and static functions. The
  1331. external prototype macros appear in lines 197 - 211. This macro expands to
  1332. extern func() for a K&R compiler and to extern func(int) for ANSI compilers.
  1333. Please note the extra parentheses around int in the PROTT definition. These
  1334. parentheses become part of the macro argument and are re-expanded. After
  1335. expansion, they are the function parentheses of extern func(int). These
  1336. parentheses are especially important if you want to prototype a function with
  1337. more than one argument. If there were no inner braces, the macro would have
  1338. two arguments, which would force you to write one prototyping macro for every
  1339. number of function arguments you will ever use. Given these inner braces the
  1340. whole prototype is one macro argument and only one prototyping macro will
  1341. satisfy all needs.
  1342. Normally you write a function header only once for each internal function. It
  1343. is more difficult to hide these prototypes: modern ANSI's style is to write
  1344. argument types and names in the function header (e.g. static func(int a)),
  1345. while K&R's style is to write the argument names only (static func (a)).
  1346. Fortunately ANSI compilers accept function headers written in K&R style, but
  1347. usually don't build prototypes for such headers. One solution is to write the
  1348. prototype first and then to write the actual function header (STATICPT(func,
  1349. (int));\n static func()). In this case the function prototype defines the
  1350. function first as extern to prototype it (just as is done in application
  1351. header files). While this has worked well with all ANSI-compilers I know of,
  1352. I'm not certain that it is guaranteed to be legal under ANSI-standard.
  1353. At first glance you may wonder why the prototype does not have the form static
  1354. func PROTT((int)) and in fact I am not sure if these constructs are legal.
  1355. Most compilers accept the functions to be declared to extern and later
  1356. redefined to static. However, the MSC compiler doesn't accept this construct
  1357. and generates error messages (at least QC does; CL accepts them with
  1358. warnings). Instead, MSC allows both the function prototype and the actual
  1359. function header to be declared static -- the approach used in environ.h. If
  1360. MSC is active, the prototype attribute is redefined to static. To do this the
  1361. macro must have control over the whole prototype line, not just part of it. So
  1362. a new construct has been created. The macro has two parameters: the function
  1363. name and the prototype. It expands to the correct modifier followed by the
  1364. function name and (if selected) the function prototype.
  1365. This may be a somewhat unusual macro construct, but remember that the C
  1366. preprocessor is mainly a text substitution tool and not part of the actual
  1367. compilation process. This allows the preprocessor to make some very strange
  1368. modifications to the C source code, including constructs like the static
  1369. function prototyping which cannot be done by any C statement. Building such
  1370. unusual constructs can give very simple solutions to otherwise intractable
  1371. problems. The STATICPT() macro can be found between lines 197 and 211.
  1372.  
  1373.  
  1374. Conclusions
  1375.  
  1376.  
  1377. As you can see, the environmental header file environ.h can aid in writing
  1378. portable programs, especially in the problem areas of data type, modifier and
  1379. name differences. In addition, some machine specifics can be hidden and some
  1380. newer constructs mapped to work with older compilers.
  1381. On the other hand, the header file can't hide some differences (e.g. different
  1382. mechanisms for interacting with the user console). Such differences require
  1383. special coding that normally should be contained in external modules. But the
  1384. header file can help you write these modules too by precisely defining the
  1385. target environment. Precise functional definitions are the basis for selecting
  1386. the right code sequences in the low-level driver modules (assuming that coding
  1387. for more than one environment can be contained in one source unit). The
  1388. definitions will aid you in activating slightly different source lines which
  1389. you may have in your program.
  1390. Thus, a larger porting system is built using three modules. First, the
  1391. environment header file describes the environment and hides all differences
  1392. possible using the preprocessor and typedefs (mainly text substitutions).
  1393. Second, libraries of standardized functions handle larger problem areas that
  1394. actually require different coding. Third, conditional compilation within the
  1395. source modules hides very small differences where the text-substitution
  1396. capabilities of the preprocessor are insufficient and a special function call
  1397. makes no sense.
  1398. This last option should be limited to cases where it is absolutely necessary,
  1399. because conditional compilation is not really portable programming, but is
  1400. rather having code for all known environments. If you switch to a new
  1401. environment, you must not only write new coding but also look for a problem
  1402. area in the source file. To avoid these problems I recommend flagging these
  1403. lines with special comments (e.g./*PORT*/).
  1404. Related code can be found in the CUG library holdings. Volume CUG227 contains
  1405. a compatible graphics system which makes extensive use of the preprocessor's
  1406. text substitution capabilities. Volume CUG265, the cpio starter kit, contains
  1407. a header file similar to the one discussed here. It also contains programs
  1408. using it.
  1409.  
  1410. Listing 1
  1411. 1: /*
  1412. 2: *e n v i r o n. h
  1413. 3: * -----------------
  1414. 4: * This module contains environment specific information.
  1415. 5: * It's used to make the programs more portable.
  1416. 6: *
  1417. 7: * @(#)Copyrigth (C) by Rainer Gerhards. All rights reserved.
  1418. 8: *
  1419. 9: * Include-file selection defines are:
  1420.  
  1421. 10: *
  1422. 11: * Define Class
  1423. 12: * ---------------------------------------------------------
  1424. 13: * INCL_ASSERT assert macro and needed functions
  1425. 14: * INCL_CONIO low-level console i/o
  1426. 15: * INCL_CONVERT conversion and classification functions
  1427. 16: * INCL_CTYPE ctype.h
  1428. 17: * INCL_CURSES curses.h
  1429. 18: * INCL_LLIO low-level i/o
  1430. 19: * INCL_MEMORY memory acclocation/deallocation functions
  1431. 20: * INCL_MSDOS MS-DOS support
  1432. 21: * INCL_PROCESS process control
  1433. 22: * INCL_STDIO stdio.h
  1434. 23: * INCL_STDLIB standard library functions
  1435. 24: * INCL_STRING string handling functions
  1436. 25: */
  1437. 26: #ifndef ENVIRON_H
  1438. 27: #define ENVIRON_H
  1439. 28:
  1440. 29: #undef MSDOS
  1441. 30: #undef OS2
  1442. 31: #undef UNIX
  1443. 32: #undef STARSYS
  1444. 33:
  1445. 34: /*
  1446. 35: * configurable parameters.
  1447. 36: * modify the following parameters according to the target environment.
  1448. 37: */
  1449. 38:
  1450. 39: /*
  1451. 40: * define target operating system
  1452. 41: */
  1453. 42: #define MSDOS 0
  1454. 43: #define UNIX 0
  1455. 44: #define OS2 1
  1456. 45: #define STARSYS 0
  1457. 46:
  1458. 47: /*
  1459. 48: * define target machine
  1460. 49: *
  1461. 50: * This is auxiluary data only needed for some operating
  1462. 51: * systems. Currently only needed if MS-DOS is active.
  1463. 52: */
  1464. 53: #define IBM_PC 1 /* IBM PC, XT, AT & compatibels */
  1465. 54: #define WANG_PC 0 /* Wang PC, APC ... */
  1466. 55:
  1467. 56: /*
  1468. 57: * define target compiler (if neccessary)
  1469. 58: */
  1470. 59: #undef MSC
  1471. 60: #define MSC 1 /* Microsoft C */
  1472. 61:
  1473. 62: #define AUTO_SEL 1
  1474. 63: /*
  1475. 64: * The above #define allowes an automatic language set selection. It is
  1476. 65: * only functional if the used compiler identifies itself via a #define.
  1477. 66: *
  1478. 67: * Note: If AUTO_SEL is set, the parameters below are meaningless!
  1479. 68: */
  1480.  
  1481. 69:
  1482. 70: #define USE_FAR 0 /* use far keyword */
  1483. 71: #define USE_NEAR 0 /* use near keyword */
  1484. 72: #define USE_VOID 1 /* use void keyword */
  1485. 73: #define USE_VOLA 0 /* use volatile keyword */
  1486. 74: #define USE_CONST 0 /* use const keyword */
  1487. 75: #define USE_PROTT 0 /* use function prototypes */
  1488. 76: #define USE_INTR 0 /* use interrupt keyword */
  1489. 78: /* +--------------------------------------------------------+
  1490. 79: * End Of Configurable Parameters 
  1491. 80: * +--------------------------------------------------------+
  1492. 81: * Please do not make any changes below this point!
  1493. 82: */
  1494. 83:
  1495. 84: #ifdef SYMDEB
  1496. 85: # define SYMDEB 0
  1497. 86: #endif
  1498. 87:
  1499. 88: /*
  1500. 89: * Check target_compiler. Note that the MSC switch is overriden if
  1501. 90: * either __TURBOC__ or DLC are defined.
  1502. 91: */
  1503. 92: #ifdef __TURBOC______LINEEND____
  1504. 93: # undef MSC
  1505. 94: #endif
  1506. 95: #ifdef DLC
  1507. 96: # undef MSC
  1508. 97: #endif
  1509. 98: #if STARSYS
  1510. 99: # undef MSC
  1511. 100: #endif
  1512. 101:
  1513. 102: #if !(MSDOS OS2)
  1514. 103: # undef MSC
  1515. 104: # undef AUTO_SEL
  1516. 105: # define AUTO_SEL 0
  1517. 106: #endif
  1518. 107:
  1519. 108: #if OS2
  1520. 109: # undef MSC
  1521. 110: # define MSC 1
  1522. 111: # undef AUTO_SEL
  1523. 112: # define AUTO_SEL 1
  1524. 113: #endif
  1525. 114:
  1526. 115: /*
  1527. 116: * Compiler ANSI-compatible?
  1528. 117: * (First we assume it's not!)
  1529. 118: */
  1530. 119: #define ANSI_C 0
  1531. 120: #ifdef MSC
  1532. 121: # undef ANSI_C
  1533. 122: # define ANSI_C 1
  1534. 123: #endif
  1535. 124: #ifdef TURBO_C
  1536. 125: # undef ANSI_C
  1537. 126: # define ANSI_C 1
  1538. 127: #endif
  1539. 128:
  1540.  
  1541. 129: #if AUTO_SEL
  1542. 130: # undef USE_FAR
  1543. 131: # undef USE_NEAR
  1544. 132: # undef USE_VOID
  1545. 133: # undef USE_VOLA
  1546. 134: # undef USE_CONST
  1547. 135: # undef USE_PROTT
  1548. 136: # undef USE_INTR
  1549. 137: # ifdef __TURBOC______LINEEND____
  1550. 138: # define USE_FAR 1
  1551. 139: # define USE_NEAR 1
  1552. 140: # define USE_VOID 1
  1553. 141: # define USE_VOLA 1
  1554. 142: # define USE_CONST 1
  1555. 143: # define USE_PROTT 1
  1556. 144: # define USE_INTR 1
  1557. 145: # endif
  1558. 146: # ifdef DLC
  1559. 147: # define USE_FAR 1
  1560. 148: # define USE_NEAR 1
  1561. 149: # define USE_VOID 1
  1562. 150: # define USE_VOLA 1
  1563. 151: # define USE_CONST 1
  1564. 152: # define USE_PROTT 1
  1565. 153: # define USE_INTR 0
  1566. 154: # endif
  1567. 155: # ifdef MSC
  1568. 156: # define USE_FAR l
  1569. 157: # define USE_NEAR 1
  1570. 158: # define USE_VOID 1
  1571. 159: # define USE_VOLA 1
  1572. 160: # define USE_CONST 1
  1573. 161: # define USE_PROTT 1
  1574. 162: # define USE_INTR 1
  1575. 163: # endif
  1576. 164: #endif
  1577. 165:
  1578. 166:
  1579. 167: #if !USE_FAR
  1580. 168: #define far
  1581. 169: #endif
  1582. 170:
  1583. 171: #if !USE_NEAR
  1584. 172: #define near
  1585. 173: #endif
  1586. 174:
  1587. 175: #if !USE_VOID
  1588. 176: #define void
  1589. 177: #endif
  1590. 178:
  1591. 179: #if !USE_VOLA
  1592. 180: #define volatile
  1593. 181: #endif
  1594. 182:
  1595. 183: #if !USE_CONST
  1596. 184: #define const
  1597. 185: #endif
  1598. 186:
  1599. 187: #if USE_INTR
  1600.  
  1601. 188: # ifdef MSC
  1602. 189: # define INTERRUPT interrupt far
  1603. 190: # else
  1604. 191: # define INTERRUPT interrupt
  1605. 192: # endif
  1606. 193: #else
  1607. 194: # define INTERRUPT
  1608. 195: #endif
  1609. 196:
  1610. 197: #if USE_PROTT
  1611. 198: # define PROTT(x) x
  1612. 199: # ifdef MSC
  1613. 200: # define STATICPT(func, prott) static func prott
  1614. 201: # else
  1615. 202: # define STATICPT(func, prott) extern func prott
  1616. 203: # endif
  1617. 204: #else
  1618. 205: # define PROTT(x) ()
  1619. 206: # ifdef MSC
  1620. 207: # define STATICPT(func, prott) static func ()
  1621. 208: # else
  1622. 209: # define STATICPT(func, prott) extern func ()
  1623. 210: # endif
  1624. 211: #endif
  1625. 212:
  1626. 213: #ifdef MSC
  1627. 214: # define inportb(port) inp(port)
  1628. 215: # define outportb(port, val) outp(port, val)
  1629. 216: #endif
  1630. 217:
  1631. 218: #ifdef__TURBOC______LINEEND____
  1632. 219: # define REGPKT struct REGS
  1633. 220: #else
  1634. 221: # define REGPKT union REGS
  1635. 222: #endif
  1636. 223:
  1637. 224: #ifdef DLC
  1638. 225: # define defined(x)
  1639. 226: # define inportb inp
  1640. 227: # define outportb outp
  1641. 228: #endif
  1642. 229:
  1643. 230: #if !SYMDEB /* symbolic debugging support */
  1644. 231: # define STATICATT static
  1645. 232: #endif
  1646. 233:
  1647. 234: #if STARSYS
  1648. 235: # define exit(x) dx_exit(x)
  1649. 236: #endif
  1650. 237:
  1651. 238: /*
  1652. 239: * Define open modes according to selected operating system/compiler.
  1653. 240: */
  1654. 241: #if MSDOS 0S2
  1655. 242: # define OPM_WB "wb"
  1656. 243: # define OPM_WT "wt"
  1657. 244: # define OPM_RB "rb"
  1658. 245: # define OPM_RT "rt"
  1659. 246: #endif
  1660.  
  1661. 247:
  1662. 248: #if UNIX
  1663. 249: # define OPM_WB "w"
  1664. 250: # define OPM_WT "w"
  1665. 251: # define OPM_RB "r"
  1666. 252: # define OPM_RT "r"
  1667. 253: #endif
  1668. 254:
  1669. 255: #define TRUE 1
  1670. 256: #define FALSE 0
  1671. 257:
  1672. 258: typedef unsigned char uchar:;
  1673. 259: typedef int bool;
  1674. 260: typedef unsigned short ushort;
  1675. 261: typedef unsigned long ulong;
  1676. 262:
  1677. 263: #define tonumber(x) ((x) - '0')
  1678. 264: #define FOREVERL() for(;;)
  1679. 265:
  1680. 266: /*
  1681. 267: * Select #include-files depending on target compiler and OS.
  1682. 268: *
  1683. 269: * Phases:
  1684. 270: * 1. Define all include selection constants to true or false.
  1685. 271: * 2. Select actual include files and include them.
  1686. 272: * 3. #Undef all include selection constants.
  1687. 273: */
  1688. 274: #ifndef INCL_STDIO
  1689. 275: # define INCL_STDIO 0
  1690. 276: #else
  1691. 277: # under INCL_STDIO
  1692. 278: # define INCL_STDIO 1
  1693. 279: #endif
  1694. 280: #ifndef INCL_CURSES
  1695. 281: # define INCL_CURSES 0
  1696. 282: #else
  1697. 283: # undef INCL_CURSES
  1698. 284: # define INCL_CURSES 1
  1699. 285: #endif
  1700. 286: #ifndef INCL_CTYPE
  1701. 287: # define INCL_CTYPE 0
  1702. 288: #else
  1703. 289: # undef INCL_CTYPE
  1704. 290: # define INCL_CTYPE 1
  1705. 291: #endif
  1706. 292: #ifndef INCL_ASSERT
  1707. 293: # define INCL_ASSERT 0
  1708. 294: #else
  1709. 295: # undef INCL_ASSERT
  1710. 296: # define INCL_ASSERT 1
  1711. 297: #endif
  1712. 298: #ifndef INCL_LLIO
  1713. 299: # define INCL_LLIO 0
  1714. 300: #else
  1715. 301: # undef INCL_LLIO
  1716. 302: # define INCL_LLIO 1
  1717. 303: #endif
  1718. 304: #ifndef INCL_PROCESS
  1719. 305: # define INCL_PROCESS 0
  1720.  
  1721. 306: #else
  1722. 307: # undef INCL_PROCESS
  1723. 308: # define INCL_PROCESS 1
  1724. 309: #endif
  1725. 310: #ifndef INCL_MEMORY
  1726. 311: # define INCL_MEMORY 0
  1727. 312: #else
  1728. 313: # undef INCL_MEMORY
  1729. 314: # define INCL_MEMORY 1
  1730. 315: #endif
  1731. 316: #ifndef INCL_STRING
  1732. 317: # define INCL_STRING 0
  1733. 318: #else
  1734. 319: # undef INCL_STRING
  1735. 320: # define INCL_STRING 1
  1736. 321: #endif
  1737. 322: #ifndef INCL_STDLIB
  1738. 323: # define INCL_STDLIB 0
  1739. 324: #else
  1740. 325: # undef INCL_STDLIB
  1741. 326: # define INCL_STDLIB 1
  1742. 327: #endif
  1743. 328: #ifndef INCL_CONVERT
  1744. 329: # define INCL_CONVERT 0
  1745. 330: #else
  1746. 331: # undef INCL_CONVERT
  1747. 332: # define INCL_CONVERT 1
  1748. 333: #endif
  1749. 334: #ifndef INCL_MSDOS
  1750. 335: # define INCL_MSDOS 0
  1751. 336: #else
  1752. 337: # undef INCL_MSDOS
  1753. 338: # define INCL_MSDOS 1
  1754. 339: #endif
  1755. 340: #ifndef INCL_CONIO
  1756. 341: # define INCL_CONIO 0
  1757. 342: #else
  1758. 343: # undef INCL_CONIO
  1759. 344: # define INCL_CONIO 1
  1760. 345: #endif
  1761. 346:
  1762. 347: #if INCL_STDIO && !(INCL_CURSES && UNIX)
  1763. 348: # include <stdio.h>
  1764. 349: #endif
  1765. 350: #if INCL_CURSES && UNIX
  1766. 351: # include <curses.h>
  1767. 352: #endif
  1768. 353: #if INCL_CTYPE INCL_CONVERT
  1769. 354: # include <ctype.h>
  1770. 355: #endif
  1771. 356: #if INCL_ASSERT
  1772. 357: # include <assert.h>
  1773. 358: # ifdef MSC
  1774. 359: # undef INCL_PROCESS
  1775. 360: # define INCL_PROCESS 1
  1776. 361: # endif
  1777. 362: # ifdef __TURBOC______LINEEND____
  1778. 363: # undef INCL_PROCESS
  1779. 364: # define INCL_PROCESS 1
  1780.  
  1781. 365: # endif
  1782. 366: #endif
  1783. 367: #if INCL_LLIO
  1784. 368: # ifdef MSC
  1785. 369: # include <fcntl.h>
  1786. 370: # include <io.h>
  1787. 371: # endif
  1788. 372: #endif
  1789. 373: #if INCL_PROCESS
  1790. 374: # ifdef MSC
  1791. 375: # include <process.h>
  1792. 376: # endif
  1793. 377: #endif
  1794. 378: #if INCL_MEMORY
  1795. 379: # include <malloc.h>
  1796. 380: #endif
  1797. 381: #if INCL_STRING
  1798. 382: # if ANSI_C
  1799. 383: # include <string.h>
  1800. 384: # endif
  1801. 385: #endif
  1802. 386: #if INCL_STDLIB INCL_CONVERT
  1803. 387: # if ANSI_C
  1804. 388: # include <stdlib.h>
  1805. 389: # endif
  1806. 390: #endif
  1807. 391: #if INCL_CONIO
  1808. 392: # ifdef __TURBOC______LINEEND____
  1809. 393: # include <conio.h>
  1810. 394: # endif
  1811. 395: # ifdef MSC
  1812. 396: # include <conio.h>
  1813. 397: # endif
  1814. 398: #endif
  1815. 399: #if MSDOS && INCL_MSDOS
  1816. 400: # include <dos.h>
  1817. 401: #endif
  1818. 402:
  1819. 403:
  1820. 404: /*
  1821. 405: * Purge utility #defines.
  1822. 406: */
  1823. 407: #undef INCL_STDIO
  1824. 408:
  1825. 409: #endif
  1826.  
  1827.  
  1828.  
  1829.  
  1830.  
  1831.  
  1832.  
  1833.  
  1834.  
  1835.  
  1836.  
  1837.  
  1838.  
  1839.  
  1840.  
  1841.  
  1842.  
  1843.  
  1844. Writing Standard Headers: The String Functions
  1845.  
  1846.  
  1847. Dan Saks
  1848.  
  1849.  
  1850. Dan Saks is the owner of Saks & Associates, which offers training and
  1851. consulting in C and C++. He is a member of X3J11, the ANSI C committee. He has
  1852. an M.S.E. in computer science from the University of Pennsylvania. You can
  1853. write to him at 287 W. McCreight Ave., Springfield, OH 45504 or call (513)
  1854. 324-3601.
  1855.  
  1856.  
  1857. In a recent letter to The C Users Journal, Phil Cogar of N.S.W. Australia
  1858. complained that much of the C source code appearing in this and other
  1859. programming journals contains references to headers such as <stdlib.h> that
  1860. are not published along with the code. He observed that if your compiler
  1861. provides these headers, then typing in the code and getting it to run is
  1862. usually easy; without them, it may be impossible. He has a legitimate
  1863. complaint, but as editor Robert Ward points out in his response, it's often
  1864. impractical to publish the headers with the code. (See The C Users Journal,
  1865. October 1989, p.138.)
  1866. To get the programs to run, you can write your own standard headers to go with
  1867. your existing compiler and library. Although writing an entire Standard C
  1868. library from scratch is a big chore, you can fill many of the gaps in an
  1869. existing library by yourself in only a few days.
  1870.  
  1871.  
  1872. The Standard Headers
  1873.  
  1874.  
  1875. The fifteen headers specified by the Standard are summarized in Table 1. Most
  1876. of them declare a set of related library functions, along with any macros and
  1877. types needed to call them. A few headers don't contain any functions; they
  1878. simply define useful macros and types that have nowhere else to go. Some
  1879. macros and types appear in more than one header, but each function is declared
  1880. only once.
  1881. Most compilers supply additional headers. For example, UNIX compilers add
  1882. headers such as <direct.h>, <fcntl.h> and <process.h>. Many MS-DOS compilers
  1883. supply some of the UNIX headers, along with others such as <bios.h>, <conio.h>
  1884. and <dos.h>. None of these headers is covered by the C Standard. Some UNIX
  1885. headers have been formalized by the IEEE 1003.1 POSIX Portable Operating
  1886. System Standard, but many aren't covered by any non-proprietary standard. A C
  1887. program using library headers other than those listed in Table 1 will not be
  1888. portable to all Standard C implementations.
  1889. A program accesses the contents of a standard header by referencing the header
  1890. in an include directive, such as
  1891. #include <stdio.h>
  1892. Headers are often referred to as "include files" because they are almost
  1893. always implemented as source files with the same names. Other implementations
  1894. are permitted, and so the Standard is careful not to refer to them as files.
  1895. Nevertheless, "headers" and "include files" are generally understood to mean
  1896. the same thing.
  1897.  
  1898.  
  1899. Determining What You Already Have
  1900.  
  1901.  
  1902. Before starting to fix your standard headers, you should look to see what you
  1903. already have. Headers are usually easy to locate them. For example, on UNIX
  1904. systems the headers for cc are usually in /usr/include (see the subheading
  1905. FILES on the manual page(s) for cc(1) in your UNIX manual). The default setup
  1906. for Turbo C on MS-DOS places the headers in \turboc\include. Most MS-DOS
  1907. compilers do something similar. The headers for DECUS C on my PDP-11 are in
  1908. the same subdirectory as my compiler executables, which is a subdirectory with
  1909. the logical name C:.
  1910. You should not be surprised to find that you already have several of the
  1911. standard headers. The standard library is not pure invention; it's the result
  1912. of an effort to "codify common existing practice." You will almost certainly
  1913. find a version of <stdio.h> -- the only standard header used by Kernighan and
  1914. Ritchie in the first edition of The C Programming Language. <ctype.h> is also
  1915. extremely common. Beyond that, it's hard to say just how many headers you're
  1916. likely to find.
  1917. For example, the DECUS C compiler has only four of the standard headers:
  1918. <ctype.h>, <setjmp.h>, <stdio.h>, and <time.h>. The UNIX 4.2 BSD compiler (cc)
  1919. has these four, plus <assert.h>, <errno.h>, <math.h>, and <signal.h>. It also
  1920. has <varargs.h>, which is very similar to <stdarg.h>. Turbo C 2.0, Microsoft C
  1921. 5.1 and Zortech C 1.07 (all for MS-DOS) have every header except <locale.h>,
  1922. but very few of the headers among all three compilers are exactly as they
  1923. should be.
  1924.  
  1925.  
  1926. Where To Put New Headers
  1927.  
  1928.  
  1929. Before you start creating and modifying headers, you should think about where
  1930. to put them. You can throw caution to the wind and put the new headers in the
  1931. same directory as your existing ones (assuming you have the access rights),
  1932. but then you run a serious risk that some of your old code won't work with the
  1933. new headers. I recommend creating a directory for your new headers and
  1934. reconfiguring your compiler environment to search this new directory before it
  1935. searches the old one. Remove the new headers from the search if you have to.
  1936. Compiler environments vary so much that I can't explain how to do this for
  1937. everyone, but I will show you what I've done on a few different systems:
  1938.  
  1939.  
  1940. On UNIX 4.2 BSD:
  1941.  
  1942.  
  1943. I put the new headers in a subdirectory /usr/include within my home directory
  1944. (/u/dsaks). I wrote a shell script called cc that simply contains
  1945. /bin/cc -I/u/dsaks/usr/include $*
  1946. This script invokes the UNIX C compiler (in /bin) with the -I option. -I tells
  1947. the compiler to search for include files in the named directory before
  1948. searching in the standard places. The $* passes all the arguments to the cc
  1949. script through to the C compiler.
  1950. I put this script in /u/dsaks/usr/bin, and added this directory name to my
  1951. shell path variable. I made the script executable by using
  1952. chmod +x cc
  1953. This cc command compiles with the new headers. If I need to omit them, I
  1954. simply rename the command with
  1955. mv cc cc.new
  1956. so the cc command reverts to the one in /usr/bin (without -I).
  1957.  
  1958.  
  1959. On MS-DOS 3.0 and higher:
  1960.  
  1961.  
  1962. I put the original headers for Microsoft C and Quick C in \ms\include, and my
  1963. new headers in \ms\usr\include. Both compilers support the -I option, so you
  1964. can create a cc.bat command file like the UNIX shell script. Yet, Microsoft
  1965. gives you an easier alternative. The Microsoft compilers use the INCLUDE
  1966. environment variable to define the search path for include files. I use two
  1967. different command files to configure the compiler environment. My msnew.bat
  1968. uses
  1969. set INCLUDE=c:\ms\usr\include;c:\ms\include
  1970. to put the new headers in the search path, while msold.bat uses
  1971. set INCLUDE=c:\ms\include
  1972.  
  1973. to take them out.
  1974. Other MS-DOS compilers require slightly different approaches. Zortech's
  1975. command line compiler, ZTC, uses the INCLUDE environment variable just like
  1976. Microsoft C, but their integrated environment, ZED, gets its search path from
  1977. a configuration file maintained by a utility called ZCONFIG. Borland's Turbo C
  1978. lets you specify the search path in a file called TURBOC.CFG. Consult your
  1979. compiler user's guide for details.
  1980.  
  1981.  
  1982. On RT-11 V5.0 and higher:
  1983.  
  1984.  
  1985. The DECUS C compiler has a built-in preprocessor that's virtually useless.
  1986. Fortunately, the compiler is distributed with MP, a decent preprocessor from
  1987. the UNIX User's Group. My compilation command files disable the built-in
  1988. preprocessor (with the /M compiler switch) and use MP instead.
  1989. MP has a preset search path for include files. First it looks in the directory
  1990. with the logical name LB:, then it looks in C:, and finally it looks in SY:. I
  1991. put the original headers in a directory assigned to C: and the new headers in
  1992. another directory assigned to LB:. I can remove the new headers from the
  1993. search by deassigning LB:.
  1994.  
  1995.  
  1996. <string.h>
  1997.  
  1998.  
  1999. I'll begin with <string.h> because it's often missing and yet is easy to
  2000. create. Once you have it, you'll use it frequently.
  2001. <string.h> (see Table 2) declares the string handling functions in the
  2002. library. It also declares one macro, NULL, and one type, size_t, that are
  2003. needed to use these functions.
  2004. There is no universal way to define NULL ---- you tailor the definition to
  2005. your machine's architecture. The easiest way to obtain a definition for NULL
  2006. is to steal one from <stdio.h>. If you can't find a definition there or in
  2007. some other header, then you should probably use
  2008. #define NULL ((void *)0)
  2009. if your compiler supports the void * type, or
  2010. #define NULL ((char *)0)
  2011. if it doesn't. If you know that your pointers have the same size as type int,
  2012. you can use simply
  2013. #define NULL 0
  2014. If the pointers on your machine have the same size as type long int, you can
  2015. use
  2016. #define NULL OL
  2017. I prefer to use the casts to determine the size of NULL. However, I suspect
  2018. you'll find that one of the latter two forms is already used in your existing
  2019. headers. Whichever form you choose, use it consistently.
  2020. Most MS-DOS C compilers provide pointers in two different sizes, near and far.
  2021. The headers in these compilers use conditional compilation to select the
  2022. appropriate definition for NULL, something like
  2023. #ifdef _NEAR_POINTERS
  2024. #define NULL 0
  2025. #else
  2026. #define NULL OL
  2027. #endif
  2028. If your <string.h> needs a definition like this, you should find it in one of
  2029. your existing headers. (For more insight into the possible definitions for
  2030. NULL, see "Doctor C's Pointers: The 'NULL' Macro and Null Pointers" by Rex
  2031. Jaeschke in The C Users Journal, Sept/Oct, 1988.)
  2032. NULL is defined in several standard headers. The headers may be included in
  2033. any order, and a given header may be included more than once, so you must
  2034. insure that the repeated definitions for NULL don't conflict with each other.
  2035. Most implementations permit "benign" macro redefinitions (repeated definitions
  2036. formed by identical sequences of tokens) as specified in the Standard. In this
  2037. case, make all the definitions the same. If your preprocessor doesn't allow
  2038. any redefinitions, you will have to put a "protective wrapper" around each
  2039. one, as in
  2040. #ifndef NULL
  2041. #define NULL ((void *)O)
  2042. #endif
  2043. size_t is the type of the result of the sizeof operator. The Standard says
  2044. that it should be an unsigned integral type, so use either
  2045. typedef unsigned size_t;
  2046. or
  2047. typedef unsigned long size_t;
  2048. You can select the appropriate definition using the program in Listing 1.
  2049. In many C implementations, sizeof yields a signed int value. You should still
  2050. define size_t as unsigned, so that operations on objects of that type have the
  2051. proper unsigned behavior. You can always use size_t to cast the possibly
  2052. negative result of sizeof to its 'true' unsigned value, as in
  2053. if ((size_t)sizeof(something_big) > 0)
  2054. For more about size_t and sizeof, see "Doctor C's Pointers: Exploring the
  2055. Subtle Side of the 'sizeof' Operator" by Rex Jaeschke in The C Users Journal,
  2056. Feb., 1988 or see Rex's book, listed in References.
  2057. As with NULL, size_t appears in several standard headers. The Standard and
  2058. many implementations do not allow typedef redefinitions (even "benign" ones)
  2059. in the same scope, so you may need a protective wrapper around each
  2060. definition. For example
  2061. #ifndef _SIZE_T_DEFINED
  2062. typedef unsigned size_t;
  2063. #define _SIZE_T_DEFINED
  2064. #endif
  2065. You don't have to use the name _SIZE_T_DEFINED. Any identifier beginning with
  2066. an underscore followed by an upper-case letter or another underscore will do.
  2067. The Standard reserves these names for the implementation of the compiler (of
  2068. which the headers are part).
  2069. Since benign macro redefinitions are usually allowed, you may be tempted to
  2070. define size_t as
  2071. #define size_t unsigned
  2072. in order to eliminate the protective wrapper. I have seen this done in some
  2073. "ANSI-conforming" compilers. Although you will probably never notice the
  2074. difference, the macro definition is wrong because it changes the scope of
  2075. size_t. Use the typedef.
  2076. And now for the functions. Most older C compilers don't support prototypes, so
  2077. you might have to delete or "comment out" the parameter lists. Some functions
  2078. return void *. If your compiler won't accept that type, use char *.
  2079. You will find that your library contains some, but not all, of the string
  2080. functions. Sometimes you will find a standard C function under an archaic
  2081. name. Many recent books on C have an appendix that details the functions in
  2082. the standard library. (See references at the end of the article.) You should
  2083. compare the functions in the standard library with the functions in your
  2084. compiler's library to find as many matches as you can.
  2085. For example, some implementations use index instead of strchr. In this case,
  2086. you could declare strchr as
  2087. char *index();
  2088. #define strchr(s, c) index(s, c)
  2089. but there is a hazard. If you forget that strchr is really index, and write
  2090. another function called index, you will inadvertently redefine strchr. (This
  2091. is an excellent way to test your debugging skills.) This macro definition
  2092. should only be used as an interim fix until you add a compiled version of the
  2093. missing function to the run-time library.
  2094.  
  2095. What about functions that are completely missing? Should you still put their
  2096. declarations in <string. h>? The answer is a definite maybe.
  2097. Suppose that memchr is missing from your library. memchr returns a void *, but
  2098. if you leave the declaration out of <string. h>, the compiler will assume it
  2099. returns an int. When you compile
  2100. char *p, s[10];
  2101. p = memchr(s, 'x', 10);
  2102. you may get a spurious warning about an illegal pointer assignment, but
  2103. compilation will continue. You won't know what's really happening until the
  2104. linker reports that memchr is undefined. Under these circumstances, you should
  2105. declare memchr in the header to eliminate the unnecesary warnings.
  2106. If you use a Lint-like program checker that can detect undeclared functions
  2107. (or if your compiler has such an option), then don't declare functions that
  2108. are missing from the library. When you reference a missing function, you will
  2109. still get a meaningful error message, but won't have to wait for the linker to
  2110. tell you what you already know.
  2111. Listing 2 shows the <string.h> that I use on UNIX 4.2 BSD. It includes some
  2112. interim macro definitions for missing functions. The #ifndef ... #endif
  2113. wrapper around the entire header prevents repeated compilation of the
  2114. declarations if the header is included more than once. The wrapper isn't
  2115. needed for protection since you can redeclare functions (provided all
  2116. declarations in the same scope are the same), and everything else in the
  2117. header is either benign or protected.
  2118. I added the wrapper to simplify debugging. While debugging macros, I sometimes
  2119. look at the preprocessor output to verify the expansions. Eliminating
  2120. redundant headers from preprocessor output makes it easier to read. The
  2121. comment at the header's beginning is not in the wrapper so it still appears
  2122. wherever the header is included, even if the rest of the header does not.
  2123. One final word of caution. In Listing 2, strlen is declared to return a
  2124. size_t, even though strlen is actually defined in the library to return an
  2125. int. On machines where a signed int to unsigned int conversion performs no
  2126. transformation of the data (as on twos-complement machines), strlen returning
  2127. a size_t is perfectly safe. On other machines, you should leave the
  2128. declaration as
  2129. int strlen();
  2130. so that the compiler can recognize that
  2131. size_t n;
  2132. n = strlen(s);
  2133. involves a signed to unsigned conversion and generate the proper code. You
  2134. should also cast the result of strlen to size_t whenever strlen is used in an
  2135. expression with other ints, such as
  2136. if ((size_t)strlen(s) > 0)
  2137. This is the same technique used with sizeof when it returns an int.
  2138.  
  2139.  
  2140. Conclusion
  2141.  
  2142.  
  2143. In this article I've tried to show why it's impossible to just publish a
  2144. single portable version of the standard headers. The headers provide a
  2145. portable definition of the Standard C environment, but they do it in a
  2146. non-portable way.
  2147. Rather than writing the missing string functions in the library, I suggest you
  2148. write the remaining standard headers. Doing so solves more portability
  2149. problems and gives you the definitions you need to compile new library
  2150. functions as you write them. In <string. h>, you've already seen many of the
  2151. design problems, so most of the remaining work is simply determining what goes
  2152. into the other headers.
  2153. References
  2154. Darnell, Peter and Margolis, Philip, Software Engineering in C (1988,
  2155. Springer-Verlag).
  2156. Gardner, James, From C to C: An Introduction to ANSI Standard C (1989,
  2157. Harcourt Brace Jovanovich).
  2158. Jaeschke, Rex, Portability and the C Language, (1989, Hayden Books).
  2159. Plauger, P.J. and Brodie, Jim, Standard C (1989, Microsoft Press).
  2160. Ritchie, Dennis and Kernighan, Brian, The C Programming Language, 2nd. ed.
  2161. (1988, Prentice-Hall).
  2162. Table 1
  2163. Standard Headers
  2164.  
  2165. assert.h - program diagnostics
  2166. ctype.h - character testing and case mapping
  2167. errno.h - error reporting
  2168. float.h - floating type characteristics
  2169. limits.h - integral type sizes
  2170. locale.h - local customs
  2171. math.h - mathematics
  2172. setjmp.h - non-local jumps
  2173. signal.h - signal handling
  2174. stdarg.h - variable-length arguments lists
  2175. stddef.h - common definitions
  2176. stdio.h - input and output
  2177. stdlib.h - general utilities
  2178. string.h - string handling
  2179. time.h - date and time utilities
  2180.  
  2181. Table 2
  2182. Summary of <string.h>
  2183.  
  2184. Macros:
  2185.  
  2186. NULL
  2187.  
  2188. Types:
  2189.  
  2190. size_t
  2191.  
  2192.  
  2193. Function Prototypes:
  2194.  
  2195. void *memchr(const void *, int, size_t);
  2196. int memcmp(const void *, const void *, size_t);
  2197. void *memcpy(void *, const void *, size_t);
  2198. void *memmove(void *, const void *, size_t);
  2199. void *memset(void *, int, size_t);
  2200. char *strcat(char *, const char *);
  2201. char *strchr(const char *, int);
  2202. int strcoll(const char *, const char *);
  2203. int strcmp(const char *, const char *);
  2204. char *strcpy(char *, const char *);
  2205. size_t strcspn(const char *, const char *);
  2206. char *strerror(int);
  2207. size_t strlen (const char *);
  2208. char *strncat(char *, const char *, size_t);
  2209. int strncmp(const char *, const char *, size_t);
  2210. char *strncpy(char *, const char *, size_t);
  2211. char *strpbrk(const char *, const char *);
  2212. char *strrchr(const char *, int);
  2213. size_t strspn(const char *, const char *);
  2214. char *strstr(const char *, const char *);
  2215. char *strtok(char *, const char *);
  2216.  
  2217. Listing 1
  2218. /*
  2219. * write the definition for size_t
  2220. */
  2221. #include <stdio.h>
  2222.  
  2223. main()
  2224. {
  2225. printf("typedef unsigned%s size_t;\n",
  2226. sizeof(sizeof(int)) == sizeof(int) ? "" : "long");
  2227. }
  2228.  
  2229.  
  2230. Listing 2
  2231. /*
  2232. * string.h - string hadling (for cc on UNIX 4.2 BSD)
  2233. */
  2234.  
  2235. #ifndef _STRING_H_INCLUDED
  2236.  
  2237. #define NULL ((char *)0)
  2238.  
  2239. #ifndef _SIZE_T_DEFINED
  2240. typeder unsigned size_t;
  2241. #define _SIZE_T_DEFINED
  2242. #endif
  2243.  
  2244. char *strcat();
  2245. int strcmp();
  2246. char *strcpy();
  2247. size_t strlen();
  2248. char *strncat();
  2249. int strncmp();
  2250. char *strncpy();
  2251.  
  2252.  
  2253. /*
  2254. * interim macro definitions for functions
  2255. */
  2256. char *index();
  2257. #define strchr(s, c) index(s, c)
  2258.  
  2259. extern int sys_nerr;
  2260. extern char *sys_errlist[];
  2261. #define strerror(e) \
  2262. ((e) < sys_nerr ? sys_errlist[e] : "?no message?")
  2263.  
  2264. char *rindex();
  2265. #define strrchr(s, c) rindex(s, c)
  2266.  
  2267. /*
  2268. * missing functions
  2269. */
  2270. char *memchr();
  2271. int memcmp();
  2272. char *memcpy();
  2273. char *memmove();
  2274. char *memset();
  2275. int strcoll();
  2276. size_t strcspn();
  2277. char *strpbrk();
  2278. size_t strspn();
  2279. char *strstr();
  2280. char *strtok();
  2281. size_t strxfrm();
  2282.  
  2283. #define _STRING H_INCLUDED
  2284.  
  2285. #endif
  2286.  
  2287.  
  2288.  
  2289.  
  2290.  
  2291.  
  2292.  
  2293.  
  2294.  
  2295.  
  2296.  
  2297.  
  2298.  
  2299.  
  2300.  
  2301.  
  2302.  
  2303.  
  2304.  
  2305.  
  2306.  
  2307.  
  2308.  
  2309.  
  2310.  
  2311.  
  2312.  
  2313.  
  2314.  
  2315.  
  2316. UNIX 'termcap' Facility Improves Portability By Hiding Terminal Dependencies
  2317.  
  2318.  
  2319. Ronald Florence
  2320.  
  2321.  
  2322. Ronald Florence is a novelist, sheep farmer, occasional computer consultant,
  2323. and UNIX addict. He can be reached at ron@mlfarm or ...
  2324. {hsi,rayssd}!mlfarm!ron.
  2325.  
  2326.  
  2327. For programmers accustomed to writing for single-user systems, UNIX (and
  2328. Xenix) holds some quick surprises. All those carefully optimized, hand-coded
  2329. screens, the lightning-fast displays that write to the screen buffer, even
  2330. "well-behaved" routines that rely on BIOS calls, are suddenly useless.
  2331. Terminal displays, including the console, are treated as teletype devices
  2332. under UNIX. To perform even the simplest screen display function, such as
  2333. clearing the screen, the program must send the proper screen control sequence.
  2334. In effect, all screen displays are comparable to using the ANSI.SYS driver
  2335. under MS-DOS.
  2336. If the UNIX system had only a single terminal or if only one type of terminal
  2337. were used on the system, it would be easy enough to hand-code the proper
  2338. screen control sequences. Indeed, even if several different terminals are used
  2339. on a system, the screen control sequences can be hand coded. For example, the
  2340. function in Listing 1 could be used to clear screens.
  2341. For a closed system where most of the output is teletype format, with only
  2342. simple screen display commands, your programs may not need much more.
  2343. But what if the system is not closed? What if there are outside logins using a
  2344. variety of terminals? And what if you want to write screen displays that
  2345. utilize a wide range of terminal capabilities, including automargins and
  2346. optimized cursor motion, and make sure those displays are scaled to the size
  2347. of the terminal display? And what if some of the terminals using the system
  2348. require padding at certain speeds or have other quirks that make them
  2349. unsuitable or tricky to use with fancy screen display programs? It is possible
  2350. to keep adding options to code like Listing 1, but by the tenth terminal type,
  2351. the code starts to look like linguini.
  2352. The alternative is to use the termcap and terminfo databases of screen display
  2353. parameters and control sequences which are provided with most UNIX systems.
  2354. Termcap, which was developed at Berkeley, uses an ASCII database; the terminfo
  2355. database is compiled. A curses library of screen display and terminal input
  2356. functions is supplied with both systems. Terminfo is theoretically faster; it
  2357. supports many terminal capabilities which are normally not encoded into the
  2358. termcap database, and the curses library supplied with terminfo has many
  2359. capabilities which are not supported under termcap curses. The termcap
  2360. database is substantially easier to modify, and there are ways to incorporate
  2361. many of the capabilities of the terminfo curses into programs running on
  2362. termcap systems. This article will discuss only termcap, which is used by
  2363. Xenix and by most BSD systems.
  2364. The UNIX documentation describes the termcap routines as "low level" and the
  2365. curses routines as "higher level," in much the way that troff/nroff is a low
  2366. level formatting package, and the formatting macro packages (MM or MS) are
  2367. high level. Actually, the analogy is not really appropriate. Curses is a
  2368. screen optimization package with some convenient windowing functions. Termcap
  2369. is a straightforward package of functions to access the database of screen and
  2370. keyboard control sequences.
  2371. The termcap database is normally in the file /etc/termcap. Comments in the
  2372. file are prefaced with a # character. All lines which do not begin with the #
  2373. are considered part of the database.
  2374. Each entry in the database represents a different terminal. The entry begins
  2375. with alternate names of the terminal, separated by characters. Usually the
  2376. first name listed for the terminal is a special two-character abbreviation,
  2377. used by some older programs. The second name is used by most utilities, such
  2378. as the editor vi. The last name listed is the full name of the terminal, and
  2379. is the only name which can have blanks inserted for readability. Thus:
  2380. d1vt100vt-100pt100pt-100dec vt100:
  2381. are the names of a DEC vt-100. If you add terminal descriptions to the termcap
  2382. database, make sure that every name in your addition is unique.
  2383. The capabilities of the terminal are listed after the name, separated from one
  2384. another by colons. Newlines in the entry must be escaped with a backslash. The
  2385. capabilities are strings, boolean, or integers. Most are mnemonic. Boolean
  2386. capabilities are true if named. Strings follow an equals sign (=). Integers
  2387. follow a #. There are no spaces or tabs within capabilities or between them,
  2388. and an entry carried to a second line must repeat the :. Thus:
  2389. MTmytermMy Special Terminal:\
  2390. bs:am:cl =\E[J:ho=\E[H:lines#24:
  2391. indicates that myterm can backspace (bs), has automatic margins (am), that
  2392. there are 24 lines displayed on the screen, and gives the sequences that
  2393. should be sent to clear the screen (cl) and home the cursor (ho).
  2394. Several special sequences are used to encode the strings:\E is the escape
  2395. character (0x1b); ^X is "Control-X" or any other control key; \n, \r, \t, \b,
  2396. and \f are newline, carriage return, tab, backspace, and form feed; \^ is ^,
  2397. and \\ is \; All non-printing characters may be represented as octal escapes;
  2398. the :, which is used to separate capabilities in each entry, must be entered
  2399. as \072 if used in a string. Null characters can be entered as \200 because
  2400. the routines that process termcap entries strip the high bits of the output
  2401. late, so that \200 comes out \000.
  2402. Padding can be encoded into the strings by prefacing the string with an
  2403. integer, representing milliseconds of delay. An integer and a * indicate that
  2404. the delay is proportional to the number of lines involved in the execution of
  2405. the command. When the * is used, the delay can be stated in tenths of a
  2406. millisecond, so that 3.5* before the string for ce (clear to end of line)
  2407. would mean that the command requires 3.5 milliseconds of padding for each line
  2408. that is to be cleared.
  2409. Terminals which are identical to another entry with few exceptions can make
  2410. use of the tc string and the @ negator.
  2411. NTnewtermMy alternate terminal:lines=25:@bs:tc=vt100:
  2412. describes a terminal with 25 lines, no backspace capability, but otherwise
  2413. identical to a vt100.
  2414. One caution in using entries with tc encoding: programs with a fixed stack
  2415. (such as Xenix 286) may crash when reading tc encoded entries. The cure is to
  2416. make the stack larger with the -F option on the compile command line.
  2417. The cursor addressing string (cm) is coded with printf-like escapes. These are
  2418. described in detail in the termcap (M) entry in the UNIX documentation.
  2419. In addition to the regular termcap capabilities, which begin with lower case
  2420. letters, some UNIX systems utilize extensions. Xenix uses a variety of upper
  2421. case termcap entries to indicate special PC keys: PU for Page Up, EN for End,
  2422. GS for Start-Character-Graphics-Mode, and pseudo-mnemonics for eight-bit PC
  2423. graphics drawing characters. GNU Emacs uses upper- case capabilities to
  2424. describe terminal command sequences which are not generally used in termcap,
  2425. such as AL and DL for adding and deleting multiple lines. Programs which use
  2426. these extended termcap capabilities may not be portable to other UNIX systems.
  2427. The termcap library provides functions to retrieve the encoded information
  2428. from the database. The termcap routines first search the environment for a
  2429. TERMCAP variable. If it is found, does not begin with a slash, and the
  2430. terminal type matches the environment string TERM, the TERMCAP string is read.
  2431. If it begins with a slash, it is read as the pathname of the termcap database
  2432. (instead of the default /etc/termcap). Using the environment variable instead
  2433. of searching the database will speed up the development of new termcap
  2434. entries. If your system has a tset command which supports separate TERM and
  2435. TERMCAP environment entries, it will also speed the startup of programs which
  2436. use termcap.
  2437. One obvious use for the termcap database is in displaying formatted text to
  2438. the screen. Although there are wordprocessing programs available to run under
  2439. UNIX and/or Xenix, much text processing in UNIX systems is done by using an
  2440. editor (vi or emacs) to prepare the text with nroff/troff formatting codes,
  2441. usually with one of the macro packages such as MM. The formatted file is then
  2442. piped to a printer or type-setter, or to a screen display for proofing.
  2443. Although it is possible to prepare nroff terminal driving tables to encode the
  2444. screen control sequences needed for such formatting features as bold type,
  2445. italics or underlining, a different table would have to be encoded and
  2446. compiled for each terminal, and the user would have to indicate the terminal
  2447. type on the nroff command line:
  2448. nroff -cm -Tmyterm myfile
  2449. Also, the nroff terminal driving table format was created when daisy-wheel
  2450. printers were the cutting edge of desktop hardcopy capabilities, and the
  2451. coding is sometimes awkward to adapt to the capabilities of a terminal
  2452. display.
  2453. For simple text formatting, it is easier to parse the default nroff output,
  2454. which uses backspaces and overstrikes to generate underlined or bold
  2455. characters, and use termcap to look up the appropriate standout (bold) and
  2456. underline sequences. The program in Listing 2 (Bold.c), uses termcap library
  2457. functions to look up the terminal screen control sequences for so and se
  2458. (standout start and standout end), us and ue (underline start and underline
  2459. end), and sg, which is an integer coded quantity indicating how many spaces
  2460. the attribute change to standout mode requires. For terminals with multiple
  2461. fonts, the switchover to italic font could be encoded in us, so that
  2462. underlined text would be displayed in italics. A bold screen attribute could
  2463. be encoded in so and se, so that bold text would be displayed in bold font,
  2464. instead of in reverse video. Alternately, new termcap entries could be created
  2465. to hold the screen control sequences for bold or italic fonts.
  2466. The termcap access functions are simple and straightforward. To parse the
  2467. database, you need to allocate a buffer of 1024 characters (tbuf in Listing
  2468. 2), to hold the entire termcap entry as it is retrieved by tgetent(). This
  2469. buffer must be retained through all calls to the three functions which parse
  2470. capabilities: tgetstr(), tgetflag(), and tgetnum(). Another buffer (sbuf in
  2471. Listing 2) should be allocated for the strings which will be retrieved by
  2472. tgetstr(). This should be a static buffer. The tgetstr() function is passed
  2473. the address of a pointer to this buffer. As string capabilities are read, they
  2474. are placed in the buffer, and the pointer is advanced. Using a static buffer
  2475. saves the overhead of allocating space for each string as it is retrieved.
  2476. The termcap library also provides a function tputs(), which correctly sends
  2477. screen control sequences to the display, including any needed padding. tputs()
  2478. requires a pointer to a user-supplied function which can display a single
  2479. character. The function prch() (Listing 1) invokes the macro putchar().
  2480. Although it is not used here, the termcap library includes one other function,
  2481. tgoto(), which uses the cm (cursor movement) string to go to a desired column
  2482. and line. Because togoto() will output tabs, programs which make tgoto() calls
  2483. should turn off the TAB3 bit when setting the line protocol.
  2484. The function putout() in Listing 2 is not really necessary. It is used here to
  2485. check for insertions of ^G (0x7) in the text files. ^G was chosen because it
  2486. passes through nroff transparently. It is used to trigger expanded font in
  2487. files sent to the printer. In Bold.c, it triggers the insertion of a space
  2488. between characters to simulate expanded font.
  2489. Termcap can also be used to retrieve the sequences sent by non-ASCII keys,
  2490. like the arrow or functions keys. Although the termcap curses library does not
  2491. use the arrow or functions keys, the keys can be added to programs which use
  2492. curses for screen control by making a second set of termcap calls (curses
  2493. makes it own calls to termcap), and then reading for the arrow or function key
  2494. sequences in a getkey() routine (see Listing 3, keys.c).
  2495. Reading arrow keys for terminals which use a single character code for each
  2496. arrow (such as ^H, ^J, ^K, ^L) is simple, but many terminals, such as the PC
  2497. console, send escape-prefaced strings (ESC[A, ESC[B, etc.) when the arrow keys
  2498. or other non-ASCII keys are pressed. Some systems may balk at reading strings
  2499. with a simple read() system call. It is worth fiddling with the VMIN and VTIME
  2500. values in structure termio if you cannot read key sequences with the code in
  2501. getkey(). The values in function fixquit() in Listing 3 are a good start.
  2502. The alternative is to put the strings together out of characters read one at a
  2503. time. This may be the most reliable technique for an editor or other program
  2504. that reads repeated sequences of fast input characters that might be
  2505. misinterpreted, such as an ESC followed by a [ and an alphabetic character,
  2506. which an ANSI terminal might interpret as a screen control sequence. The trick
  2507. if you are reading a character at a time is to distinguish between a lone ESC
  2508. (0x1b) and an ESC sent as the first character of an escape sequence. One
  2509. technique is to set a timeout alarm. If you get the characters that would
  2510. constitute a key string before the timeout, return the key string, otherwise
  2511. return an ESC followed by individual characters. The whole procedure takes
  2512. tinkering, and fast typists can foul it up. Hence, using a read() call is
  2513. simpler.
  2514. One problem that can arise with the arrow key is that ^\, the UNIX "quit"
  2515. character, is used as an arrow key on some terminals. Even if the "quit"
  2516. signal is disabled, the keys will still be intercepted. The easiest fix to the
  2517. problem is to change the "quit" key to an impossible value. The function
  2518. fixquit() does this.
  2519. The global variable ttytype is set by the curses termcap routines, which in
  2520. this program are called before lookupkeys(). The ttytype could be set by a
  2521. call to getenv(), as in the code for Listing 1. The header file in Listing 4
  2522. (keys.h) defines integer equivalents for the arrow and function keys; these
  2523. defines can be used in switch statements. (The values given are those used in
  2524. the terminfo header files.)
  2525. What termcap cannot do is to optimize screen output by cutting down the
  2526. overhead of repeated cursor movement sequences. The output routines in the
  2527. curses library do a fair job and are simple to use. The code for life.c in
  2528. Listing 5 uses these routines along with the arrow key routines from key. c,
  2529. and while the speed of output cannot compare with an optimized routine writing
  2530. directly to screen memory, it is quick enough on a console or a terminal
  2531. running at 19,200 baud.
  2532. Highly optimized screen output which requires even more efficiency could mean
  2533. a journey into the treacherous code of screen display routines which calculate
  2534. the cost of each move. One such package is the display routines in the Gosling
  2535. Emacs code, which quite properly carries a dire warning to those who would
  2536. venture into the tangles of the code.
  2537.  
  2538. Listing 1
  2539. cls ()
  2540. {
  2541. char *getenv(), *term, *cl;
  2542.  
  2543. term = getenv ("TERM");
  2544. if (!strcmp(term, "ansi"))
  2545. cl = "\033[2J\033[H";
  2546. else if (!strcmp(term, "wy50"))
  2547.  
  2548. cl = "\033*";
  2549. /* add other terminals ... */
  2550. /* if all else fails, try a form feed */
  2551. else
  2552. cl = "\f";
  2553. fputs (cl, stderr);
  2554. }
  2555.  
  2556.  
  2557. Listing 2
  2558. /*
  2559. * Bold.c - filters nroff output for terminal display
  2560. * displays bold in SO, underlines, expanded font
  2561. * copyright 1987 Ronald Florence
  2562. */
  2563.  
  2564. #include <stdio.h>
  2565.  
  2566. #define UL 01
  2567. #define BOLD 02
  2568. #define ULSTOP 04
  2569. #define Bold() tputs(so, 1, prch), att = BOLD
  2570. #define Stopbold() tputs(se, 1, prch), att &= ~BOLD
  2571. #define Uline() tputs(us, 1, prch), att = UL
  2572. #define Stopuline() tputs(ue, 1, prch), att &= ~(ULULSTOP)
  2573.  
  2574. prch(c)
  2575. register char c;
  2576. {
  2577. putchar(c);
  2578. }
  2579.  
  2580. char *so, *se, *us, *ue;
  2581.  
  2582. main()
  2583. {
  2584. static char sbuf[256];
  2585. char tbuf[1024], *fill = sbuf, *tgetstr(), *getenv();
  2586. register a, c;
  2587. int i, att = 0;
  2588.  
  2589. if (tgetent(tbuf, getenv("TERM")) == 1 && tgetnum("sg") < 1)
  2590. {
  2591. so = tgetstr("so", &fill);
  2592. se = tgetstr("se", &fill);
  2593. us = tgetstr("us", &fill);
  2594. ue = tgetstr("ue", &fill);
  2595. }
  2596. a = getchar();
  2597. while ((c = getchar()) != EOF)
  2598. {
  2599. if (a == '_')
  2600. {
  2601. if (c == '_' && (att & UL) == 0)
  2602. Uline();
  2603. else if (c == '\b') /* nroff italics */
  2604. {
  2605. if ((a = getchar()) == EOF)
  2606. a = 0;
  2607.  
  2608. c = getchar();
  2609. if ((att & UL) == 0)
  2610. Uline();
  2611. }
  2612. if (c != '_' && (att & UL))
  2613. /* c is the last underline */
  2614. att = ULSTOP;
  2615. }
  2616. else if (c == '\b')
  2617. {
  2618. if ((c = getchar()) != a)
  2619. { /* Not a bold: print the character
  2620. and pass the \b to be printed. */
  2621. putout(a);
  2622. a = '\b';
  2623. }
  2624. else
  2625. {
  2626. if ((att & BOLD) == 0)
  2627. Bold();
  2628. for (i = 0; i < 5; i++)
  2629. if ((c = getchar()) != a && c != '\b')
  2630. break;
  2631. }
  2632. }
  2633. else if (att & BOLD)
  2634. Stopbold();
  2635. putout(a);
  2636. if (att & ULSTOP)
  2637. Stopuline();
  2638. a = c;
  2639. }
  2640. }
  2641.  
  2642.  
  2643. putout(c)
  2644. register char c;
  2645. {
  2646. static int expanded;
  2647.  
  2648. if (c == 07) /* ^G signals expanded font */
  2649. {
  2650. expanded++;
  2651. return(0);
  2652. }
  2653. putchar(c);
  2654. if (expanded)
  2655. {
  2656. if (c == '\n')
  2657. expanded = 0;
  2658. else
  2659. putchar(' ');
  2660. }
  2661. }
  2662.  
  2663.  
  2664. Listing 3
  2665. /*
  2666. * keys.c - gets arrow and function keys from termcap,
  2667.  
  2668. * returns terminfo codes
  2669. * changes quit key for use as arrow
  2670. *
  2671. * define NO_SYSV for versions of curses that do not look up
  2672. * arrow & function keys from termcap
  2673. *
  2674. * copyright 1988 Ronald Florence
  2675. * changed VMIN & VTIME for wy99 @ 9600 ron@mlfarm (7/11/88)
  2676. */
  2677.  
  2678. #include <curses.h>
  2679. #ifndef KEY_DOWN
  2680. #include "keys.h"
  2681. #endif
  2682.  
  2683. #define NKEYS 16
  2684.  
  2685. char
  2686. #ifdef NO_SYSV
  2687. *tcap_ids[] = {
  2688. "kd", "ku", "k1", "kr", "kh", "kb",
  2689. "k0", "k1", "k2", "k3", "k4", "k5", "k6", "k7", "k8", "k9", 0
  2690. },
  2691. #endif
  2692. *fkeys[NKEYS];
  2693.  
  2694. lookupkeys()
  2695. {
  2696. #ifdef NO_SYSV
  2697. static char sbuf[256];
  2698. char **key, tbuf[1024], *fill = sbuf, *tgetstr();
  2699. int i = 0;
  2700.  
  2701. tgetent(tbuf, ttytype);
  2702. for (key = tcap_ids; *key; ++key)
  2703. fkeys[i++] = tgetstr(*key, &fill);
  2704. #else
  2705. fkeys[0] = KD;
  2706. fkeys[1] = KU;
  2707. fkeys[2] = KL;
  2708. fkeys[3] = KR;
  2709. fkeys[4] = KH;
  2710. fkeys[5] = KB;
  2711. fkeys[6] = K0;
  2712. fkeys[7] = K1;
  2713. fkeys[8] = K2;
  2714. fkeys[9] = K3;
  2715. fkeys[10] = K4;
  2716. fkeys[11] = K5;
  2717. fkeys[12] = K6;
  2718. fkeys[13] = K7;
  2719. fkeys[14] = K8;
  2720. fkeys[15] = K9;
  2721. #endif
  2722. fixquit();
  2723. }
  2724.  
  2725.  
  2726. getkey()
  2727.  
  2728. {
  2729. char cmd[6];
  2730. register k;
  2731.  
  2732. k = read(0, cmd, 6);
  2733. cmd[k] = '\0';
  2734. for (k = 0; k < NKEYS; k++)
  2735. if (strcmp(cmd, fkeys[k]) == 0)
  2736. return (k + KEY_DOWN);
  2737. return ((int) *cmd);
  2738. }
  2739.  
  2740.  
  2741. fixquit()
  2742. {
  2743. struct termio new;
  2744.  
  2745. ioctl(0, TCGETA, &new);
  2746. new.c_cc[VQUIT] = 0xff; /* in case QUIT is an arrow */
  2747. new.c_cc[VTIME] = 1; /* minimum timeout */
  2748. new.c_cc[VMIN] = 3; /* three characters satisfy */
  2749. ioctl(0, TCSETA, &new);
  2750. }
  2751.  
  2752.  
  2753. Listing 4
  2754. /*
  2755. * keys. h
  2756. * copyright 1988 Ronald Florence
  2757. *
  2758. * use with curses programs that need extended keyboard
  2759. * (if tcap.h does not include the defines)
  2760. */
  2761.  
  2762. #define KEY_DOWN 0402
  2763. #define KEY_UP 0403
  2764. #define KEY_LEFT 0404
  2765. #define KEY_RIGHT 0405
  2766. #define KEY_HOME 0406
  2767. #define KEY_BACKSPACE 0407
  2768. #define KEY_F0 0410
  2769. #define KEY_F(n) (KEY_F0 + (n))
  2770.  
  2771.  
  2772. Listing 5
  2773. /*
  2774. life.c
  2775. copyright 1985, 1988 Ronald Florence
  2776.  
  2777. compile:
  2778. cc -O -s life.c keys.c -lcurses -ltermcap -o life
  2779. */
  2780.  
  2781.  
  2782. #include <curses.h>
  2783. #include <signal.h>
  2784. #ifndef KEY_DOWN
  2785. #include "keys.h"
  2786. #endif
  2787.  
  2788.  
  2789. #define ESC 0x1b
  2790. #define life '@'
  2791. #define crowd (life + 4)
  2792. #define lonely (life + 2)
  2793. #define birth (' ' + 3)
  2794. #define minwrap(a,d) a = --a < 0 ? d : a
  2795. #define maxwrap(a,d) a = ++a > d ? 0 : a
  2796. #define wrap(a,z) if (a < 0) (a) += z; \
  2797. else if (a > z) (a) = 1; \
  2798. else if (a == z) (a) = 0
  2799. #define MAXX (COLS-1)
  2800. #define MAXY (LINES-3)
  2801. #define boredom 5
  2802.  
  2803. typedef struct node
  2804. {
  2805. int y, x;
  2806. struct node *prev, *next;
  2807. } LIFE;
  2808.  
  2809. struct
  2810. {
  2811. int y, x;
  2812. } pos[8] = { { 1,-1}, {1, 0}, {1, 1}, {0, 1},
  2813. {-1, 1}, {-1, 0}, {-1,-1}, { 0,-1}
  2814. };
  2815.  
  2816. LIFE *head, *tail;
  2817.  
  2818. extern char *malloc();
  2819. char
  2820. *rules[] = {
  2821. " ",
  2822. "The Rules of Life:",
  2823. " ",
  2824. " 1. A cell with more than three neighbors",
  2825. " dies of overcrowding.",
  2826. " 2. A cell with less than two neighbors",
  2827. " dies of loneliness.",
  2828. " 3. A cell is born in an empty space",
  2829. " with exactly three neighbors.",
  2830. " ",
  2831. 0
  2832. },
  2833.  
  2834. *rules2[] = {
  2835. "Use the arrow keys or the vi cursor keys",
  2836. "(H = left, J = down, K = up, L = right)",
  2837. "to move the cursor around the screen.",
  2838. "The spacebar creates and destroys life.",
  2839. "<Esc> starts the cycle of reproduction.",
  2840. "<Del> ends life.",
  2841. " ",
  2842. "Press any key to play The Game of Life.",
  2843. 0
  2844. };
  2845.  
  2846. main(ac, av)
  2847.  
  2848. int ac;
  2849. char **av;
  2850. {
  2851. int i = 0, k, die();
  2852.  
  2853. initscr();
  2854. crmode();
  2855. noecho();
  2856. signal(SIGINT, die);
  2857. lookupkeys();
  2858. head = (LIFE *)malloc(sizeof(LIFE));
  2859. /* lest we have an unanchored pointer */
  2860. tail = (LIFE *)malloc(sizeof(LIFE));
  2861. head->next = tail;
  2862. tail->prev = head;
  2863.  
  2864. if (ac > 1)
  2865. readfn(*++av);
  2866. else
  2867. {
  2868. erase();
  2869. if (COLS > 40)
  2870. for ( ; rules[i]; i++)
  2871. mvaddstr(i+1, 0, rules[i]);
  2872. for (k = 0; rules2[k]; k++)
  2873. mvaddstr(i+k+1, 0, rules2[k]);
  2874. refresh();
  2875. while (!getch())
  2876. ;
  2877. setup();
  2878. }
  2879. nonl();
  2880.  
  2881. while (TRUE)
  2882. {
  2883. display();
  2884. mark_life();
  2885. update();
  2886. }
  2887. }
  2888.  
  2889.  
  2890. die()
  2891. {
  2892. signal(SIGINT, SIG_IGN);
  2893. move(LINES-1, 0);
  2894. refresh();
  2895. endwin();
  2896. exit(0);
  2897. }
  2898.  
  2899.  
  2900. kill_life(ly, lx)
  2901. register int ly, lx;
  2902. {
  2903. register LIFE *lp;
  2904.  
  2905. for (lp = head->next; lp != tail; lp = lp->next)
  2906. if (lp->y == ly && lp->x == lx)
  2907.  
  2908. {
  2909. lp->prev->next = lp->next;
  2910. lp->next->prev = lp->prev;
  2911. free(lp);
  2912. break;
  2913. }
  2914. }
  2915.  
  2916. display()
  2917. {
  2918. int pop = 0;
  2919. static int gen, oldpop, boring;
  2920. char c;
  2921. register LIFE *lp;
  2922. erase();
  2923. for(lp = head->next; lp != tail; lp = lp->next)
  2924. {
  2925. mvaddch(lp->y, lp->x, life);
  2926. pop++;
  2927. }
  2928. if (pop == oldpop)
  2929. boring++;
  2930. else
  2931. {
  2932. oldpop = pop;
  2933. boring = 0;
  2934. }
  2935. move(MAXY+1, 0);
  2936. if (!pop)
  2937. {
  2938. printw("Life ends after %d generations.", gen);
  2939. die();
  2940. }
  2941. printw("generation - %-4d", ++gen);
  2942. printw(" population - %-4d", pop);
  2943. refresh();
  2944. if (boring == boredom)
  2945. {
  2946. mvprintw(MAXY, 0, "Population stable. Abort? ");
  2947. refresh();
  2948. while (!(c = getch()))
  2949. ;
  2950. if (toupper(c) == 'Y')
  2951. die();
  2952. }
  2953. }
  2954. mark_life()
  2955. {
  2956. register k, ty, tx;
  2957. register LIFE *lp;
  2958.  
  2959. for (lp = head->next; lp; lp = lp->next)
  2960. for (k = 0; k < 8; k++)
  2961. {
  2962. ty = lp->y + pos[k].y;
  2963. wrap(ty, MAXY);
  2964. tx = lp->x + pos[k].x;
  2965. wrap(tx, MAXX);
  2966. stdscr->_y[ty][tx]++;
  2967.  
  2968. }
  2969. }
  2970.  
  2971.  
  2972. update()
  2973. {
  2974. register int i, j, c;
  2975. for (i = 0; i <= MAXY; i++)
  2976. for (j = 0; j <= MAXX; j++)
  2977. {
  2978. c = stdscr->_y[i][j];
  2979. if (c >= crowd c >= life && c < lonely)
  2980. kill_life(i, j);
  2981. else if (c == birth)
  2982. newlife(i, j);
  2983. }
  2984. }
  2985.  
  2986. setup()
  2987. {
  2988. int x, y, c, start = 0;
  2989.  
  2990. erase();
  2991. y = MAXY/2;
  2992. x = MAXX/2;
  2993. while (!start)
  2994. {
  2995. move(y, x);
  2996. refresh();
  2997. switch (c = getkey())
  2998. {
  2999. case 'h' :
  3000. case 'H' :
  3001. case ('H' - '@'):
  3002. case KEY_LEFT:
  3003. case KEY_BACKSPACE:
  3004. minwrap(x, MAXX);
  3005. break;
  3006. case 'j' :
  3007. case 'J' :
  3008. case ('J' - '@'):
  3009. case KEY_DOWN:
  3010. maxwrap(y, MAXY);
  3011. break;
  3012. case 'k' :
  3013. case 'K' :
  3014. case ('K' - '@'):
  3015. case KEY_UP:
  3016. minwrap(y, MAXY);
  3017. break;
  3018. case '1' :
  3019. case 'L' :
  3020. case ('L' - '@'):
  3021. case KEY_RIGHT:
  3022. maxwrap(x, MAXX);
  3023. break;
  3024. case ' ' :
  3025. if (inch() == life)
  3026. {
  3027.  
  3028. addch(' ');
  3029. kill_life(y, x);
  3030. }
  3031. else
  3032. {
  3033. addch(life);
  3034. newlife(y, x);
  3035. }
  3036. break;
  3037. case 'q' :
  3038. case 'Q' :
  3039. case ESC :
  3040. ++start;
  3041. break;
  3042. }
  3043. }
  3044. }
  3045.  
  3046. newlife(ny, nx)
  3047. int ny, nx;
  3048. {
  3049. LIFE *new;
  3050.  
  3051. new = (LIFE *)malloc(sizeof(LIFE));
  3052. new->y = ny;
  3053. new->x = nx;
  3054. new->next = head->next;
  3055. new->prev = head;
  3056. head->next->prev = new;
  3057. head->next = new;
  3058. }
  3059.  
  3060.  
  3061. readfn(f)
  3062. char *f;
  3063. {
  3064. FILE *fl;
  3065. int y, x;
  3066.  
  3067. if ((fl = fopen(f, "r")) == NULL)
  3068. errx("usage: life [file (line/col pts)]\n", NULL);
  3069. while (fscanf(fl, "%d%d", &y, &x) != EOF)
  3070. {
  3071. if (y < 0 y > MAXY x < 0 x > MAXX)
  3072. errx("life: invalid data point in %s\n", f);
  3073. mvaddch(y, x, life);
  3074. newlife(y, x);
  3075. }
  3076. fclose(fl);
  3077.  
  3078. }
  3079. errx(m,d)
  3080. char *m, *d;
  3081. {
  3082. fprintf(stderr, m, d);
  3083. endwin();
  3084. exit(0);
  3085. }
  3086.  
  3087.  
  3088.  
  3089.  
  3090.  
  3091.  
  3092.  
  3093.  
  3094.  
  3095.  
  3096.  
  3097.  
  3098.  
  3099.  
  3100.  
  3101.  
  3102.  
  3103.  
  3104.  
  3105.  
  3106.  
  3107.  
  3108.  
  3109.  
  3110.  
  3111.  
  3112.  
  3113.  
  3114.  
  3115.  
  3116.  
  3117.  
  3118.  
  3119.  
  3120.  
  3121.  
  3122.  
  3123.  
  3124.  
  3125.  
  3126.  
  3127.  
  3128.  
  3129.  
  3130.  
  3131.  
  3132.  
  3133.  
  3134.  
  3135.  
  3136.  
  3137.  
  3138.  
  3139.  
  3140.  
  3141.  
  3142.  
  3143.  
  3144.  
  3145.  
  3146.  
  3147.  
  3148.  
  3149.  
  3150.  
  3151. Fitting Curves To Data
  3152.  
  3153.  
  3154. Michael Brannigan
  3155.  
  3156.  
  3157. Micheal Brannigan is President of Information and Graphic System, IGS, 15
  3158. Normandy Court, Atlanta, GA 30324 (404) 231-9582. IGS is involved in
  3159. consulting and writing software in computer graphics, computational
  3160. mathematics, and data base design. He is presently writing a book on computer
  3161. graphics algorithms. He is also the author of The C Math Library EMCL, part of
  3162. which are the routines set out here.
  3163.  
  3164.  
  3165. Fitting curves to data ranks as one of the most fundamental needs in
  3166. engineering, science, and business. Curve fitting is known as regression in
  3167. statistical applications and nearly every statistical package, business
  3168. graphics package, math library, and even spreadsheet software can produce some
  3169. kind of curve from given data. Unfortunately the process and underlying
  3170. computational mathematics is not sufficiently understood even by the software
  3171. firms producing the programs. It is not difficult, for example, to input data
  3172. for a linear regression routine to a well known statistical package (which I
  3173. shall not name) used on micros and mainframes for which the output is
  3174. incorrect.
  3175. Constructing a functional approximation to data (the formal act known as curve
  3176. fitting) involves three steps: choosing a suitable curve, analyzing the
  3177. statistical error in the data, and setting up and solving the required
  3178. equations. Choosing a suitable curve is a mixture of artistic sensibility and
  3179. a knowledge of the data and where it comes from. Analyzing statistical error
  3180. can be something of a guessing game and requires some thought. Setting up and
  3181. solving the equations is computationally the most interesting. It is here that
  3182. many programs fail because they use computationally unstable methods, but more
  3183. of that later.
  3184. The number of methods for data fitting is legion and we suggest some in this
  3185. article. However, we give only one method in full and consider only 2-D data.
  3186. Anyone interested in other specific data fitting techniques may contact the
  3187. author.
  3188.  
  3189.  
  3190. Problem
  3191.  
  3192.  
  3193. Given data points (xi,yi)i=1,...,n we suppose there exists a relation
  3194. yi = F(xi) + ei, i = 1,...,n
  3195. where F(x) is an unknown underlying function and ei represents the unknown
  3196. errors in the measurements yi. The problem is to find a good approximation
  3197. f(x) to F(x). We thus need a function f to work with and some idea, however
  3198. minimal, of the errors.
  3199.  
  3200.  
  3201. How To Choose f
  3202.  
  3203.  
  3204. f will have variable parameters whose correct values (the values that solve
  3205. the approximation problem) are found by solving a system of equations, each
  3206. data point defining one equation. We call the function f linear or non-linear
  3207. if it is linear or non-linear in its parameters.
  3208. Consider some of the general principles involved in choosing a suitable
  3209. function f. We must have more data points than parameters, otherwise f will
  3210. fit the data exactly and we will not model the errors. Unless absolutely
  3211. necessary, don't use a non-linear f; solving systems of non-linear equations
  3212. uniquely is, except for special cases, nearly impossible. In most cases
  3213. polynomials are not a good choice; they are wiggly curves and nearly always
  3214. wiggle in the wrong places.
  3215. The best option in most cases is to use piecewise polynomials. The example we
  3216. give is a piecewise cubic polynomial such that the first derivatives are
  3217. continuous everywhere.
  3218. (You can, of course, use cubic splines if you want second derivatives to be
  3219. continuous, but in most cases the example set out here is superior for a
  3220. general purpose curve fitting routine. If you want the full cubic spline,
  3221. please use the B-spline formulation, no other, otherwise you get unstable
  3222. systems of equations resulting in incorrect solutions. Using the B-spline
  3223. formulation for spline approximation, you need only change the routine
  3224. coeff_cubic() in the program given in this article. The system of equations is
  3225. solved by the same routines.)
  3226. Once f has been chosen and applied to each data point, we obtain a system of
  3227. linear equations to solve, where the number of equations will be greater than
  3228. the number of unknowns. Such a system is called an overdetermined system and
  3229. no exact solution exists -- one that is exactly what we want. However,
  3230. overdetermined systems have an infinite number of inexact (approximate)
  3231. solutions; we will seek an approximation that minimizes some particular error
  3232. measure. (Mathematicians call these error measures "norms". Thus the problem
  3233. of curve fitting becomes an optimization problem.)
  3234. Of the infinite possible norms three should be considered for any curve
  3235. fitting package: the L1-norm, the L2-norm (least squares norm), and the
  3236. minimax (Chebyshev) norm (These norms are defined later in this article.).
  3237. Fortunately good algorithms exist for solving overdetermined systems of linear
  3238. equations in all three norms. For the L1-norm and the minimax norm, you use a
  3239. variation of the simplex method of linear programming; for the L2-norm you use
  3240. a QR decomposition of the matrix in preference to the computationally unstable
  3241. method of solving the normal equations. (We cannot give all the program code
  3242. here as space is limited but for more guidance the reader can contact the
  3243. author.)
  3244. Of many possible combinations the following solution is a good general-purpose
  3245. option.
  3246.  
  3247.  
  3248. Solution
  3249.  
  3250.  
  3251. We have data points (xi,yi)i=1,...,n. Let each xi belong to some interval
  3252. [a,b]. Specify k points Xj j=1,...k, on the X-axis, we call these points
  3253. knots. These knots are such that
  3254. a = X1 < X2 < < Xk = b
  3255. We can now define our function as follows: for each x in the interval
  3256. [Xj,Xj+1] define the cubic polynomial
  3257. y = [(d3 - 3dp2(x) + 2p3(x))Yj
  3258. + (dp(x)q2(x))Yj' + (3d - 2p(x))p2(x)Yj+1
  3259. + dp2(x)q(x)Yj+1']/d3
  3260. where
  3261. d = Xj+1 - Xj
  3262. p(x) = x - Xj
  3263. q(x) = Xj+1 - x
  3264. Thus y is a cubic polynomial with the linear parameters Yj, Yj+1,Yj',Yj+1',
  3265. which are the values and first derivatives at the knots Xj and Xj+1
  3266. respectively.
  3267. For each data point we obtain one linear equation so we can set up n linear
  3268. equations in the 2k unknowns Y1,Y1',..Y, k,Y k'. In matrix form this can be
  3269. written as
  3270. AY = b
  3271. where A is a block diagonal matrix, Y is the vector of unknowns, and b is the
  3272. vector of y values. Because A is block-diagonal, for very large data sets
  3273. optimal use should be made of the structured sparsity.
  3274. With the same knots we could also define cubic B-splines and then fit a cubic
  3275. spline to the data. We would again arrive at an overdetermined system of
  3276. linear equations with a matrix of coefficients having block-diagonal
  3277. structure. In fact the equations we have set out above form a cubic spline
  3278. with each knot Xj a double knot.
  3279.  
  3280.  
  3281. Choosing A Norm
  3282.  
  3283.  
  3284.  
  3285. For each possible solution Y we have errors si i=1,...,n such that
  3286. AY - b = s
  3287. where s is the vector of si values.
  3288. The L1-norm is defined to be
  3289. åabs(si)
  3290. i
  3291. The L2-norm or least squares norm is
  3292. (Ã¥si2)1/2
  3293. i
  3294. And the minimax or Chebyshev norm is
  3295. max(abs(si) :i=1,...,n)
  3296. We solve the overdetermined system of equations by finding that vector Y which
  3297. minimizes one of these norms. The choice of norm depends on the unknown errors
  3298. ei and we hope that the choice of norm will give errors si that will mirror
  3299. these unknown errors. The general rule is: choose the L1-norm if the ei are
  3300. scattered (belong to a long tailed distribution); choose the L2-norm if the ei
  3301. are normally distributed; choose the minimax norm if the ei are very small or
  3302. belong to a uniform distribution. Research has indicated that data sets have
  3303. errors nearer to the L1-norm than the L2-norm. (Errors in data are never
  3304. normally distributed, neither as they are nor in the limit. This assumption of
  3305. normally distributed errors is common in most packages, the user should
  3306. question this assumption very carefully.) So when you don't know how the
  3307. errors are distributed, use the L1-norm. The minimax norm is rarely used for
  3308. fitting curves to experimental data. However, always use the minimax norm if
  3309. you want to fit a function to another function, for example fitting a Fourier
  3310. series to a complicated function where you know the values exactly.
  3311. Whichever norm you choose, the computer solution of the equations is not
  3312. straightforward. You must choose an algorithm that is computationally stable.
  3313. (A computationally unstable algorithm is one that is mathematically correct
  3314. but when fed into a computer, produces wrong answers. For example solving
  3315. linear equations without pivoting, or solving quadratic equations from the
  3316. well-known formula. So get some professional help in choosing the algorithm.)
  3317.  
  3318.  
  3319. Program
  3320.  
  3321.  
  3322. After you have spent some time analyzing your particular data fitting problem,
  3323. decided upon a suitable function to approximate the data, and also decided
  3324. upon the norm to use for the errors in the data, you must program the result.
  3325. Unless your application requires special functions, then the approximating
  3326. function set out above is a good general purpose function. The programming for
  3327. this function or any other has the same form. The system of equations is set
  3328. up with one equation for each data point, and then the system is solved with
  3329. the required norm. For the function described here the programming is just as
  3330. straightforward.
  3331. The main routine is Hermite(), named after the mathematician who defined these
  3332. piecewise polynomials. The routine first gives the user the choice (by setting
  3333. the variable flag) of either setting the k knots lambda[] on input or using
  3334. the routine app_knots() to compute the knots. In most cases the user will
  3335. never just use the routine once but compute a first approximation then alter
  3336. the position of the knots for a second approximation. For a first
  3337. approximation set flag to true and use app_knots() to compute the knots
  3338. automatically. Then look at the result and choose new knots. A more
  3339. sophisticated method automatically chooses the number of knots k and their
  3340. position.
  3341. Once the knots are defined the routine allocates space for the matrix A of
  3342. size nx2k. After making sure all elements of the matrix are zero, the routine
  3343. calls coeff_cubic() to set up the coefficients of the matrix.
  3344. Now the program solves the overdetermined system in the appropriate norm. The
  3345. variable norm is set by the user to indicate which norm to use. (We do not
  3346. give here the three routines that solve the overdetermined system of equations
  3347. as they require lots of space, but the reader can find the algorithms in most
  3348. computational mathematics textbooks.) The routine L1_approx() uses the
  3349. Ll-norm, the routine CH_lsq() uses the least squares norm, and the routine
  3350. Cheby() uses the minimax norm. With the solution from the appropriate routine,
  3351. the function now fits the data.
  3352. Some words on the other routines. First, the routine app_knots() will compute
  3353. k knots lambda[j] so that in each interval (lambda[j], lambda[j+1]) there are
  3354. approximately the same number of x values. This is a good starting point for
  3355. our Hermite approximation and for any spline approximation that needs knots.
  3356. The routine coeff_cubic() is merely a direct translation of the formulae. This
  3357. routine uses interval(), which finds to which knot interval each x value
  3358. belongs. coeff_cubic() also uses the macro SUB() to move around the matrix
  3359. (this is my preferred method for dealing with matrices passed as parameters).
  3360. Finally there is the routine app_cubic(). This routine uses the results from
  3361. Hermite() to compute the piecewise polynomial for any value of x. Thus
  3362. app_cubic() completes the curve fitting problem.
  3363.  
  3364.  
  3365. Example
  3366.  
  3367.  
  3368. An example (using data from actual measurements of the horizontal angular
  3369. displacement of an elbow flexion against time) will show how the pieces fit
  3370. together. There are 142 measured points and these measurements are quite
  3371. accurate (the experimenters knew the kind of instruments they were using--see
  3372. the paper by Pezzack, et al). In this instance a close fit to the data points
  3373. is required. In all the figures the dark circles are the knots and the crosses
  3374. are the data points.
  3375. The solution is in Figure 1. Figure 2 shows the result when the L2-norm is
  3376. used. Figure 3 shows the result when the minimax norm is used. As would be
  3377. expected with such "clean" data, the answers are all quite good, the best
  3378. being Figure 1.
  3379. To illustrate the behavior in the presence of noise, add some significant
  3380. errors to the same data points. Using the same curve approximation method,
  3381. then Figure 4, Figure 5, and Figure 6 show the result when using the L1- norm,
  3382. L2-norm, and minimax norm respectively. As theory suggests, the Ll-norm gives
  3383. definitely superior results. This example is a straightforward application of
  3384. the method set out here -- well, nearly! You may be asking the six thousand
  3385. dollar question, "How do I choose the knots?" The answer is not
  3386. straightforward and contemporary research has different answers.
  3387. As you can see from the figures, the number and position of the knots changes
  3388. for each example. The goal is to choose the number of knots and their position
  3389. so as to give the best fit possible for the norm chosen--easy to say but not
  3390. easy to compute. All the knots in each figure have been chosen according to an
  3391. information theoretic criterion, plus a little experience on the placement of
  3392. knots. The idea behind this method is to attempt to extract the maximum amount
  3393. of information from the data points until only error remains. To do this we
  3394. need a computable value for the amount of information contained in the errors
  3395. si; we suggest using the Akaike Information Criterion. The routine changes the
  3396. number of knots and their position until there is no more information in the
  3397. errors. For those readers who wsh to go further into this problem, see the
  3398. papers by Brannigan for a full mathematical treatment of this method, the
  3399. information theoretic criterion, and an extension to multivariate data.
  3400.  
  3401.  
  3402. Bibliography
  3403.  
  3404.  
  3405. Pezzak, J.C. et al. "An Assessment of Derivatives Determining Techniques Used
  3406. for Motion Analysis," J. Biomechanics, 10 (1977).
  3407. Brannigan, M. "An Adaptive Piecewise Polynomial Curve Fitting Procedure for
  3408. Data Analysis," Commun. Statist. A10(18), (1981).
  3409. Brannigan, M. "Multivariate Data Modelling by Metric Approximants," Comp.
  3410. Stats. & Data Analysis 2, (1985).
  3411. Figure 1
  3412. Figure 2
  3413. Figure 3
  3414. Figure 4
  3415. Figure 5
  3416. Figure 6
  3417.  
  3418. Listing 1 Coeff_Cubic
  3419. void coeff_cubic (a,p,q,x,y, lambda,k)
  3420. /*
  3421. * Set up the equations for the Hermite cubic approximation.
  3422. */
  3423. double *a,*x,*y,*lambda;
  3424. int p,q,k;
  3425. {
  3426. double d,alpha,beta,d3,alpha3;
  3427. int i,j,col;
  3428. for (i=0; i<p; i++)
  3429. {
  3430.  
  3431. j = interval (lambda,x[i],k);
  3432. col = SUB(i,2*(j-1),q);
  3433. d = lambda[j] - lambda[j-1];
  3434. alpha = x[i]-lambda[j-1];
  3435. beta = d-alpha;
  3436. d3 = d*d*d;
  3437. alpha3 = alpha*alpha*alpha;
  3438. *(a+col) = (d3-3.0*d*alpha*alpha+2.0*alpha3)/d3;
  3439. *(a+col+1) = d*alpha*beta*beta/d3;
  3440. *(a+col+2) = (3.0*d-2.0*alpha)*alpha*alpha/d3;
  3441. *(a+col+3) = -d*alpha*alpha*beta/d3;
  3442. }
  3443. }
  3444.  
  3445. int interval (x,v,n)
  3446. /*
  3447. * Given a value v find the interval j such that v is in the interval
  3448. * x[j-1] to x[j], where x is an increasing set of n values.
  3449. */
  3450. double x[],v;
  3451. int n;
  3452. {
  3453. int j = 0, found = 0;
  3454. if (v == x[0]) return(1);
  3455. while (!found && ++j<n)
  3456. found =( v<=x[j] && v> x[j-1]) ? 1:0;
  3457. return(j);
  3458. }
  3459.  
  3460.  
  3461. double app_cubic (x,j,lambda,res)
  3462. /*
  3463. * Given the result res[] from the routine Hermite() find the value
  3464. * of y for the given x value.
  3465. */
  3466. double x,*lambda,*res;
  3467. int j;
  3468. {
  3469.  
  3470. double d,alpha,beta,d3,alpha3,sum,val[4];
  3471. int i, col;
  3472. col = 2*(j-1);
  3473. d = lambda[j] - lambda[j-1];
  3474. alpha = x-lambda[j-i];
  3475. beta = d-alpha;
  3476. d3 = d*d*d;
  3477. alpha3 = alpha*alpha*alpha;
  3478. val[0] = (d3-3.O*d*alpha*alpha+2.0*alpha3)/d3;
  3479. val[1] = d*alpha*beta*beta/d3;
  3480. val[2] = (3.0*d-2.0*alpha)*alpha*alpha/d3;
  3481. val[3] = -d*alpha*alpha*beta/d3;
  3482. for (sum=0.0,i=0; i<4; i++) sum += val[i]*res[col+i];
  3483. return (sum);
  3484. }
  3485.  
  3486.  
  3487. Hermite (Listing 2)
  3488. #define SUB(i,j,k) (i)*(k)+(j)
  3489.  
  3490.  
  3491.  
  3492. double Hermite (x,y,n,norm,lambda,k,flag,res,err)
  3493. /*
  3494. * Given n data points (x[],y[]) find the Hermite cubic approximation
  3495. * to this data using the k nots lambda[]. If flag = true then find the
  3496. * knots from the routine app_knots() otherwise lambda[] is set by the
  3497. * user. The 2k result is returned in res[] and the error at each point
  3498. * is returned in err[].The overdetermined system of equations is
  3499. * solved with respect to the value of norm, uses L1-norm if norm = 1,
  3500. * uses the L2-norm if norm = 2, and uses the minimax norm if norm = 3.
  3501. * The return value z is the size of the resultant norm.
  3502. */
  3503. double *x,*y,*lambda,*res,*err;
  3504. int n,norm,k,flag;
  3505. {
  3506. double *a,z;
  3507. int i,j,l,kk,m,m2;
  3508. /*
  3509. * Find whether the knots are to be computed.
  3510. */
  3511. if (flag) app_knots (x,n,lambda,k);
  3512. /*
  3513. * Now form the system of equations one equation per data point.
  3514. */
  3515. m2 = n*2*k;
  3516. /*
  3517. * Allocate space for the matrix.
  3518. */
  3519. a = (double*)calloc(m2,sizeof(double));
  3520. if (a==0) printf ("\n NO DYNAMIC SPACE AVAILABLE");
  3521. else
  3522. {
  3523. for (i=0; i<m2; i++) *(a+i) = 0.0;
  3524. coeff_cubic (a,n,m,x,y,lambda,k); /* Set up the matrix. */
  3525. switch (norm)
  3526. {
  3527. case 1:
  3528. z = Ll_approx(a,n,m,m,y,res,err]; /* L1-norm solution */
  3529. break;
  3530. case 2:
  3531. z = CH_lsq {a,n,m,m,y,res,err); /* L2-norm solution */
  3532. break;
  3533. default:
  3534. z = Cheby (a,n,m,m,y,res,err); /* Minimax norm solution */
  3535. break;
  3536. }
  3537. free (a);
  3538. }
  3539. return (z);
  3540. }
  3541.  
  3542. void app_knot0s (x,n,lambda,k)
  3543. /*
  3544. * Given n x[] values compute k knots lambda[] such that the
  3545. * distribution of points in each interval is nearly the same.
  3546. */
  3547. double *x,*lambda;
  3548. int n,k;
  3549. {
  3550.  
  3551. int i,j,s,t;
  3552. lambda[0] = x[0];
  3553. lambda[k-1] = x[n-1];
  3554. if (k>2)
  3555. {
  3556. i = n/(k-1); j = (n-(i*(k-3)))/2;
  3557. lambda = x[j];
  3558. if (k>3)
  3559. {
  3560. s = j;
  3561. for (t=2; t<k-1; t++)
  3562. {s+=i;
  3563. lambda[t] = x[s];
  3564. }
  3565. }
  3566. }
  3567. }
  3568.  
  3569.  
  3570.  
  3571.  
  3572.  
  3573.  
  3574.  
  3575.  
  3576.  
  3577.  
  3578.  
  3579.  
  3580.  
  3581.  
  3582.  
  3583.  
  3584.  
  3585.  
  3586.  
  3587.  
  3588.  
  3589.  
  3590.  
  3591.  
  3592.  
  3593.  
  3594.  
  3595.  
  3596.  
  3597.  
  3598.  
  3599.  
  3600.  
  3601.  
  3602.  
  3603.  
  3604.  
  3605.  
  3606.  
  3607.  
  3608.  
  3609.  
  3610.  
  3611.  
  3612.  
  3613.  
  3614. A Simple Application Environment
  3615.  
  3616.  
  3617. Mark A. Johnson
  3618.  
  3619.  
  3620. Mark Johnson has been designing software for a major R&D corporation since
  3621. 1976. He received a BSCS from the University of Pittsburgh and his MSCS from
  3622. Carnegie-Mellon. His current computer interests include languages, programming
  3623. for children, business applications, and computer-generated music. Mark is
  3624. continuing to develop other DCUWCU applications.
  3625.  
  3626.  
  3627. Having used a mouse in user interfaces since 1981, I believe it to be most
  3628. convenient way to inform a computer program what you want it to do. I wanted
  3629. to use a mouse in a number of PC programs and so looked into a few application
  3630. environments. Microsoft Windows and Digital Research's GEM disappointed me,
  3631. due to the complexity that had to be mastered. The resource construction sets,
  3632. complex window management, and other overheads needed to write a simple
  3633. application led me to write my own simple application environment based on
  3634. Turbo C graphics routines and a public domain mouse interface.
  3635. My goal was to build an easy-to-use environment that provides a mouse-driven
  3636. cursor, stacked pop-up menus, and forms that contain editable fields and a
  3637. variety of selectable buttons. The environment would keep track of what the
  3638. user was doing, inform the application as needed, and clean up after itself.
  3639. An additional goal was to facilitate porting the environment to other machines
  3640. that have a mouse, bitmap graphics, console I/O, and a simple timer. I have
  3641. the same DCUWCU environment on my PC compatible and Atari ST, allowing me to
  3642. easily move applications between systems.
  3643.  
  3644.  
  3645. Operation
  3646.  
  3647.  
  3648. A typical application begins with a blank screen (or suitable greeting)
  3649. showing an arrow-shaped cursor controlled by the mouse. Pressing the right
  3650. mouse button displays a set of stacked pop-up menus. While holding the right
  3651. mouse button down, the user selects an item (or another menu) from the
  3652. front-most menu and then releases the button. If a menu item was selected,
  3653. then the application acts on that selection. If another menu was selected, it
  3654. is brought to the front of the menu stack, ready for another round of menu
  3655. item selection. Pressing the left mouse button or a keyboard key usually
  3656. causes an application-specific action, often resulting in a form appearing on
  3657. the screen that the user must fill out. When processing the form, all mouse
  3658. and keyboard events are handled by the environment. Keyboard input is directed
  3659. to the current editable field, denoted by the special input cursor. A TAB
  3660. moves the input cursor to the next editable field. An ESC (cancel) or ENTER
  3661. (accept) terminates form processing, returning data and control back to the
  3662. application. Some forms may contain small text labels, called form buttons,
  3663. that are selected (or de-selected) by moving the cursor over them and pressing
  3664. the left mouse button. There are three types of buttons: plain, radio, and
  3665. exit. A plain button is a simple on/off switch. A radio button is a
  3666. one-of-many switch, much like the buttons on a car radio. An exit button is
  3667. like the plain button, but causes form processing to end.
  3668. The application environment works equally well when no mouse is present by
  3669. using the cursor keys to simulate mouse motion and the function keys F1 and F2
  3670. to simulate the left and right mouse buttons. Pressing F2 once simulates
  3671. pressing the right mouse button; another press of F2 simulates its release. A
  3672. single press of the F1 button simulates the left mouse button.
  3673.  
  3674.  
  3675. Application Interface
  3676.  
  3677.  
  3678. The interface between application and environment was made as simple as
  3679. possible: strings are used to define forms and menus, pointers to variables
  3680. are used to store values collected by forms, and calls to functions inform the
  3681. application of user events, such as menu selection or mouse button clicks.
  3682. The application environment follows (and is named after) what is called "The
  3683. Hollywood Principle," (or "don't call us, we'll call you"). An application
  3684. developer supplies four critical routines, called when the application
  3685. environment detects various user interface events.
  3686.  
  3687.  
  3688. start(argc, argv, envp) int argc; char **argv, **envp;
  3689.  
  3690.  
  3691. This is the initialization routine called immediately after initializing the
  3692. graphics interface but before the environment is completely started. It is
  3693. passed the same arguments normally passed to a C main() routine. The start()
  3694. routine usually initializes the application and creates the menu stack using
  3695. repeated calls to add_menu().
  3696.  
  3697.  
  3698. menu(m, i)
  3699.  
  3700.  
  3701. The menu() routine is called whenever a menu selection is made. The
  3702. application environment supports a stack of pop-up menus. Any number of menus
  3703. can be supported, although only two or three are usually active at any one
  3704. time in order to minimize interface complexity (see menu_state() below). The m
  3705. argument identifies which menu was selected. When the menu is first declared
  3706. (see add_menu()), the application provides a value that identifies the menu.
  3707. This same identifer value is passed back to the application when a menu is
  3708. selected. The i argument specifies which menu item was selected, a value of 1
  3709. meaning the first item, etc.
  3710.  
  3711.  
  3712. button(b, x, y)
  3713.  
  3714.  
  3715. The button() routine is called when a mouse button is pressed. The right mouse
  3716. button is reserved for menu manipulation, all others are passed to the
  3717. application. The b argument is the button number (usually 1) and the x and y
  3718. arguments are the mouse coordinates when the button was pressed.
  3719.  
  3720.  
  3721. keyboard(key)
  3722.  
  3723.  
  3724. The keyboard() routine is called whenever a console key is struck. The
  3725. character typed by the user is contained in the single argument.
  3726.  
  3727.  
  3728. timer(t)
  3729.  
  3730.  
  3731. The (optional) timer() routine is called whenever an application-requested
  3732. timer expires. When the timer is requested, a value identifying the timer is
  3733. passed to the application environment. The same identifer value is passed back
  3734. to the application in the t argument when the timer expires.
  3735.  
  3736.  
  3737.  
  3738. Environment Interface
  3739.  
  3740.  
  3741. The application environment provides some basic routines that an application
  3742. can call for control and service.
  3743.  
  3744.  
  3745. finish()
  3746.  
  3747.  
  3748. The finish() routine is called whenever the application is done and the
  3749. program must exit.
  3750.  
  3751.  
  3752. add_menu(m, mdef) char *mdef;
  3753.  
  3754.  
  3755. The add_menu() routine adds a menu to the current set of pop-up menus
  3756. maintained by the environment. An application typically initializes all its
  3757. menus from the start() routine. The m argument is remembered by the
  3758. environment and passed back to the application when a menu selection is made.
  3759. The mdef argument is a string that defines the menu. For example,
  3760. add_menu(1, "Main:AboutHelpQuit")
  3761. defines a menu identified as menu 1, titled Main, and with three items: About,
  3762. Help, and Quit.
  3763.  
  3764.  
  3765. menu_state(m, on)
  3766.  
  3767.  
  3768. The menu_state() routine allows the application to activate or de-activate a
  3769. particular menu. The m argument refers to the menu defined with a previous
  3770. add_menu() call. The on argument should be set to 1 to activate or 0 to
  3771. deactivate the menu.
  3772.  
  3773.  
  3774. menu_item(m, i, str) char *str;
  3775.  
  3776.  
  3777. The menu_item() routine is used to change the name of a particular menu item.
  3778. For example, suppose a drawing program can turn a grid on and off. The
  3779. application might have a menu item called Grid when no grid is shown and
  3780. change it to No Grid using menu_item() when the grid is shown.
  3781.  
  3782.  
  3783. mouse_state(on)
  3784.  
  3785.  
  3786. The mouse_state() routine will activate or deactivate the mouse-driven cursor.
  3787. The on variable should be set to 1 to show the mouse and 0 to hide it.
  3788.  
  3789.  
  3790. mouse_shape(type, bits, hotx, hoty) char *bits;
  3791.  
  3792.  
  3793. The application can control the cursor's shape with mouse_shape. There are two
  3794. built-in forms: arrow and cross. A type value of 0 and 1 specify arrow and
  3795. cross, respectively. A type value of 3 allows the user to specify a custom
  3796. designed mouse cursor. The bits argument is a pointer to an eight-byte
  3797. character array containing the mouse bitmap (8 x 8 bits). The hotx and hoty
  3798. arguments indicate which bit in the bitmap is considered the hotspot of the
  3799. cursor. For example, the cross form has a hotspot of hotx=hoty=3, which is the
  3800. center of the 8 x 8 bitmap.
  3801.  
  3802.  
  3803. add_timer(t, wait) long wait;
  3804.  
  3805.  
  3806. Many applications, especially games, require some sense of the passage of
  3807. time. Using add_timer(), the application can arrange for timer() to be called
  3808. after some time has elapsed. The application's timer() routine can do such
  3809. things as blank the screen if no activity has taken place for many minutes or
  3810. move sprites around the screen after a few tenths of a second. The t argument
  3811. identifies a particular timer and is passed back to the application when the
  3812. timer expires. The wait argument specifies the needed delay in milliseconds
  3813. (e.g. wait=1000L is a delay of one second).
  3814.  
  3815.  
  3816. form(def, argptr1, ...) char *def;
  3817.  
  3818.  
  3819. form() displays a form on the screen, collects data from the user, and
  3820. deposits it in the variables pointed to by the argptr parameters. This routine
  3821. is somewhat similar to scanf(). The form definition string defines a number of
  3822. fields. For example,
  3823. " Name: %15s Number: %5d %[malefemale] %[over 55] %{ok}"
  3824. This form definition would result in the following being displayed in the
  3825. middle of the screen surrounded by a rectangle.
  3826. Name:____________________LINEEND____
  3827. Number:_____
  3828.  
  3829. [malefemale] [over 55]
  3830. {ok}
  3831. Most of the text in the definition string is used as titles. A signifies the
  3832. beginning of a new line in the form. A data field begins with a % and is
  3833. associated with a particular variable. There are five types of data fields.
  3834. (For the examples that follow, assume the following declarations occur before
  3835. the call to form(): char c, buf[11]; int x;). (See Table 1.) If a character
  3836. pointer fdef points to the string described in Table 1, the following code
  3837. fragment uses form() correctly.
  3838. char name[16], m_or_f = 0, over_55 = 1, ok = 0;
  3839. int number;
  3840. if (form(fdef,
  3841. name, &number,
  3842. &m_or_f,
  3843. &over_55,
  3844. &ok)) {
  3845. /* do stuff with name, number, etc. */
  3846. }
  3847. After filling out the form, if the user selects the {ok} button or hits ENTER,
  3848. form() returns a non-zero value. The data values collected by the form and
  3849. stored in name, number, m_or_f, and over_55 are processed further by the if
  3850. statement. If the user strikes the ESC key while filling out the form, form()
  3851. returns zero and the processing doesn't take place.
  3852.  
  3853.  
  3854. An Example
  3855.  
  3856.  
  3857. Listing 1 is a simple drawing program that illustrates how to build a DCUWCU
  3858. application. The code for this example and the complete source for the DCUWCU
  3859. have been added to the CUG Library. See the New Releases column by Kenji Hino
  3860. for more details.
  3861.  
  3862.  
  3863. Conclusion
  3864.  
  3865.  
  3866. I have used the "Hollywood Principle" design model for a number of projects
  3867. and have found it to shorten development time and result in a robust
  3868. application. The mouse is an effective user interface device and, when coupled
  3869. with pop-up windows and forms, provides clean, uncluttered operation.
  3870. I would like to acknowledge the designers of the many mouse-based user
  3871. interfaces I have used in the past, such as the Apple Macintosh, Microsoft
  3872. Windows, DR/Atari GEM, but most significantly the Xerox Mesa Development
  3873. System, for the inspiration to build this simple application environment.
  3874. Table 1
  3875. Field Example Argument Values
  3876. -------------------------------------------
  3877. text %10s &buf[0] string
  3878. number %5d &x integer
  3879. button %[abc] &c 0 or 1
  3880. radio %[abc:def:ghi] &c 0, 1, ...
  3881. exit %{ok} &c 0 or 1
  3882.  
  3883. Listing 1
  3884. /*
  3885. * this is a very simple drawing program that
  3886. * illustrates how to build a DCUWCU application
  3887. *
  3888. * Copyright 1989 Mark A. Johnson
  3889. */
  3890.  
  3891. #include <stdio.h>
  3892. #include <graphics.h>
  3893.  
  3894. #define M_POINTER 0 /* mouse shapes */
  3895. #define M_CROSS 1
  3896.  
  3897. #define ON 1
  3898. #define OFF 0
  3899.  
  3900. #define MAX_OBJECT 100
  3901. #define ESC 27
  3902.  
  3903. #define BOX 'b' /* object types we support */
  3904. #define ELLIPSE 'e'
  3905. #define LINE 'l'
  3906. #define TEXT 't'
  3907.  
  3908.  
  3909. #define M_MAIN 1 /* handles for the menus */
  3910. #define M_FILE 2
  3911. #define M_OBJ 3
  3912. #define M_ACT 4
  3913.  
  3914. #define A_COPY 1 /* action requests for button() */
  3915. #define A_MOVE 2
  3916. #define A_EDIT 3
  3917.  
  3918. #define min(a,b) ((a) < (b) ? (a) : (b))
  3919. #define max(a,b) ((a) > (b) ? (a) : (b))
  3920.  
  3921. typedef struct { int type, l, t, r, b; char select, *data; } Object;
  3922.  
  3923. Object objects[MAX_OBJECT]; /* the table of objects defined so far */
  3924. int last_object; /* the end of the object table */
  3925.  
  3926. int map[] = { /* maps a M_OBJ menu item to an object */
  3927. 0, BOX, ELLIPSE, LINE, TEXT };
  3928.  
  3929. char *about = /* form used on the M_MAIN About item */
  3930. " Draw This! byMark A. Johnson %{continue}";
  3931.  
  3932. char *help = /* help message for wrong keyboard input */
  3933. "quit refresh : box line ellipse text : delete copy move edit";
  3934.  
  3935. char filename[20]; /* save the filename we're working with */
  3936. char text[100]; /* extra buffer for text i/o */
  3937.  
  3938. int actn_obj = 0; /* flag for button(), some action req */
  3939. int make_obj = 0; /* flag for button(), need to create */
  3940. int slct_cnt = 0; /* count of selected objects */
  3941. int first; /* helps make_object collect points */
  3942. int grid = 0; /* grid displayed, snap coords */
  3943.  
  3944. extern int Maxx, Maxy, MaxColor;
  3945.  
  3946. /* start routine, called by the application driver, gets things going */
  3947.  
  3948. start(argc, argv) char **argv; {
  3949. add_menu(M_MAIN, "Main:AboutQuitRefreshGrid");
  3950. add_menu(M_FILE, "File:ReadWriteSavePrint");
  3951. add_menu(M_OBJ, "Objects:BoxEllipseLineText");
  3952. add_menu(M_ACT, "Actions:DeleteCopyMoveEdit");
  3953. menu_state[M_ACT, 0);
  3954. if (argc> 1) {
  3955. strcpy(filename, argv[1]);
  3956. read_objects();
  3957. }
  3958. }
  3959.  
  3960. /* no timers in this application , but DCUWCU needs an entry anyway */
  3961.  
  3962. timer() {}
  3963.  
  3964. /* button routine called every time button 1 is depressed */
  3965.  
  3966. button(b, x, y) {
  3967.  
  3968. if (make_obj) { /* need points to make an object */
  3969. make_object(x, y);
  3970. }
  3971. else if (actn_obj) { /* got a point for a copy or move */
  3972. action_object(x, y);
  3973. }
  3974. else { /* do a selection */
  3975. select_object(in_object(x, y));
  3976. }
  3977. check_menu();
  3978. }
  3979.  
  3980. /* menu routine called every time a menu item is selected */
  3981.  
  3982. menu(m, i) {
  3983. char junk = 0, on;
  3984. switch (m) {
  3985. case M_MAIN: /* main menu */
  3986. switch (i) {
  3987. case 1: form(about, &junk); break;
  3988. case 2: quit(); break;
  3989. case 3: refresh(); break;
  3990. case 4: do_grid(); break;
  3991. }
  3992. break;
  3993. case M_FILE: /* file menu */
  3994. if (i < 3 && !get_name())
  3995. break;
  3996. switch (i) {
  3997. case 1: read_objects(); break;
  3998. case 2: case 3: write_objects(); break;
  3999. case 4: print(); break;
  4000. }
  4001. break;
  4002. case M_OBJ: /* objects] */
  4003. start_make(map[i]);
  4004. break;
  4005. case M_ACT: /* actions */
  4006. switch (i) {
  4007. case 1: kill_object(); break;
  4008. case 2: start_actn(A_COPY); break;
  4009. case 4: start_actn(A_MOVE); break;
  4010. case 4: start_edit(); break;
  4011. }
  4012. break;
  4013. }
  4014. }
  4015.  
  4016. /* routine called everytime a key is struck */
  4017.  
  4018. keyboard(c) {
  4019. switch (c) {
  4020. case 'p': print(); break;
  4021. case 'g': do_grid(); break;
  4022. case 'r': refresh(); break;
  4023. case 'q': quit(); break;
  4024. case 'b': start_make(BOX); break;
  4025. case 't': start_make(TEXT); break;
  4026. case 'l': start_make(LINE); break;
  4027.  
  4028. case 'm': start_actn(A_MOVE); break;
  4029. case 'c': start_actn(A_COPY); break;
  4030. case 'd': kill_object(); break;
  4031. case 'e':
  4032. if (slct_cnt)
  4033. start_edit();
  4034. else start_make(ELLIPSE);
  4035. break;
  4036. default: msg(help);
  4037. }
  4038. }
  4039.  
  4040. /* time to go, see if they really want to */
  4041.  
  4042. quit() {
  4043. char yes = 0, no = 0;
  4044. char *f_exit = "Are you sure? %{yes} %{no}";
  4045. if (form(f_exit, &yes, &no) && no == 0)
  4046. finish();
  4047. }
  4048.  
  4049. /*
  4050. * miscellaneous support routines
  4051. */
  4052.  
  4053. /* reset the current grid size */
  4054.  
  4055. do_grid() {
  4056. char gridval, ok = 0, nok = 0, x;
  4057. switch (grid) {
  4058. case 8: gridval = 1; break;
  4059. case 16: gridval = 2; break;
  4060. default: gridval = 0; break;
  4061. }
  4062. x = form("Change Grid Size %[none:8:16]%{ok} %{cancel}",
  4063. &gridval, &ok, &nok);
  4064. if (x == 0 nok) return;
  4065. grid = gridval * 8;
  4066. refresh();
  4067. }
  4068.  
  4069. /* print the current screen somewhere, Epson-compatible graphics mode */
  4070.  
  4071. print() {
  4072. static char grhd[] = { ESC, 'L', 0, 0 }; /* 960 bit graphics */
  4073. static char grlf[] = { ESC, 'J', 24, '\r' }; /* line feed */
  4074. static char prbuf[960];
  4075. int x, y, i, b, n, any, pixel, max;
  4076. max = min(Maxx, 960);
  4077. grhd[2] = max;
  4078. grhd[3] = max >> 8;
  4079. mouse_state(OFF);
  4080. b = 0x80;
  4081. any = 0;
  4082. for (y = 0; y < Maxy; y++) {
  4083. for (x = 0; x < max; x++) {
  4084. if (getpixel(x, y)) {
  4085. any = 1;
  4086. prbuf[x] = b;
  4087.  
  4088. }
  4089. }
  4090. b >>= 1;
  4091. if (b == 0) { /* out it goes */
  4092. if (any) {
  4093. prn(grhd, 4);
  4094. prn(prbuf, max);
  4095. }
  4096. prn(grlf, 4);
  4097. b = 0x80;
  4098. any = 0;
  4099. for (x = 0; x < max; x++)
  4100. prbuf[x] = 0;
  4101. }
  4102. }
  4103. mouse_state(ON);
  4104. }
  4105.  
  4106. /* print the n bytes out the printer port */
  4107.  
  4108. prn(s, n) char *s; { while (n-) biosprint(0, *s++, 0); }
  4109.  
  4110. /* select or de--select an object */
  4111.  
  4112. select_object(obj) {
  4113. int i;
  4114. Object *o;
  4115.  
  4116. if (obj == --1) { /* de--select all */
  4117. for (i = 0; i < last_object; i++) {
  4118. o = &objects[i];
  4119. if (o-->select) {
  4120. o-->select = 0;
  4121. highlight(o, 0);
  4122. }
  4123. }
  4124. slct_cnt = 0;
  4125. }
  4126. else {
  4127. o = &objects[obj];
  4128. o-->select = !o-->select;
  4129. highlight(o, o-->select);
  4130. slct_cnt += o-->select ? 1 : --1;
  4131. }
  4132. }
  4133.  
  4134. /* get a filename from the user, return 0 if abort */
  4135.  
  4136. get_name() {
  4137. return form("Path: %20s", filename);
  4138. }
  4139.  
  4140. /* based on current select state, set the top--most menu */
  4141.  
  4142. check_menu() {
  4143. menu_state(M_ACT, slct_cnt > 0);
  4144. menu_state(M_OBJ, slct_cnt <= 0);
  4145. }
  4146.  
  4147.  
  4148. /* start to make an object by collecting points */
  4149.  
  4150. start_make(tape) {
  4151. char *s;
  4152. switch (make_obj = type) {
  4153. case BOX: s = "box: top left corner..."; break;
  4154. case ELLIPSE: s = "ellipse: top left corner..."; break;
  4155. case LINE: s = "line: one end..."; break;
  4156. case TEXT: s = "text: starting..."; break;
  4157. }
  4158. msg(s);
  4159. mouse_shape(M_CROSS);
  4160. first = 1;
  4161. }
  4162.  
  4163. /* if enough points have been collected, make the object */
  4164. make_object(x, y) {
  4165. static int fx, fy;
  4166.  
  4167. if (grid) snap(&x, &y);
  4168.  
  4169. switch (make_obj) {
  4170. case TEXT:
  4171. *text = 0;
  4172. form("text: %20s", text);
  4173. add_object(TEXT, x, y, x + strlen(text)*8, y+8, text);
  4174. make_obj = 0;
  4175. mouse_shape(M_POINTER);
  4176. msg("");
  4177. break;
  4178. default:
  4179. if (first) {
  4180. fx = x;
  4181. fy = y;
  4182. first = 0;
  4183. line(x--3, y, x+3, y);
  4184. line(x, y--3, x, y+3);
  4185. if (make_obj == LINE)
  4186. msg("other end...");
  4187. else msg("bottom right corner...");
  4188. }
  4189. else {
  4190. add_object(make_obj, fx, fy, x, y, 0L);
  4191. msg("");
  4192. make_obj = 0;
  4193. mouse_shape(M_POINTER);
  4194. }
  4195. }
  4196. }
  4197.  
  4198. /* snap the coordinates to the nearest grid point */
  4199.  
  4200. snap(xp, yp) int *xp, *yp; {
  4201. int g2 = grid/2, g4 = grid/4, x = *xp, y = *yp;
  4202. x = ((x + g2) / grid) * grid;
  4203. y = ((y + g4) / g2) * g2;
  4204. msg("x %d-->%d y %d-->%d", *xp, x, *yp, y);
  4205. *xp = x;
  4206. *yp = y;
  4207.  
  4208. }
  4209.  
  4210. /* move, copy, or edit a figure */
  4211.  
  4212. action_object(x, y) {
  4213. int i, dx, dy;
  4214. Object *o;
  4215.  
  4216. if (grid) snap(&x, &y);
  4217.  
  4218. /* find reference point and compute distance moved */
  4219. dx = dy = (actn_obj == A_EDIT ? 0 : 10000);
  4220. for (i = 0; i < last_object; i++) {
  4221. o = &objects[i];
  4222. if (o-->select) {
  4223. if (actn_obj == A_EDIT) {
  4224. dx = max(o-->r, dx);
  4225. dy = max(o-->b, dy);
  4226. }
  4227. else {
  4228. dx = min(o-->l, dx);
  4229. dy = min(o-->t, dy);
  4230. }
  4231. }
  4232. }
  4233. dx = x -- dx;
  4234. dy = y -- dy;
  4235.  
  4236. /* do it to all selected items, de-selecting as you go */
  4237. for (i = 0; i < last_object; i++) {
  4238. o = &objects[i];
  4239. if (o-->select) {
  4240. o-->select = 0;
  4241. highlight(o, 0);
  4242. switch (actn_obj) {
  4243. case A_COPY:
  4244. highlight(o, 0);
  4245. add_object(o-->type,
  4246. o-->l + dx, o-->t + dy,
  4247. o-->r + dx, o-->b + dy,
  4248. o-->data);
  4249. break;
  4250. case A_MOVE:
  4251. draw_object(o, 0);
  4252. o-->l += dx;
  4253. o-->t += dy;
  4254. o-->r += dx;
  4255. o-->b += dy;
  4256. draw_object(o, 1);
  4257. break;
  4258. case A_EDIT:
  4259. draw_object(o, 0);
  4260. set_coords(o,
  4261. o-->l, 0-->t,
  4262. o-->r + dx, o-->b + dy);
  4263. draw_object(o, 1);
  4264. break;
  4265. }
  4266. }
  4267.  
  4268. }
  4269.  
  4270. /* deselect all and reset the mouse */
  4271. actn_obj = 0;
  4272. slct_cnt = 0;
  4273. mouse_shape(M_POINTER);
  4274. msg("");
  4275. check_menu();
  4276. }
  4277.  
  4278. /* read objects from a file */
  4279.  
  4280. read_objects() {
  4281. int type, t, l, r, b;
  4282. FILE *f = fopen(filename, "r");
  4283. if (f != NULL) {
  4284. last_object = 0;
  4285. while (fgets(text, 100, f)) {
  4286. sscanf(text, "%c %d %d %d %d '%[^']\n",
  4287. &type, &l, &t, &r, &b, text);
  4288. add_object(type, l, t, r, b, text);
  4289. }
  4290. fclose(f);
  4291. msg("%d objects loaded", last_object);
  4292. }
  4293. else msg("can't open '%s'", filename);
  4294. }
  4295.  
  4296. /* write objects to a file */
  4297.  
  4298. write_objects() {
  4299. int i;
  4300. Object *o;
  4301. FILE *f;
  4302. if (*filename == 0 && !get_name())
  4303. return;
  4304.  
  4305. if ((f = fopen(filename, "w")) != NULL) {
  4306. for (i = 0; i < last_object; i++) {
  4307. o = &objects[i];
  4308. fprintf(f, "%c %d %d %d %d '%s'\n",
  4309. o-->type, o-->1, o-->t, o-->r, o-->b,
  4310. o-->type == TEXT ? o->data : "");
  4311. }
  4312. fclose(f);
  4313. }
  4314. else msg("can't write '%s'", filename);
  4315. }
  4316.  
  4317. /* save the given string in malloc'ed memory */
  4318.  
  4319. char *
  4320. strsave(s) char *s; {
  4321. char *malloc();
  4322. char *r = malloc(strlen(s)+1);
  4323. if (r) strcpy(r, s);
  4324. else msg("out of memory!!!");
  4325. return r;
  4326. }
  4327.  
  4328.  
  4329. /* re--display all the objects on the screen */
  4330.  
  4331. refresh() {
  4332. int i, x, y, gy;
  4333.  
  4334. Object *o;
  4335. clearviewport();
  4336. setcolor(MaxColor);
  4337. if (grid) {
  4338. gy = grid/2;
  4339. for (x = grid; x < Maxx; x += grid)
  4340. for (y = gy; y <Maxy; y += gy)
  4341. putpixel(x, y, 1);
  4342. }
  4343. for (i = 0; i < last_object; i++) {
  4344. o = &objects[i];
  4345. draw_object(o, 1);
  4346. if (o->select) highlight(o, 1);
  4347. }
  4348. }
  4349.  
  4350. /* (de)highlight the current selected item */
  4351.  
  4352. highlight(o, color] object *o; {
  4353. setcolor(color);
  4354. rectangle(o-->l--2, o-->t--2, o-->l+2, o-->t+2);
  4355. rectangle(o-->r--2, o-->b--2, o-->r+2, o-->b+2);
  4356. }
  4357.  
  4358. /* give the user some feedback */
  4359.  
  4360. msg(fmt, a, b, c, d) char *fmt; {
  4361. static int lastback = 0;
  4362. setfillstyle(EMPTY_FILL, 0);
  4363. bar(0, 0, lastback, 8);
  4364. sprintf(text, fmt, a, b, c, d);
  4365. setcolor(MaxColor);
  4366. outtextxy(0, 0, text);
  4367. lastback = strlen(text) * 8;
  4368. }
  4369.  
  4370. /*
  4371. * object handling
  4372. */
  4373.  
  4374. /* see if x, y are in an object, begin looking at start + 1 */
  4375.  
  4376. in_object(x, y) {
  4377. static int last = 0;
  4378. int l, r, t, b;
  4379. Object *o;
  4380. int i = last+1, n = last_object;
  4381. while (n-) {
  4382. if (i >= last_object) i = 0;
  4383. o = &objects[i];
  4384. l = min(o-->l, o-->r);
  4385. r = max(o-->l, o-->r);
  4386. t = min(o-->t, o-->b);
  4387.  
  4388. b = max(o-->t, o-->b);
  4389. if (x >= l && x <= r && y >= t && y <= b)
  4390. return (last = i);
  4391. i++;
  4392. }
  4393. return (last = --1);
  4394. }
  4395.  
  4396. /* add an object to the object table */
  4397.  
  4398. add_object(type, l, t, r, b, data) char *data; {
  4399. Object *o = &objects[last_object++];
  4400. char *s;
  4401. o-->type = type;
  4402. set_coords(o, l, t, r, b);
  4403. o-->select = 0;
  4404. if (type == TEXT)
  4405. o-->data = strsave(data);
  4406. draw_object(o, 1);
  4407. }
  4408.  
  4409. /* set the coordinates properly */
  4410.  
  4411. set_coords(o, l, t, r, b) Object *o; {
  4412. if (o-->type == LINE) { /* no fixup on these */
  4413. o-->l = l;
  4414. o-->t = t;
  4415. o-->r = r;
  4416. o-->b = b;
  4417. }
  4418. else {
  4419. o-->l = min(l, r];
  4420. o-->t = min(t, b);
  4421. o-->r = max(l, r);
  4422. o-->b = max(t, b);
  4423. }
  4424. }
  4425.  
  4426. /* draw an object on the screen */
  4427.  
  4428. draw_object(o, color) Object *o; {
  4429. int x, y, xrad, yrad;
  4430. setcolor(color);
  4431. switch (o->type) {
  4432. case TEXT:
  4433. x = strlen(o->data) * 8;
  4434. setfillstyle(EMPTY_FILL, 0);
  4435. bar(o-->l, o-->t, o-->l + x, o-->t + 8);
  4436. outtextxy(o-->1, o-->t, o-->data);
  4437. break;
  4438. case BOX:
  4439. rectangle(o-->l, o-->t, o-->r, o-->b);
  4440. break;
  4441. case LINE:
  4442. line(o-->l, o-->t, o-->r, o-->b);
  4443. break;
  4444. case ELLIPSE:
  4445. x = o-->l + (o-->r -- o-->l)/2;
  4446. y = o-->t + (o-->b -- o-->t)/2;
  4447.  
  4448. xrad = o-->r -- x;
  4449. yrad = o-->b -- y;
  4450. ellipse(x, y, 0, 360, xrad, yrad);
  4451. break;
  4452. }
  4453. }
  4454.  
  4455. /* delete an object */
  4456.  
  4457. kill_object() {
  4458. int i, j;
  4459. Object *o;
  4460. for (i = j = 0; i < last_object; i++) {
  4461. o = &objects[i];
  4462. if (o-->select) {
  4463. highlight(o, 0);
  4464. draw_object(o, 0);
  4465. o-->select = 0;
  4466. }
  4467. else {
  4468. if (i > j)
  4469. objects[j++] = objects[i];
  4470. else j++;
  4471. }
  4472. }
  4473. last_object = j;
  4474. slct_cnt = 0;
  4475. check_menu();
  4476. }
  4477.  
  4478. /* start an edit on the selected objects */
  4479.  
  4480. start_edit() {
  4481. int i;
  4482. Object *o;
  4483. /* edit the text objects now */
  4484. for (i = 0; i < last_object; i++) {
  4485. o = &objects[i];
  4486.  
  4487. if (o-->type == TEXT && o-->select) {
  4488. o-->select = 0;
  4489. highlight(o, 0);
  4490. draw_object(o, 0);
  4491. strcpy(text, o-->data);
  4492. if (form("edit: %20s", text)) {
  4493. free(o-->data);
  4494. o-->data = strsave(text);
  4495. o-->r = o-->l + strlen(text)*8;
  4496. }
  4497. draw_object(o, 1);
  4498. slct_cnt-;
  4499. }
  4500. }
  4501.  
  4502. if (slct_cnt > 0) { /* must be other stuff */
  4503. start_actn(A_EDIT);
  4504. }
  4505. check_menu();
  4506. }
  4507.  
  4508.  
  4509. /* initiate an action on selected objects */
  4510.  
  4511. start_actn(actn) {
  4512. switch (actn) {
  4513. case A_COPY:
  4514. msg("copy to...");
  4515. break;
  4516. case A_MOVE:
  4517. msg("move to...");
  4518. break;
  4519. case A_EDIT:
  4520. msg("editing...");
  4521. break;
  4522. }
  4523. actn_obj = actn;
  4524. mouse_shape(M_CROSS);
  4525. }
  4526.  
  4527.  
  4528.  
  4529.  
  4530.  
  4531.  
  4532.  
  4533.  
  4534.  
  4535.  
  4536.  
  4537.  
  4538.  
  4539.  
  4540.  
  4541.  
  4542.  
  4543.  
  4544.  
  4545.  
  4546.  
  4547.  
  4548.  
  4549.  
  4550.  
  4551.  
  4552.  
  4553.  
  4554.  
  4555.  
  4556.  
  4557.  
  4558.  
  4559.  
  4560.  
  4561.  
  4562.  
  4563.  
  4564.  
  4565.  
  4566.  
  4567.  
  4568.  
  4569.  
  4570.  
  4571. Spiffier Windows For Turbo C
  4572.  
  4573.  
  4574. Tony Servies
  4575.  
  4576.  
  4577. Tony Servies is a programmer/analyst with World Computer Systems in Oak Ridge,
  4578. Tennessee. Presently he is working on a project to develop computer-based
  4579. training programs for the U.S. Navy. His computer interests include utilities
  4580. and C programming. You may contact him at Route 1, Box 143, Greenback, TN
  4581. 37742.
  4582.  
  4583.  
  4584. Want to spice up your user interface with flashy windows with only a minimal
  4585. amount of coding and time? With a few lines of code and Borland's Turbo C,
  4586. it's possible.
  4587.  
  4588.  
  4589. Turbo C Window Interface
  4590.  
  4591.  
  4592. Two functions can be used to create text windows in Turbo C: gettext() and
  4593. puttext(). Each function either gets a screen image or puts an image to the
  4594. screen, respectively. The programmer supplies only the window coordinates and
  4595. a character string pointer (or character array, if you will); the function
  4596. does the rest. These remarkable routines do some rudimentary screen I/O
  4597. quickly and cleanly.
  4598. One drawback though is the inherent lag between the time you write to the
  4599. window area and the moment the text is displayed. The user 'sees' any text
  4600. writing that you perform. Most applications today require a window flashed on
  4601. the screen intact, such as in a pull-down menu.
  4602. The code in Listing 1 allows writing to a window before it is displayed on the
  4603. screen. Then when the window is flashed on the screen, it is complete.
  4604. You call puttext_write with the x,y coordinates, window size, the character
  4605. string to display, the attribute for the string, and the pointer to the window
  4606. buffer. The x,y coordinates start from the upper left corner with the location
  4607. 0,0. The size of the window is given with the number of columns (width) and
  4608. the number of rows (heighth). The string to display is simply a character
  4609. string stored in standard C format; a '\0' character terminates the string.
  4610. The buffer is a pointer to an area of characters that denotes the window area.
  4611. The string attribute is the usual color attributes found in almost every
  4612. reference manual on PCs.
  4613.  
  4614.  
  4615. How It Works
  4616.  
  4617.  
  4618. puttext_write first checks that you are not positioning the data beyond the
  4619. physical bounds of the window. Of course, this routine will wrap any text past
  4620. the end of a line onto the following line (unless it is the last line in the
  4621. window).
  4622. The routine then gets the pointer address for the last character and attribute
  4623. pair in the window area, called maxbuffer in the subroutine. The offset for
  4624. the proper x,y location is added to the buffer so that it points to the
  4625. correct character.
  4626. While the buffer location is less than the maxbuffer pointer and the character
  4627. in the string is not the end of string terminator ('\0'), the while loop
  4628. updates the character and attribute of the buffer. The loop terminates only
  4629. when the buffer overflows (buffer >= maxbuffer) or at the end of string
  4630. (*string == '\0'). Now, just put the window on the screen and you're ready to
  4631. go. I've included a quick and dirty sample program illustrating the flashy
  4632. windows routine (Listing 2).
  4633. Note how easy it is to create a window. Just use a character array of
  4634. XSIZE*YSIZE*2 bytes. (You multiply the area by two because each displayed
  4635. character is followed by a byte of attribute information (color, blink,
  4636. etc.).)
  4637. The program then clears the window and sets all of the attributes. In this
  4638. example I set all attributes to magenta characters on cyan background. Then
  4639. the routine that does the actual call to puttext_write() loops through ten
  4640. times. After the page is full, I put the window on the screen with the
  4641. puttext() command and wait for half a second. The routine loops through nine
  4642. more times until it completes the for loop. I then restore the original screen
  4643. with the puttext() call for the original screen area (oldbuffer).
  4644. This routine should enable you to enhance your pull- down, pop-up, and
  4645. user-entry screens. Feel free to modify the code to account for border areas,
  4646. highlighted text, etc.
  4647.  
  4648. Listing 1
  4649. puttext_write (x, y, xsize,ysize,string,attr,buffer)
  4650. int x,y,xsize,ysize;
  4651. char *string, attr, *buffer;
  4652. {
  4653. char *maxbuffer;
  4654.  
  4655. if (x >= xsize y >= ysize) /* Range Errors */
  4656. return;
  4657.  
  4658. maxbuffer = buffer+(xsize*ysize*2)-1;
  4659. /* maxbuffer points to the attribute of the last character */
  4660.  
  4661. buffer += (((y*xsize)+x)*2);
  4662. /* buffer points to the first character to write */
  4663.  
  4664. /* While buffer is not overrun and there are characters left
  4665. * to print. */
  4666. while ((buffer < maxbuffer) && (*string != '\0')) {
  4667. *buffer++ = *string++; /* Do character */
  4668. *buffer++ = attr; /* Do attribute */
  4669. }
  4670. }
  4671.  
  4672.  
  4673. Listing 2
  4674.  
  4675. #include <stdio.h>
  4676. #include <conio.h>
  4677.  
  4678. #define XSIZE 50
  4679. #define YSIZE 15
  4680.  
  4681. char newbuffer[XSIZE * YSIZE * 2]; /* Allow for Attributes */
  4682. char oldbuffer[XSIZE * YSIZE * 2];
  4683.  
  4684. main()
  4685. {
  4686. int i, j;
  4687. char key_string[15];
  4688.  
  4689. /* Get the existing screen area and store it in oldbuffer.
  4690. * Subtract 1 from size, since the 1st position is 0.
  4691. */
  4692. gettext (5,5,5+XSIZE-1,5+YSIZE-1,oldbuffer);
  4693.  
  4694. /* Clear the new window area (newbuffer) */
  4695. for (i = 0; i < YSIZE; i++) {
  4696. for (j = 0; j < XSIZE*2; j+=2) {
  4697. newbuffer[i*XSIZE*2+j] = ' '; /* Blank Space */
  4698. newbuffer[i*XSIZE*2+j+1] = '\35'; /* Attribute */
  4699. }
  4700. }
  4701.  
  4702. /* Loop through 10 times */
  4703. for (j = 0; j < 10; j++) {
  4704.  
  4705. /* Print YSIZE lines */
  4706. for (i = 0; i < YSIZE; i++) {
  4707. sprintf(key_string,"Value %.3d",i+(j*(int)YSIZE));
  4708. puttext_write(1,i,XSIZE,YSIZE,key_string,\
  4709. '\35',newbuffer);
  4710. }
  4711.  
  4712. /* Show it on the screen */
  4713. puttext(5,5,5+XSIZE-1,5+YSIZE-1,newbuffer);
  4714. delay(500);
  4715. }
  4716.  
  4717. /* Restore the original screeen */
  4718. puttext(5,5,5+XSIZE-1,5+YSIZE-1,oldbuffer);
  4719. }
  4720.  
  4721.  
  4722.  
  4723.  
  4724.  
  4725.  
  4726.  
  4727.  
  4728.  
  4729.  
  4730.  
  4731.  
  4732.  
  4733.  
  4734.  
  4735.  
  4736.  
  4737.  
  4738. The C Programmer's Reference: A Bibliography Of Periodicals
  4739.  
  4740.  
  4741. Harold Ogg
  4742.  
  4743.  
  4744. This article is not available in electronic form.
  4745.  
  4746.  
  4747.  
  4748.  
  4749.  
  4750.  
  4751.  
  4752.  
  4753.  
  4754.  
  4755.  
  4756.  
  4757.  
  4758.  
  4759.  
  4760.  
  4761.  
  4762.  
  4763.  
  4764.  
  4765.  
  4766.  
  4767.  
  4768.  
  4769.  
  4770.  
  4771.  
  4772.  
  4773.  
  4774.  
  4775.  
  4776.  
  4777.  
  4778.  
  4779.  
  4780.  
  4781.  
  4782.  
  4783.  
  4784.  
  4785.  
  4786.  
  4787.  
  4788.  
  4789.  
  4790.  
  4791.  
  4792.  
  4793.  
  4794.  
  4795.  
  4796.  
  4797.  
  4798.  
  4799. Standard C
  4800.  
  4801.  
  4802. Formatted Input
  4803.  
  4804.  
  4805.  
  4806.  
  4807. P.J. Plauger
  4808.  
  4809.  
  4810. P.J. Plauger has been a prolific programmer, textbook author, and software
  4811. entrepreneur. He is secretary of the ANSI C standards committee, X3J11, and
  4812. convenor of the ISO C standard committee.
  4813.  
  4814.  
  4815. This is the fourth in a series of columns on input and output under Standard
  4816. C. (See "Evolution of the C I/O Model," CUJ August '89, "Streams," CUJ October
  4817. '89, and "Formatted Output," CUJ November '89.) The topic this month is how to
  4818. perform formatted input. You can think of it as a natural, but not essential,
  4819. companion to formatted output.
  4820. As I emphasized last month, you really must perform output somewhere in every
  4821. program that you write. If the output is to be directly digestible by human
  4822. beings, as is often the case, then you want the program to produce readable
  4823. text. The formatted output functions help you produce readable text that
  4824. reflects the values of encoded data in your program.
  4825. On the other hand, not all programs read input. Those that do can read data
  4826. directly, using an assortment of standard library functions, and interpret it
  4827. as they see fit. Converting small integers and text strings for internal
  4828. consumption are both five-finger exercises that most C programmers perform
  4829. easily. It is only when you must convert floating point values, or recognize a
  4830. complex mix of data fields, that standard scanning functions begin to look
  4831. attractive.
  4832. Even then the choice is not always clear. The usability of a program depends
  4833. heavily on how tolerant it is to variations in user input. You as a programmer
  4834. may not agree with the conventions enforced by the standard formatted input
  4835. functions. You may not like the way they handle errors. In short, you are much
  4836. more likely to want to roll your own input scanner.
  4837. Obtaining formatted input in not simply the inverse of producing formatted
  4838. output. With output, you know what you want the program to generate next and
  4839. it does it. With input, however, you are more at the mercy of the person
  4840. producing the input text. Your program must scan the input text for
  4841. recognizable patterns, then parse it into separate fields. Only then can it
  4842. determine what to do next.
  4843. Not only that, the input text may contain no recognizable pattern. You must
  4844. then decide how to respond to such an "error." Do you print a nasty message
  4845. and prompt for fresh input? Do you make an educated guess and bull ahead? Or
  4846. do you abort the program? Various canned input scanners have tried all of
  4847. these strategies. No one of them is appropriate for all cases.
  4848. It is no surprise, therefore, that the history of the formatted input
  4849. functions in C is far more checkered than for the formatted output functions.
  4850. Most implementations of C have long agreed on the basic properties of printf
  4851. and its buddies. (A notable exception is the I/O library I originally wrote
  4852. for the Whitesmiths C compiler. It nicely regularized the names of functions
  4853. and format conversion specifications, but at a serious cost in compatibility.
  4854. Eventually, we had to abandon our special dialect of I/O.) By contrast, scanf
  4855. and its ilk have changed steadily over the years and have proliferated
  4856. dialects.
  4857. Committee X3J11 spent considerable time sorting out the proper behavior of
  4858. formatted input. Once we agreed on which input conversions to include in
  4859. Standard C, we had to agree on exactly what they did. Implementations varied
  4860. on the valid formats for numeric fields. They were all over the map on how to
  4861. respond to invalid input. They seldom clarified how scanf interacts with
  4862. ungetc and other I/O functions.
  4863. All these decisions had to be made in an atmosphere of general
  4864. dissatisfaction. A vocal minority wanted major changes in the formatted input
  4865. functions. An almost silent majority didn't want to be bothered with details
  4866. about functions they considered useless at best, dangerous at worst. Given all
  4867. these handicaps, I think X3J11 did rather a good job of clarifying the
  4868. formatted input functions and making them useful.
  4869. After that introduction, I will rashly assume that you still care about the
  4870. formatted input functions. The rest of this column discusses the scan
  4871. functions, so called because they all have scan as part of their names. These
  4872. are the functions that scan input text and convert text fields to encoded
  4873. data. All are declared in the standard header <stdio.h>. To use the scan
  4874. functions, you must know how to call them, how to specify conversion formats,
  4875. and what conversions they will perform for you.
  4876.  
  4877.  
  4878. Calling Scan Functions
  4879.  
  4880.  
  4881. The Standard C library provides three different scan functions, declared as
  4882. follows:
  4883. int fscanf(FILE *stream, const char *format, ...);
  4884. int scanf(const char *format, ...);
  4885. int sscanf(char *src, const char *format, ...);
  4886. The function fscanf obtains characters from the stream stream. The function
  4887. scanf obtains characters from the stream stdin. Both stop scanning input early
  4888. if an attempt to obtain a character sets the end-of-file or error indicator
  4889. for the stream. The function sscanf obtains characters from the
  4890. null-terminated string beginning at src. It stops scanning input early if it
  4891. encounters the terminating null character for the string.
  4892. Note that all of the functions accept a varying number of arguments, just like
  4893. the print functions. And just like the print functions, you had better declare
  4894. any scan functions before you use them by including <stdio.h>. Otherwise, some
  4895. implementation may go crazy when you call your undeclared scan function.
  4896. All the functions accept a read-only format argument, which is a pointer to a
  4897. null-terminated string. The format tells the function what additional
  4898. arguments to expect, if any, and how to convert input fields to values to be
  4899. stored. (A typical argument is a pointer to a data object that receives the
  4900. converted value.) It also specifies any literal text or whitespace you want to
  4901. match between converted fields. If scan formats sound remarkably like print
  4902. formats, the resemblance is quite intentional. But there are also important
  4903. differences. I will revisit formats in considerable detail later in this
  4904. column.
  4905. All the functions return a count of the number of text fields converted to
  4906. values that are stored. If any of the functions stops scanning early for one
  4907. of the reasons cited above, however, it returns the value of the macro EOF
  4908. (defined in the standard header <stdio.h>). Since EOF must have a negative
  4909. value, you can easily distinguish it from any valid count, including zero.
  4910. Note, however, that you can't tell how many values were stored before an early
  4911. stop. If you need to locate a stopping point more precisely, break your scan
  4912. call into multiple calls.
  4913. A scan function can also stop scanning because it obtains a character that it
  4914. is unprepared to deal with. In this case, the function returns the cumulative
  4915. count of values converted and stored. You can determine the largest possible
  4916. return value for any given call by counting all the conversions you specify in
  4917. the format. The actual return value will be between zero and this maximum
  4918. value, inclusive.
  4919. When either fscanf or scanf obtains such an unexpected character, it pushes it
  4920. back to the input stream. (It also pushes back the first character beyond a
  4921. valid field when it has to peek ahead to determine the end of the field.) How
  4922. it does so is similar to calling the function ungetc. There is a very
  4923. important difference, however. You cannot portably push back two characters to
  4924. a stream with successive calls to ungetc (and no other intervening operations
  4925. on the stream). You can portably follow an arbitrary call to a scan function
  4926. with a call to ungetc for the same stream.
  4927. What this means effectively is that the one-character pushback limit imposed
  4928. on ungetc is not compromised by calls to the scan functions. Either the
  4929. implementation guarantees two or more characters of pushback to a stream or it
  4930. provides separate machinery for the scan functions.
  4931. Note that the scan functions push back at most one character. Say, for
  4932. example, that you try to convert the field 123EASY as a floating point value.
  4933. The field is, of course, invalid. Even the subfield 123E is invalid, since the
  4934. conversion requires at least one exponent digit. What will happen is, the
  4935. subfield 123E is consumed and the conversion fails. No value is stored and the
  4936. scan function returns. The next character to read from the stream is A. This
  4937. behavior matters most for floating point fields, which have the most ornate
  4938. syntax. Other conversions can usually digest all the characters in the longest
  4939. subfield that looks valid.
  4940. As a final point, the Standard C library does not provide any of the functions
  4941. vfscanf, vscanf, or vsscanf. These are obvious analogs to the print functions
  4942. vfprintf, vprintf, and vsprintf which I described last month. X3J11 simply
  4943. felt that there was not enough call for such scan functions to require them of
  4944. all implementations.
  4945.  
  4946.  
  4947. Writing Formats
  4948.  
  4949.  
  4950. Last month, I described the print formats as a mini programming language. The
  4951. same is, of course, true of the scan formats. I also commented earlier that
  4952. print and scan formats look remarkably alike. This should serve as both a
  4953. comfort and a warning to you.
  4954. The comfort is that the print and scan functions are designed to work
  4955. together. What you write to a text file with one program should be readable as
  4956. a text file by another. Any values you represent in text by calling a print
  4957. function should be reclaimable by calling a scan function. (At least they
  4958. should be to good accuracy, over a reasonable range of values.) You would even
  4959. like the print and scan formats to resemble each other closely.
  4960. Doug McIlroy, at AT&T Bell Laboratories, makes a stronger statement. He feels
  4961. that any good formatted I/O package should let you write identical formats for
  4962. print and scan function calls. A formatting language that is not symmetric, he
  4963. feels, is deficient. I believe that Standard C comes close to achieving this
  4964. goal. It is at least possible for you to write symmetric formats (those that
  4965. read back what you wrote out). Be warned, however, that developing symmetry
  4966. can take a bit of extra thought.
  4967. And here lies the danger. The fact remains that the print and scan format
  4968. languages are different. Sometimes the apparent similarity is only
  4969. superficial. You can write text with a print function call that does not scan
  4970. as you might expect with a scan function call using the same format. Be
  4971. particularly wary when you print text using conversions with no intervening
  4972. whitespace. Be somewhat wary when you print adjacent whitespace in two
  4973. successive print calls. The scan functions tend to run together fields that
  4974. you think of as separate.
  4975. The basic operation of the scan functions is, indeed, the same as for the
  4976. print functions. Call a scan function and it scans the format string once from
  4977. beginning to end. As it recognizes each component of the format string, it
  4978. performs various operations. Most of these operations consume characters
  4979. sequentially from a stream (fscanf or scanf) or from a string stored in memory
  4980. (sscanf).
  4981. Many of these operations generate values that the scan function stores in
  4982. various data objects that you specify with pointer arguments. Any such
  4983. arguments must appear in the varying length argument list, in the order in
  4984. which the format string calls for them. For example,
  4985. sscanf("thx 1138", "%s%2o%d",
  4986. &a, &b, &c);
  4987. stores the string "thx" in the char array a, the value 9 (octal eleven) in the
  4988. int data object b, and the value 38 in the int data object c.
  4989. It is up to you to ensure that the type of each actual argument pointer
  4990. matches the type expected by the scan function. (The pointer must, of course,
  4991. also point to a data object of the expected type.) Standard C has no way to
  4992. check the types of additional arguments in a varying length argument list.
  4993.  
  4994. Not every part of a format string calls for the conversion of a field and the
  4995. consumption of an additional argument. In fact, only certain conversion
  4996. specifications gobble arguments. Every conversion specification begins with
  4997. the % escape character and matches one of the patterns described below. The
  4998. scan functions treat everything else either as whitespace or as literal text.
  4999. Whitespace in a scan format, by the way, is whatever the standard library
  5000. function iswhite (declared in <ctype.h>) says it is. That can change if you
  5001. call the function setlocale (declared in <locale.h>) before you call the scan
  5002. function. Your program begins execution in the "C" locale, where whitespace is
  5003. what you have learned to know and love.
  5004. A sequence of one or more whitespace characters in a scan format is treated as
  5005. a single entity. It consumes an arbitrarily long sequence of whitespace
  5006. characters from the input. (Again, whitespace is whatever the current locale
  5007. says it is.) The whitespace in the format need not resemble the whitespace in
  5008. the input in any way. The input can contain no whitespace. Whitespace in the
  5009. format simply guarantees that the next input character (if any) is not a
  5010. whitespace character.
  5011. Any character in the format that is not whitespace and not part of a
  5012. conversion specification calls for a literal match. The next input character
  5013. must match the format character. Otherwise, the scan function returns with the
  5014. current count of converted values stored. A format that ends with a literal
  5015. match can produce ambiguous results. You cannot determine from the return
  5016. value whether the trailing match failed. Similarly, you cannot determine
  5017. whether a literal match failed or a conversion that follows it. For these
  5018. reasons, literal matches have only limited use in scan formats.
  5019. For completeness, I should point out that a literal match can be any string of
  5020. multibyte characters. Each sequence of literal text must begin and end in the
  5021. initial shift state, if your target environment uses a state-dependent
  5022. encoding for multibyte characters. I suspect, however, that you will have
  5023. little need to match Kanji characters with scan formats in the next few years.
  5024.  
  5025.  
  5026. Conversion Specifications
  5027.  
  5028.  
  5029. A scan conversion specification differs from a print conversion specification
  5030. in fundamental ways. You cannot write any of the print conversion flags and
  5031. you cannot write a precision (following a decimal point). On the other hand,
  5032. scan conversions have an assignment-suppression flag and a conversion
  5033. specification called a scan set. Following the % you write three components.
  5034. All but the last component is optional. In order:
  5035. You write an optional asterisk (*) to specify that the converted value is not
  5036. to be stored.
  5037. You write an optional field width to specify the maximum number of input
  5038. characters to match when determining the conversion field. The field width is
  5039. an unsigned decimal integer. Many conversions skip any leading whitespace,
  5040. which is not counted as part of the field width.
  5041. You write a conversion specifier to determine the type of any argument, how to
  5042. determine its conversion field, and how to convert the value to store. You
  5043. write a scan set conversion specifier between brackets ([]). All others
  5044. consist of one or two character sequences from a predefined list of about
  5045. three dozen valid sequences. The two-character sequences begin with an h, l,
  5046. or L, to indicate alternate argument types. I describe scan sets and list all
  5047. valid sequences in Table 1. Don't write anything else in a scan format if you
  5048. want your code to be portable.
  5049. The goal of each formatted input conversion is to determine the sequence of
  5050. input characters that constitutes the field to convert. The scan function then
  5051. converts the field, if possible, and stores the converted value in the data
  5052. object designated by the next pointer argument. (If assignment is suppressed,
  5053. no function argument is consumed.)
  5054. Unless otherwise specified below, each conversion first skips arbitrary
  5055. whitespace in the input. Skipping is just the same as for whitespace in the
  5056. scan format. The conversion then matches a pattern against succeeding
  5057. characters in the input to determine the conversion field. You can specify a
  5058. field width to limit the size of the field. Otherwise, the field extends to
  5059. the last character in the input that matches the pattern.
  5060. The scan functions convert numeric fields by calling one of the standard
  5061. library functions strtod, strtol, or strtoul (declared in <stdlib.h>). A
  5062. numeric conversion field matches the longest pattern acceptable to the
  5063. function it calls.
  5064.  
  5065.  
  5066. Scan Sets
  5067.  
  5068.  
  5069. A scan set behaves much like the s conversion specifier. It stores up to w
  5070. characters (default is the rest of the input) in the array of char pointed at
  5071. by ptr. It always stores a null character after any input.
  5072. It does not, however, skip leading whitespace. It also lets you specify what
  5073. characters to consider as part of the field. You can specify all the
  5074. characters to match, as in:
  5075. "%[0123456789abcdefABCDEF]"
  5076. which matches an arbitrary sequence of hexadecimal digits. Or you can specify
  5077. all the characters that do not match, as in:
  5078. "%[^0123456789]"
  5079. which matches any characters other than digits.
  5080. If you want to include the right bracket (]) in the set of characters you
  5081. specify, write it immediately after the opening [ (or [^). You cannot include
  5082. the null character in the set of characters you specify.
  5083. Some implementations may let you specify a range of characters by using a
  5084. minus sign (-). The list of hexadecimal digits, for example, can be written
  5085. as:
  5086. "%[0-9abcdefABCDEF]"
  5087. or even, in some cases, as:
  5088. "%[0-9a-fA-F]"
  5089. Please note, however, that such usage is not universal. Avoid it in a program
  5090. that you wish to keep maximally portable.
  5091. Table 1
  5092. Conversion Specifiers
  5093.  In the descriptions that follow, I summarize the match
  5094. pattern and conversion rules for each valid conversion
  5095. specifier. w stands for the field width you specify, or the
  5096. indicated default value if you specify no field width. ptr
  5097. stands for the next argument to consume in the varying
  5098. length argument list:
  5099.  c -- stores w characters (default is 1) in the array of
  5100. char whose first element is pointed at by ptr. It does not
  5101. skip leading whitespace.
  5102.  d -- converts the integer input field by calling strtol
  5103. with a base of 10, then stores the result in the int pointed
  5104. at by ptr.
  5105.  hd -- converts the integer input field by calling strtol
  5106. with a base of 10, then stores the result in the short
  5107. pointed at by ptr.
  5108.  ld -- converts the integer input field by calling strtol
  5109. with a base of 10, then stores the result in the long
  5110. pointed at by ptr.
  5111.  e -- converts the floating point input field by calling
  5112. strtod, then stores the result in the float pointed at by
  5113. ptr.
  5114.  le -- converts the floating point input field by calling
  5115. strtod, then stores the result in the double pointed at by
  5116.  
  5117. ptr.
  5118.  Le -- converts the floating point input field by calling
  5119. strtod, then stores the result in the long double pointed
  5120. at by ptr.
  5121.  E -- is the same as e.
  5122.  lE -- is the same as le.
  5123.  LE -- is the same as Le.
  5124.  f -- is the same as e.
  5125.  lf -- is the same as le.
  5126.  Lf -- is the same as Le.
  5127.  g -- is the same as e.
  5128.  lg -- is the same as le.
  5129.  Lg -- is the same as Le.
  5130.  G -- is the same as e.
  5131.  lG -- is the same as le.
  5132.  LG -- is the same as Le
  5133.  i -- converts the integer input field by calling strtol
  5134. with a base of zero, then stores the result in the int
  5135. pointed at by ptr. (A base of zero lets you write input
  5136. that begins with 0, 0x, or 0X to specify an actual numeric
  5137. base other than 10.)
  5138.  hi -- converts the integer input field by calling strtol
  5139. with a base of zero, then stores the result in the short
  5140. pointed at by ptr.
  5141.  i -- converts the integer input field by calling strtol
  5142. with a base of zero, then stores the result in the long
  5143. pointed at by ptr.
  5144.  n -- converts no input, but stores the cumulative
  5145. number of matched input characters in the int pointed at
  5146. by ptr. It does not skip leading whitespace.
  5147.  hn -- converts no input, but stores the cumulative
  5148. number of matched input characters in the short pointed
  5149. at by ptr. It does not skip leading whitespace.
  5150.  ln -- converts no input, but stores the cumulative
  5151. number of matched input characters in the long pointed
  5152. at by ptr. It does not skip leading whitespace.
  5153.  o -- converts the integer input field by calling strtoul
  5154. with a base of eight, then stores the result in the
  5155. unsigned int pointed at by ptr.
  5156.  ho -- converts the integer input field by calling
  5157. strtoul with a base of eight, then stores the result in the
  5158. unsigned short pointed at by ptr.
  5159.  lo -- converts the integer input field by calling
  5160. strtoul with a base of eight, then stores the result in the
  5161. unsigned long pointed at by ptr.
  5162.  p -- converts the pointer input field, then stores the
  5163. result in the void * pointed at by ptr. Each
  5164. implementation defines its pointer input field to be consistent with
  5165. pointers written by the print function.
  5166.  s -- stores up to w non-whitespace characters (default
  5167. is the rest of the input) in the array of char pointed at by
  5168. ptr. It first skips leading whitespace, and it always stores a
  5169. null character after any input.
  5170.  u -- converts the integer input field by calling strtoul
  5171. with a base of 10, then stores the result in the unsigned
  5172. int pointed at by ptr.
  5173.  hu -- converts the integer input field by calling
  5174. strtoul with a base of 10, then stores the result in the
  5175. unsigned short pointed at by ptr.
  5176.  
  5177.  lu -- converts the integer input field by calling
  5178. strtoul with a base of 10, then stores the result in the
  5179. unsigned long pointed at by ptr.
  5180.  x -- converts the integer input field by calling strtoul
  5181. with a base of 16, then stores the result in the unsigned
  5182. int pointed at by ptr.
  5183.  hx -- converts the integer input field by calling
  5184. strtoul with a base of 16, then stores the result in the
  5185. unsigned short pointed at by ptr.
  5186.  lx -- converts the integer input field by calling
  5187. strtoul with a base of 16, then stores the result in the
  5188. unsigned long pointed at by ptr.
  5189.  X -- is the same as x.
  5190.  hX -- is the same as hx.
  5191.  lX -- is the same as lx.
  5192.  % -- converts no input, but matches a percent
  5193. character. (%)
  5194.  
  5195.  
  5196.  
  5197.  
  5198.  
  5199.  
  5200.  
  5201.  
  5202.  
  5203.  
  5204.  
  5205.  
  5206.  
  5207.  
  5208.  
  5209.  
  5210.  
  5211.  
  5212.  
  5213.  
  5214.  
  5215.  
  5216.  
  5217.  
  5218.  
  5219.  
  5220.  
  5221.  
  5222.  
  5223.  
  5224.  
  5225.  
  5226.  
  5227.  
  5228.  
  5229.  
  5230.  
  5231.  
  5232.  
  5233.  
  5234.  
  5235.  
  5236.  
  5237.  
  5238.  
  5239.  
  5240. Doctor C's Pointers (R)
  5241.  
  5242.  
  5243. The Memory Management Library
  5244.  
  5245.  
  5246.  
  5247.  
  5248. Rex Jaeschke
  5249.  
  5250.  
  5251. Rex Jaeschke is an independent computer consultant, author and seminar leader.
  5252. He participates in both ANSI and ISO C Standards meetings and is the editor of
  5253. The Journal of C Language Translation, a quarterly publication aimed at
  5254. implementers of C language translation tools. Readers are encouraged to submit
  5255. column topics and suggestions to Rex at 2051 Swans Neck Way, Reston, VA, 22091
  5256. or via UUCP at uunet!aussie!rex.
  5257.  
  5258.  
  5259. The C run-time library has long had a family of routines that enable a
  5260. programmer to allocate and free memory at run-time, at his pleasure. This
  5261. capability is a powerful one and was adopted (and somewhat expanded) in ANSI
  5262. C.
  5263. Oftentimes you define an array of elements (necessarily of fixed size) only to
  5264. find that, in most cases, you don't use all the elements or that, in some
  5265. cases, you need just a few more. What you need is the ability to have variable
  5266. sized arrays. However, according to the definition of C, the dimension of an
  5267. array in a definition must be a compile-time integer constant. That is, the C
  5268. language does not support such constructs. (Note that the Numerical C
  5269. Extensions Group, of which I am the convener, is investigating the possibility
  5270. of adding such a construct.) However, this idea can be implemented using the
  5271. memory allocation routines in the standard library.
  5272. The beauty of these allocation routines is twofold: the programmer determines
  5273. just when space is allocated and exactly how long it is kept, and, if the
  5274. program is written correctly, you can change the manner in which the space is
  5275. allocated and freed, transparently. Let's discuss the second point further.
  5276. ANSI C defines the term storage duration by saying "An object has a storage
  5277. duration that determines its lifetime. There are two storage durations: static
  5278. and automatic." I prefer to also add a third duration, dynamic. An object
  5279. having dynamic storage duration is one allocated by the programmer using the
  5280. library. (For the purposes of this discussion, the address space from which
  5281. dynamic objects are allocated will be referred to as the heap. This term is
  5282. widely used for this purpose but is not used in the ANSI C Standard.)
  5283. Consider the following example:
  5284. #include <stdlib.h>
  5285.  
  5286. void f()
  5287. {
  5288. char c1[100];
  5289. static char c2[100];
  5290. char *c3;
  5291. c3 = malloc(100);
  5292.  
  5293. c1[10] = 'a';
  5294. c2[10] = 'a';
  5295. c3[10] = 'a';
  5296. }
  5297. Ignoring the possibility of malloc() failing to allocate memory, c1, c2, and
  5298. c3 can be used to designate the automatic, static, and dynamic arrays,
  5299. respectively. Since the notation for referencing all three arrays is
  5300. identical, the executable code can be ignorant of the object's storage
  5301. duration. You can change from automatic to dynamic, from dynamic to static,
  5302. etc., with no real impact on the code, if you design it appropriately to begin
  5303. with.
  5304. The allocation functions somehow magically change the address space of our
  5305. program at run-time. The way in which this is done is specific to an
  5306. implementation and may vary widely. In any case, an understanding of such
  5307. details is unnecessary to use the allocation functions effectively. All you
  5308. need know is that if they succeed, the requested space is allocated
  5309. contiguously and you are given a base address.
  5310.  
  5311.  
  5312. The Parent Header
  5313.  
  5314.  
  5315. In the not too distant past, there were only four or five "standard" headers.
  5316. Apart from those, there was a wide variation as to which functions were
  5317. provided and in which header (if any) they were declared. ANSI C requires the
  5318. allocation functions to be declared in the header stdlib.h. Many
  5319. implementations currently declare them in malloc.h as well as, or instead of,
  5320. stdlib.h. I have also seen quite a lot of old code that contained explicit
  5321. declarations for these functions, presumably because no header in their
  5322. implementation contained them.
  5323. As a result of ANSI C, the declarations of these functions has changed both
  5324. with regard to return as well as argument types. ANSI C adopted the concept of
  5325. a void pointer from C++. This solved two important issues: it provided a
  5326. bridge for porting code across byte and word (and other) architectures where
  5327. different pointer types may actually have different physical representations,
  5328. and secondly, it provided a way to represent a generic pointer, one that
  5329. simply contained an address of some (unknown) object type.
  5330. Since the allocation routines are not given any information about the type of
  5331. object a programmer wishes to store in the allocated space, the pointers used
  5332. and returned by these functions were prime candidates for type void *. A
  5333. consequence of this is that the returned value no longer need be explicitly
  5334. cast. For example in the following case:
  5335. int *pi;
  5336. pi = (int *)malloc(10 * sizeof(int));
  5337. pi = malloc(10 * sizeof(int));
  5338. the assignments are equivalent since a void pointer is assignment-compatible
  5339. with all other pointer types. (Historically, it was common to see such casts
  5340. even though they generally were not needed. That is, strict pointer
  5341. assignment-compatibility checking was not enforced as is now required by
  5342. ANSI.)
  5343. If some of your code explicitly declares the allocation functions as having
  5344. return types of char *, without such casts you will get errors when compiling
  5345. in strict ANSI mode if the target of the assignment has type other than char
  5346. *. The best solution to this is to remove the explicit declaration and include
  5347. stdlib.h instead.
  5348. With ANSI's adaptation of function prototypes from C++, stdlib.h now describes
  5349. the allocation routines' argument type information as well. Again, all pointer
  5350. types here have type void * but this is of no consequence since any "real"
  5351. pointer type is compatible with void * and, as such, objects of such type can
  5352. be passed in.
  5353. ANSI C has invented the type size_t, the type of a sizeof expression. This
  5354. type is typedefed in numerous standard headers including stdlib.h and is used
  5355. in various library function prototypes (including the allocation functions)
  5356. for the type of sizes and counts. Since sizes and counts can never be
  5357. negative, size_t is an unsigned integer type. However, the underlying type of
  5358. size_t is implementation-defined and may be unsigned int or unsigned long.
  5359. Historically, descriptions of the allocation functions stated that sizes and
  5360. counts had type unsigned int.
  5361.  
  5362.  
  5363. The Allocation Functions
  5364.  
  5365.  
  5366.  
  5367.  
  5368. calloc
  5369.  
  5370.  
  5371.  
  5372. #include <stdlib.h>
  5373. void *calloc(size_t nmemb, size_t size);
  5374. calloc() allocates contiguous space for nmemb objects, each of whose size is
  5375. size.
  5376. The space allocated is initialized to all-bits-zero. Note that this is not
  5377. guaranteed to be the same representation as floating-point zero or the null
  5378. pointer constant NULL.
  5379.  
  5380.  
  5381. free
  5382.  
  5383.  
  5384. #include <stdlib.h>
  5385. void free(void *ptr);
  5386. free() causes the space (previously allocated by calloc(), malloc(), or
  5387. realloc()) pointed to by ptr to be freed.
  5388. If ptr is NULL, free does nothing. Otherwise, if ptr is not a value previously
  5389. returned by one of these three allocation functions, the behavior is
  5390. undefined.
  5391. The value of a pointer that refers to space that has been freed is
  5392. indeterminate, and such pointers should not be dereferenced.
  5393. Note that free() has no way to communicate an error if one is detected.
  5394. On some systems, most noticeably MS-DOS, freed space may not actually be given
  5395. back to the operating system. (It likely will, however, be available for
  5396. future allocations within that program.) It might only be really released when
  5397. the program terminates. One consequence of this is that if you try to execute
  5398. another program from within a running program that has freed up memory using
  5399. free(), there still might not be sufficient physical memory available to start
  5400. the new program.
  5401.  
  5402.  
  5403. malloc
  5404.  
  5405.  
  5406. #include <stdlib.h>
  5407. void *malloc(size_t size);
  5408. malloc() allocates contiguous space for size bytes. The space allocated has no
  5409. guaranteed initial value.
  5410.  
  5411.  
  5412. realloc
  5413.  
  5414.  
  5415. #include <stdlib.h>
  5416. void *realloc(void *ptr, size_t
  5417. size);
  5418. realloc() changes the size of the space pointed to by ptr to have size size.
  5419. If ptr is NULL, realloc() behaves like malloc(). Otherwise, if ptr is not a
  5420. value previously returned by calloc(), malloc(), or realloc(), the behavior is
  5421. undefined. The same is true if ptr points to space that has been freed.
  5422. size is absolute, not relative. If size is larger than the size of the
  5423. existing space, new uninitialized contiguous space is allocated at the end;
  5424. the previous contents of the space are preserved. If size is smaller, the
  5425. excess space is freed; however, the contents of the retained space are
  5426. preserved.
  5427. If realloc() cannot allocate the requested space, the contents of the space
  5428. pointed to by ptr remain intact.
  5429. If ptr is non-NULL and size is 0, realloc() acts like free().
  5430. Whenever the size of space is changed by realloc(), the new space may begin at
  5431. an address different from the one given it, even when realloc() is truncating.
  5432. Therefore, if you use realloc() in this manner, you must beware of pointers
  5433. that point into this possibly-moved space. For example, if you build a linked
  5434. list there and use realloc() to allocate more (or less) space for the chain,
  5435. it is possible that the space will be "moved," in which case the pointers now
  5436. point to where successive links used to be, not where they are now. You should
  5437. always use realloc() as follows:
  5438. ptr1 = realloc(ptr, new_size);
  5439. if (ptr1 != NULL) {
  5440. ptr = ptr1;
  5441. ...
  5442. }
  5443. This way, you never care whether the object has been relocated since you
  5444. always update ptr each call, to point to the (possibly new) location.
  5445.  
  5446.  
  5447. General Comments
  5448.  
  5449.  
  5450. The way in which a heap is physically organized can vary widely. On some
  5451. systems, the stack and the heap (and possibly even the static data area) share
  5452. the same address space. On others, each may have its own address space. Some
  5453. MS-DOS implementations provide both near and far heaps.
  5454. Historically, many C implementations have permitted the allocation of zero
  5455. bytes to be successful. That is, a non-NULL pointer is returned. Since ANSI C
  5456. does not permit zero-sized objects to be defined, this practice was hotly
  5457. debated during X3J11 deliberations. As a compromise, if you attempt to
  5458. allocate zero bytes, it is implementation-defined whether a null pointer or a
  5459. unique pointer is returned.
  5460. We are told that if an allocation attempt fails, NULL is returned. The common
  5461. approach I've seen to this is to display some error message and call exit().
  5462. However, most applications I have seen could ill-afford to actually do this
  5463. since it would leave either disk files and/or shared memory data areas
  5464. compromised. For example, if you cannot get more dynamic space, you may have
  5465. quite some work to undo your current situation before you can gracefully
  5466. terminate or continue. On the other hand, failure to allocate more memory when
  5467. doing an in-memory sort can simply be handled by writing the sorted tree to
  5468. disk, freeing the memory, and starting on the next set of strings. In such
  5469. cases, the failure to allocate memory is not fatal. In cases where it is, you
  5470. must consider the ramifications of receiving a NULL return at design time, not
  5471. during maintenance when the first failure occurs.
  5472. When heap allocation fails it might well be useful to find out how much you
  5473. can get. Unfortunately, ANSI C does not provide this capability.
  5474. Several implementations (including Microsoft's) do provide some help in this
  5475. area. Either they can tell you how much is available now in one allocation or,
  5476. how many allocations you can make of a given size. (The two need not add up to
  5477. the same number of bytes since each time you request bytes, extra bytes may
  5478. also be fetched to help manage the space allocated.)
  5479. Similarly, ANSI C provides no help in debugging heap-related problems by
  5480. "walking the heap links" and the like. Again, it's up to the quality of the
  5481. implementation.
  5482. On some systems (VAX/VMS, for example) the cost of allocating memory
  5483. dynamically can be somewhat expensive. As such, a caching approach may be
  5484. taken. That is, when you free memory the larger of the freed block and the
  5485. cache currently held, will be kept. The idea is that if you alternately
  5486. allocate and free, each new allocation will have some chance of getting memory
  5487. from the freed cache.
  5488. ANSI C guarantees that any non-NULL address returned by the allocation
  5489. functions will be aligned appropriately so it can be dereferenced via any
  5490. pointer type. On systems that require object alignment, this means that space
  5491. is allocated in multiples of some cluster value (such as machine words, for
  5492. example.) On such systems, more memory may actually be allocated than you
  5493. requested. If your program contains a bug and copies (slightly) beyond the end
  5494. of allocated memory, the bytes overwritten may be those extra ones and no
  5495. error occurs. However, if you change the request to a few extra bytes, the bug
  5496. may manifest itself. The most common example I see is as follows:
  5497.  
  5498. char name[30];
  5499.  
  5500. getname(name);
  5501. pc = malloc(strlen(name));
  5502. strcpy(pc, name);
  5503. Here, strcpy() adds a null character to the destination but no space was
  5504. allocated for it. If the length was odd and malloc() allocates an even number
  5505. anyway, the problem will not be observed. However, with even length names it
  5506. may well appear.
  5507. It is considered good style to explicitly free allocated memory when you are
  5508. done with it. Presumably, if you don't, this is done when your program
  5509. terminates (although this is not so stated by ANSI C.) Note that if you
  5510. "forget" where your allocated space resides (by overwriting the pointer value
  5511. returned by malloc(), for example), there is no way of getting that address
  5512. back. One relatively easy way of having this happen is to use:
  5513. ptr = realloc(ptr, new_size);
  5514. If realloc() fails, you have lost the address of the original area.
  5515. An alternate memory allocation system also exists in many systems. It usually
  5516. involves using sbrk(). The two schemes are incompatible and must not be used
  5517. in the same program. ANSI C does not include this alternate scheme.
  5518.  
  5519.  
  5520. Transparent Heap Usage
  5521.  
  5522.  
  5523. It is possible that your program calls the allocation routines even if you
  5524. don't call them yourself. For example, some library routines might need
  5525. dynamic space to efficiently handle variable size amounts of local
  5526. information.
  5527. Many systems have a fixed limit on the number of open files they support.
  5528. However, others do not. They can achieve this by building a linked list of
  5529. FILE objects using the allocation routines. They may even include stdin,
  5530. stdout, and stderr in this list, in which case, the program startup code may
  5531. contain calls to malloc(), etc. Compile and link an empty main() program and
  5532. look at the linker map to see if these library functions are called at
  5533. startup.
  5534.  
  5535.  
  5536. Multi-Dimensional Arrays
  5537.  
  5538.  
  5539. Occasionally, it may be necessary to allocate a multi-dimensional array on the
  5540. heap. This can be done just as easily as for single-dimensioned arrays once
  5541. you master the required pointer declaration. For example,
  5542. double (*pd)[10];
  5543. pd = malloc(50 * sizeof(double));
  5544. pd[3][2] = 1.234;
  5545. By declaring pd to be a pointer to an array of 10 doubles, pd can be
  5546. subscripted to two levels. pd[3] designates the fourth row of 10 elements and
  5547. pd[3] [2] designates the third column in that row. (If you are confused about
  5548. the difference between a pointer to double and a pointer to an array of
  5549. double, you will have to wait for a future column.)
  5550.  
  5551.  
  5552.  
  5553.  
  5554.  
  5555.  
  5556.  
  5557.  
  5558.  
  5559.  
  5560.  
  5561.  
  5562.  
  5563.  
  5564.  
  5565.  
  5566.  
  5567.  
  5568.  
  5569.  
  5570.  
  5571.  
  5572.  
  5573.  
  5574.  
  5575.  
  5576.  
  5577.  
  5578.  
  5579.  
  5580.  
  5581.  
  5582.  
  5583.  
  5584.  
  5585.  
  5586. Implementer's Notebook
  5587.  
  5588.  
  5589. Life With Static Buffers
  5590.  
  5591.  
  5592.  
  5593.  
  5594. Don Libes
  5595.  
  5596.  
  5597. This article is not available in electronic form.
  5598.  
  5599.  
  5600.  
  5601.  
  5602.  
  5603.  
  5604.  
  5605.  
  5606.  
  5607.  
  5608.  
  5609.  
  5610.  
  5611.  
  5612.  
  5613.  
  5614.  
  5615.  
  5616.  
  5617.  
  5618.  
  5619.  
  5620.  
  5621.  
  5622.  
  5623.  
  5624.  
  5625.  
  5626.  
  5627.  
  5628.  
  5629.  
  5630.  
  5631.  
  5632.  
  5633.  
  5634.  
  5635.  
  5636.  
  5637.  
  5638.  
  5639.  
  5640.  
  5641.  
  5642.  
  5643.  
  5644.  
  5645.  
  5646.  
  5647. Applying C++
  5648.  
  5649.  
  5650. Designing And Implementing A Text Editor Using OOP, Part 1
  5651.  
  5652.  
  5653.  
  5654.  
  5655. Tsvi Bar-David
  5656.  
  5657.  
  5658. Tsvi Bar-David is president of Deerworks and currently a faculty member in the
  5659. Software Engineering Department at Monmouth Collge. He received his PhD in
  5660. mathematics from the University of California at Berkeley. Previously, he was
  5661. employed at Bell Labs in the development and delivery of UNIX, C++, and
  5662. Object-Oriented courses.
  5663.  
  5664.  
  5665. In my July 1989 column on training for object-oriented programming, I
  5666. presented a simple framework for object-oriented design. Today we embark upon
  5667. a journey -- likely to last several columns -- in which we apply the design
  5668. framework to the problem of constructing a simple text editor. Along the way
  5669. we will develop some types which are not only useful in building the editor,
  5670. but also as tools in general, and so can serve as members of a general-purpose
  5671. object library.
  5672. Most languages, including C++, require that the solution to a problem be
  5673. represented as a main program. This we will do. Yet, our goal is not to design
  5674. and build programs, but rather to identify and construct useful object types,
  5675. out of which we can construct an infinite number of programs.
  5676. In a sense akin to mathematics, we are constructing a solution not to just one
  5677. problem, but rather to a family of related problems -- for example, the
  5678. problem of editing text. It is precisely this approach to problem solving, I
  5679. believe, that permits an object-oriented design (design as a noun, the result
  5680. of the design process) to be easily modified, enhanced and re-used. The
  5681. brevity of the main programs that we build reflects this approach; typically
  5682. these programs instantiate an object or two and then invoke a couple of member
  5683. functions. Bertrand Meyer [2] takes and supports very much the same position
  5684. in the eiffel language. Indeed, eiffel has no main program; one simply selects
  5685. a first object to which to send a message. The action associated with that
  5686. message goes ahead and creates other objects and sends messages to them ad
  5687. infinitum.
  5688.  
  5689.  
  5690. Design Framework
  5691.  
  5692.  
  5693. You should refer to the July 1989 column for details of the design framework.
  5694. In sum, the framework manages a process that maps a requirements document to
  5695. an implementable design document.
  5696. To quote the earlier column: "The heart of object-oriented design is the
  5697. identification of the types in the program and the relationships between them.
  5698. To identify a type is to specify its behavior (public interface). To identify
  5699. relationships means bringing to light the relationships (inheritance and
  5700. parametric types) in the behavior of the types. One can then implement the
  5701. behavior in many ways."
  5702. Here is the pseudo-code for the design process.
  5703. initial decomposition(on requirements document);
  5704. while( stopping condition has not been met ) {
  5705. abstraction;
  5706. type relationships;
  5707. type decomposition;
  5708. }
  5709.  
  5710. return design specification;
  5711. In order to begin the design process, we need a behavioral description of the
  5712. object we want to build, namely the text editor.
  5713.  
  5714.  
  5715. Describing The Editor
  5716.  
  5717.  
  5718. The ced editor allows the user to create new text files or edit existing ones.
  5719. The editor views the file as just a sequence of characters (thus the 'c' in
  5720. ced) with no other structure, such as a sequence of lines. Since newline
  5721. ('\n') is just an ordinary character, we can easily recover the traditional
  5722. line structure of a file by using ordinary edit operations. In addition, the
  5723. editor maintains the notion of current point in the file. The point is
  5724. regarded as being between two characters. The notion of current point is
  5725. pretty close to the concept of current offset in UNIX files.
  5726. At this point, we have to make a requirements decision about the user
  5727. interface to the editor. For the sake of simplicity, assume that the editor
  5728. has a traditional command line interface like edlin on MS-DOS systems or ed on
  5729. UNIX systems (the input command stream looks like a sequence of lines). Each
  5730. line consists of an optional integer prefix followed by a character. The table
  5731. below associates commands with the characters that invoke them.
  5732. N.B. Bracketed arguments are optional
  5733. [n] g -- Move point to just before nth character (zero-based). Default value
  5734. for n is 0.
  5735. [n] p -- Print n characters starting at the first character after point,
  5736. followed by a newline. n defaults to 1. Increment point by n.
  5737. i -- Insert an arbitrary number of characters before point. Terminate
  5738. insertion with '.' on a line by itself.
  5739. [n] d -- Delete n characters starting at the first character after point. n
  5740. defaults to 1.
  5741. [n] y -- Paste whatever was last deleted n times just before point. n defaults
  5742. to 1.
  5743. w [file] -- Write out the internal representation of the file (the buffer) to
  5744. the named file. The primary default for file is the filename command line
  5745. argument to ced. If ced was invoked without a filename, it selects the last
  5746. file written to.
  5747. q -- Exit the editor.
  5748. ? -- Print out useful information, like filename, point and size of file.
  5749. Normally the editor scans standard input for commands. However, for
  5750. flexibility, the editor should be able to get its command stream from a file
  5751. or possibly some other source, like a string or a window.
  5752. When the editor is invoked with an argument at the command line interface,
  5753. such as
  5754. prompt> ced filename
  5755. the editor opens an existing file for editing or creates an empty file of that
  5756. name. In either case, point is located just before the first character in the
  5757. file. If the editor is called without an argument
  5758. prompt> ced
  5759. it manages an editing session. The user decides how to explicitly write the
  5760. contents to a named file.
  5761. A typical edit session might look like:
  5762.  
  5763. 36g
  5764. i
  5765. hello there
  5766. .
  5767. g
  5768. 50p
  5769. w
  5770. q
  5771.  
  5772.  
  5773. Initial Decomposition
  5774.  
  5775.  
  5776. Our task now is to identify the high-level types from the requirements, out of
  5777. which we will construct the editor. Certainly File is one of these types and
  5778. is used in two ways: as the file to be created or modified, and as the command
  5779. stream (typically standard input from the terminal).
  5780. In our description of the editor write command, we briefly mentioned the
  5781. internal representation of the file under edit, traditionally known as the
  5782. buffer. Is the Buffer type synonymous with the File type? We can answer this
  5783. question more easily once we have described (the abstraction step) the public
  5784. interface of both File and Buffer; namely, if the public interfaces (really,
  5785. the manual pages) of two types are the same, then the types are one and the
  5786. same.
  5787. At the risk of getting ahead of ourselves, let's try to answer this question
  5788. right now. Assume that a File object essentially has the semantics of a
  5789. standard I/O FILE object (as supported by the standard run-time library of the
  5790. ANSI C compiler [1]).
  5791. Files and Buffers may very well share the offset or point concept. On the
  5792. other hand, whatever a Buffer is, it must support the editor commands listed
  5793. in the requirement section, particularly insertion and deletion. Yet, there
  5794. are no native insertion and deletion operators on Files. The operation that
  5795. puts a character into a file (putc( int, FILE *)) can be considered as
  5796. inserting only when appending the file, not if the file offset is anywhere in
  5797. the middle of the file (it will overwrite the character at the offset). This
  5798. is not the behavior we are looking for.
  5799. We conclude then that a Buffer is not a File, and so we must design and
  5800. implement the Buffer abstraction. Now we may be able to implement Buffer in
  5801. terms of File (as some implementations of the full-screen editor will do), but
  5802. that is merely (yes, merely!) a matter of implementation and is not to be
  5803. confused with the behavior or semantics of the Buffer.
  5804. Is the editor itself a type? Even though giving the Editor a type may seem
  5805. unnatural at first, we will reap the benefits already mentioned.
  5806. Our design policy is clear, albeit extreme -- everything in the application is
  5807. an object of one type or another. So what is the behavior of an editor object?
  5808. An editor object interprets the command stream and performs actions both upon
  5809. a buffer and the user interface, which for now is just standard output. That
  5810. is, the editor coordinates three objects: the input (command) stream, a
  5811. buffer, and an output stream (a view of the buffer).
  5812. For simplicity of design, assume that an editor object manages precisely one
  5813. buffer, which corresponds to at most one file. I say "at most" and not
  5814. "precisely one" since the edit program ced can be invoked with no arguments.
  5815. In such a case, the program presumably contains an editor object which manages
  5816. a buffer, which currently does not correspond to any file. Later on, we will
  5817. build an edit program based upon the Editor type which manages multiple
  5818. buffers and files, something in the spirit of emacs.
  5819. Now that we have identified the object types Editor, Buffer, and File we must
  5820. now perform the design process on each of the types.
  5821. We'll start with File since it is the most familiar of the types in our
  5822. working list. But why even bother representing File as a class when all C++
  5823. compilers already support the standard I/O FILE structure? There are several
  5824. reasons:
  5825. Consistency. We want objects of all types in our application -- other than
  5826. built-in types of the language -- to be represented by classes. This provides
  5827. developers and maintainers a uniform feel of object orientation. The message
  5828. expression
  5829. object.memberfunction()
  5830. will be the sole means of communicating with an object. Using a standard I/O
  5831. function like putc( 'a', fp) directly on a FILE pointer (fp) would violate
  5832. this desideratum.
  5833. Insulation. We can regard our File type as an application-specific type
  5834. layered on top of the environments's existing I/O support. This helps to make
  5835. the editor more portable. When you port the editor to a new operating system,
  5836. only the implementation of File need change. Other code that uses File doesn't
  5837. change one iota. But we can do better. Since every C++ compiler's run-time
  5838. support library contains FILE, we can just implement/layer File on top of
  5839. FILE. Furthermore, there won't be much of a run-time penalty for this
  5840. layering, if we declare all of the member functions of File to be inline!
  5841. For File's public interface you need the five classic operations of a minimal
  5842. interface.
  5843. open -- connect the program to the named file or create it.
  5844. close -- sever the connection between the program and the file.
  5845. iseof -- returns true if at end-of-file, otherwise false.
  5846. get -- get a character and advance the file offset.
  5847. put -- put a character and advance the file offset.
  5848. We can get rid of the explicit open and close member functions elegantly by
  5849. using a constructor and destructor respectively. The advantage of this
  5850. approach is that an instantiated File object is guaranteed to be initialized
  5851. properly. Furthermore, mapping close to the destructor guarantees that when
  5852. the File object dies (goes out of scope) in the program, the associated file
  5853. in the file system is automatically closed, without the client programmer
  5854. having to explicitly close it. The public interface of File as a C++ class is
  5855. class File {
  5856. public:
  5857. File( char *name = "", char
  5858. *mode = "r");
  5859. ~File();
  5860. Truth iseof();
  5861. int get();
  5862. void put( int c);
  5863. private:
  5864. // data members
  5865. };
  5866. The constructor takes two arguments, and both are provided with defaults. Here
  5867. are the intended semantics. The declaration
  5868. File f;
  5869. invokes the default constructor File( "", "r" ), which connects the object f
  5870. to standard input for reading.
  5871. File f( filename);
  5872. invokes File( filename, "r") and so opens filename for reading.
  5873. File f( filename, mode);
  5874. opens filename with some mode (with the same semantics as fopen()). So, for
  5875. example,
  5876. File f( "foo", "w" );
  5877. opens the file foo for writing.
  5878. Before we wax too lyrical about the joys of using constructors in place of an
  5879. open function, we must face a design problem. Just after the constructor runs,
  5880. how do we know that the file is really open? If the open failed for any reason
  5881. (the file doesn't exist, we don't have the correct permissions, etc.), it
  5882. would be nonsensical to invoke any member function against the object.
  5883. One solution is to forget the constructor approach and just endow File with an
  5884. explicit open function with the following form
  5885. typedef int Truth;// boolean type
  5886. Truth File::open( char *filename, char *mode);
  5887. The open function could report success or failure of the operation in a manner
  5888. similar to the C/C++ library functions fopen() and open() ---- by returning a
  5889. boolean value (the value is regarded as boolean by convention).
  5890.  
  5891. However, for those who want to stick with the constructor approach, here is
  5892. another solution to the problem. Endow File with a member function
  5893. // returns TRUE if open succeeded in constructor
  5894. Truth File::isok();
  5895. whose sole purpose is to report on the status of the open performed in the
  5896. constructor. Perhaps a separate isok() function is unnecessary; iseof() can
  5897. report on open. But assigning this function to iseof() is bad design for two
  5898. reasons. First, checking for end-of-file is conceptually a completely separate
  5899. matter from checking to see if the open succeeded. And secondly, how are we to
  5900. interpret the return value of iseof() on a newly created file for writing? To
  5901. play it safe, we will have two predicate functions.
  5902. The File type was originally developed to support a lexical scanner object. To
  5903. make implementing the scanner easier, we included the following additional
  5904. member functions in File's public interface:
  5905. class File {
  5906. public:
  5907. ...
  5908. void unget(int c);
  5909. int peek();
  5910. ...
  5911. };
  5912. unget() pushes the character c back onto an input stream. c is the next
  5913. character get() gets. peek() returns the value of the next character without
  5914. removing it from the input stream.
  5915. As we have alluded, we can piggy-back or layer the implementation of File on
  5916. top of standard I/O FILE. One easy implementation is found in Listing 1.
  5917. The only thing difficult about this layered implementation was figuring out
  5918. that we needed a state data member for recording the status of the open. All
  5919. the member functions, with the exception of the constructor, are one-liners.
  5920.  
  5921.  
  5922. Wrap-up
  5923.  
  5924.  
  5925. In this column we have begun applying an object-oriented design framework to
  5926. the problem of constructing a text editor. Starting from a description of the
  5927. editor's behavior, we have identified three types of objects: Editor, Buffer,
  5928. and File. We discussed how File might be used by other types and let that
  5929. guide us in identifying its public interface. We then wrote a portable
  5930. implementation of File layered on top of the standard I/O FILE abstraction.
  5931. In the next column, we will continue on our journey, focusing our attention on
  5932. the Buffer abstraction. In the course of designing Buffer, we will become
  5933. acquainted with two useful parametric container types, Sloop[T] and Yacht[T],
  5934. that will make the implementation of Buffer rather simple.
  5935.  
  5936.  
  5937. Bibliography
  5938.  
  5939.  
  5940. [1] Brian Kernighan and Dennis Ritchie, The C Programming Language, second
  5941. edition, 1988, Prentice Hall.
  5942. [2] Bertrand Meyer, Object Oriented Program Construction, 1988, Prentice Hall.
  5943. (addresses object oriented design, including parametric types).
  5944.  
  5945. Listing 1
  5946. class File {
  5947. public:
  5948. File( char *name = "", char *mode = "r")
  5949. {
  5950. if( *name )
  5951. fp = fopen( name, mode);
  5952. else if( *mode == 'r' )
  5953. fp = stdin;
  5954. else
  5955. fp = stdout;
  5956. state = (int)fp;
  5957. }
  5958.  
  5959. ~File() { if( fp) fclose( fp); }
  5960.  
  5961. Truth isok() { return state; }
  5962.  
  5963. Truth iseof() { return feof( fp); }
  5964.  
  5965. int get() { return getc( fp); }
  5966.  
  5967. void unget(int c) { (void)ungetc( c, fp); }
  5968.  
  5969. int peek() { int c = get(); unget(c); return c; }
  5970.  
  5971. void put( int c) { putc( c, fp); }
  5972. private:
  5973. FILE *fp;
  5974.  
  5975. int state;
  5976. };
  5977.  
  5978.  
  5979.  
  5980.  
  5981.  
  5982.  
  5983.  
  5984.  
  5985.  
  5986.  
  5987.  
  5988.  
  5989.  
  5990.  
  5991.  
  5992.  
  5993.  
  5994.  
  5995.  
  5996.  
  5997.  
  5998.  
  5999.  
  6000.  
  6001.  
  6002.  
  6003.  
  6004.  
  6005.  
  6006.  
  6007.  
  6008.  
  6009.  
  6010.  
  6011.  
  6012.  
  6013.  
  6014.  
  6015.  
  6016.  
  6017.  
  6018.  
  6019.  
  6020.  
  6021.  
  6022.  
  6023.  
  6024.  
  6025.  
  6026.  
  6027.  
  6028.  
  6029.  
  6030.  
  6031.  
  6032.  
  6033.  
  6034.  
  6035.  
  6036.  
  6037.  
  6038. Questions & Answers
  6039.  
  6040.  
  6041. Readability, Portability, And Coding Style
  6042.  
  6043.  
  6044.  
  6045.  
  6046. Ken Pugh
  6047.  
  6048.  
  6049. Kenneth Pugh, a principal in Pugh-Killeen Associates, teaches C language
  6050. courses for corporations. He is the author of C Language for Programmers and
  6051. All On C, and is a member on the ANSI C committee. He also does custom C
  6052. programming for communications, graphics, and image databases. His address is
  6053. 4201 University Dr., Suite 102, Durham, NC 27707.
  6054.  
  6055.  
  6056. Q
  6057. I would appreciate your comments on the following questions and problems:
  6058. 1. Type char: signed or unsigned?
  6059. Most compilers consider chars as signed by default. We, European users, make
  6060. extensive use of ASCII codes above 127 and the signed chars default does not
  6061. seem to be the best choice. Which mode, in your opinion, is "better"? Why are
  6062. constant chars considered as ints? The following:
  6063. char c = 'é';
  6064. if (c == 'é')
  6065. will work only if default char is unsigned. Otherwise, a cast to (char) is
  6066. necessary to get the program to work, yet the constant Ã© is clearly a char,
  6067. not an int.
  6068. 2. Good use or abuse of #defines and typedefs?
  6069. What does one think of the current practice of #defineing or typedefing native
  6070. C types, like char into BYTE, unsigned char into BYTE or UBYTE, char * into
  6071. TEXT, int into COUNT, int into BOOL, etc.
  6072. Is there really a reason for this (except (sometimes!) for portability, of
  6073. course)? There is no such things (as far as I know) in the standard library
  6074. header files! Moreover, when strictly prototyped programs are compiled the
  6075. result is generally a long list of type mismatch errors (often pointer
  6076. mismatches between (char *) and (unsigned char *)).
  6077. 3. New C programming style
  6078. What do you think of the 'new' (?) C style programming, Ã  la PASCAL, with
  6079. (long) identifiers mixing lowercase and uppercase and banishing the
  6080. underscore?
  6081. Thanks for your opinion and sincerely yours,
  6082. Hubert Toullec
  6083. Angers, France
  6084. A
  6085. In the ANSI C committee meetings, there was considerable discussion as to
  6086. whether a particular feature of the language should be made right or whether
  6087. backward compatibility should be preserved, to avoid "breaking" existing
  6088. programs that used documented features of the language. If George Burns (in
  6089. "Oh God") remade the world from scratch, he "would make the avocados with
  6090. smaller seeds"; judging from the committee's discussion of this topic,
  6091. remaking C is much more complex.
  6092. Several features were left unchanged for the sake of backward compatibility
  6093. including the priority of the operators (even though some of the bitwise
  6094. operators could be used more comfortably if the priorities were modified).
  6095. Similarly, the type of plain chars was specifically left unchanged and thus
  6096. remains unspecified (i.e. not specifically typed as signed or unsigned). I
  6097. agree with you that unsigned chars are more useful. I sometimes use the char
  6098. type to hold small integer values, but they are usually non-negative integers.
  6099. The char data type has been converted to int since the early days of the
  6100. language. That eliminates having separate rules for character arithmetic.
  6101. Character constants should be treated the same way (signed or unsigned) as
  6102. character variables. Note that standard ASCII includes only seven bit
  6103. characters, so none of its values have the high order bit set. The C language
  6104. does not specify that programs must run if you include non-ASCII characters.
  6105. (Actually it specifies exactly which source characters are acceptable, but
  6106. that basically is the ASCII set). With your example,
  6107. char c = 'é';
  6108. if (c == 'é')
  6109. you have used a character that is not specified as being standard. The
  6110. compiler is not even obliged to compile the code. If you used the octal or
  6111. hexadecimal escape sequence to represent the character, then the compiler
  6112. would treat it as a regular character constant. I compiled with Quick-C and
  6113. ran the program in Listing 1 with one unexpected result. The results were:
  6114. Unequal -118 138
  6115. (char) Equal -118 -118
  6116. Hex Equal -118 -118
  6117. Hex (char) Equal -118 -118
  6118. Notice that the compiler treated both the char variable and the char constant
  6119. as signed. However, it treated the non-standard character as a regular integer
  6120. value. Some compilers provide a runtime switch on the interpretation of
  6121. character variables. You might try using one that has such a switch.
  6122. On your next question, I am strongly in favor of using typedefs to define
  6123. logical data types. Using typedefs is preferable to using #defines for
  6124. consistency's sake, as there are many types which cannot be described in terms
  6125. of a #define.
  6126. Declaring variables with typedefs captures a significant amount of information
  6127. for the maintenance programmer. Unfortunately the C standard, in my opinion,
  6128. does not go far enough in checking the use of typedefs. My favorite
  6129. illustration is:
  6130. typedef SPEED double;
  6131. typedef TIME double;
  6132. typedef DISTANCE double;
  6133. SPEED compute_speed(time, distance)
  6134. TIME time;
  6135. DISTANCE distance;
  6136. {
  6137. SPEED speed;
  6138. if (distance != 0.0)
  6139. speed = time / distance;
  6140. else
  6141. speed = 0.0;
  6142. return speed;
  6143.  
  6144. }
  6145. and in another program:
  6146. SPEED car_speed;
  6147. TIME car_time;
  6148. DISTANCE car_distance;
  6149. car_speed = compute_speed(car_time, car_distance);
  6150. car_speed = compute_speed(car_distance, car_time);
  6151. Under the ANSI standard, both of these function calls are compatible, but
  6152. logically one is erroneous. Some super lint or the compiler itself may one day
  6153. use the typedef information for error checking.
  6154. I agree that there is a problem with the type checking performed when
  6155. comparing or assigning unsigned char pointers and regular char pointers. This
  6156. problem is most irritating when it forces you to write the declaration as:
  6157. unsigned char *string = "ABC";
  6158. with a cast as:
  6159. unsigned char *string = (unsigned char *) "ABC";
  6160. The ANSI committee debated whether it would be okay to not require such a cast
  6161. in an initialization statement, but decided that consistency in typing was
  6162. more important.
  6163. Of course, I strongly urge using full names for the type names, e.g. BOOLEAN
  6164. instead of BOOL, etc.
  6165. On your final question, I am in favor of readable and meaningful variable and
  6166. function names. Some people may have heard of studies that conclude otherwise,
  6167. but ALongVariable-Name appears less readable to me than a_long_variable_name.
  6168. The latter appears closer to what you would expect to read in normal text.
  6169. How much you should use abbreviations in naming is an open issue. The more
  6170. abbreviations you use, the more you will have to remember and the more the
  6171. maintenance programmer will have to infer and comprehend when reading the
  6172. program. For example, XMT for transmit and TX for transaction may be common,
  6173. but does CMP stand for compare or compute?
  6174. Q
  6175. I am developing a simulation program for study of our company's manufacturing
  6176. plant using C Language compilers on IBM-PC/AT Machine.
  6177. I shall be thankful to you for sending information on various software tools
  6178. in C language for incorporating graphics in the Program.
  6179. P.K. Gupta
  6180. Gujarat, India
  6181. A
  6182. The only package with which I personally have extensive experience is
  6183. Essential Graphics by South Mountain Software, Inc., 76 So. Orange Avenue,
  6184. South Orange, NJ 07079 (201) 762-6965 ($299 list, $230 street). You can
  6185. distribute products built with Essential Graphics royalty-free, and you can
  6186. use direct coordinates (your x,y values specify an exact pixel location) or
  6187. world coordinates (your x,y values are transformed into a pixel location), the
  6188. latter at some price in speed.
  6189. The names in this package are somewhat unintelligible, since the developers
  6190. tried to stay with an eight character name. For example: grbx draws a box,
  6191. grwx draws an x at a point, hsrect draws a rectangle with a hatch style and a
  6192. label. As I mentioned above, I would prefer something like graph_box,
  6193. graph_write_x, and hatch_rectangle_with_label.
  6194. Essential Graphics also supports loading and saving PC Paintbrush .PCX files.
  6195. There are several other packages on the market, including Halo Graphics and
  6196. Advantage Graphics. Perhaps some of our readers may have comments on these or
  6197. other packages.
  6198.  
  6199.  
  6200. Reader Responses:
  6201.  
  6202.  
  6203.  
  6204.  
  6205. Commodore 128
  6206.  
  6207.  
  6208. In the May 1989 issue of The C Users Journal, I took note of the questions by
  6209. Mr. David Ockrassa regarding printing special characters such as the braces,
  6210. vertical bar, and tilde on the Commodore 128. Before I started programming the
  6211. Amiga in C, I dealt with the same problem. 
  6212. The problem is two-fold in nature. Because these characters are not in the
  6213. standard font set of the Commodore 128, the C language packages for that
  6214. machine generally include an editor that re-defines several characters bitmaps
  6215. to conform to the missing ones. These are saved with the file as a non-ASCII
  6216. byte. The problem occurs when the file is printed, because the redefined
  6217. characters may or may not have the same font set as that of the printer being
  6218. used. 
  6219. The solution is to write a small printer utility in C. The accompanying code
  6220. (Listing 2) accomplishes this task, and is available on most commercial
  6221. bulletin boards. I wrote several printer drivers of this type for the
  6222. Commodore 128 for use with different printers that have a few more features
  6223. than the included code such as pagination and filename/date headers. 
  6224. John D. Clark
  6225. St. Louis, MO
  6226.  
  6227.  
  6228. MS Dynamic Data Exchange:
  6229.  
  6230.  
  6231. This letter is in response to Ken Libert's request for material concerning MS
  6232. Dynamic Data Exchange.
  6233. If you contact Microsoft's product support services and ask for Windows
  6234. Software Development Kit support, you can request their Application Notes
  6235. concerning Dynamic Data Exchange. With this publication you get a disk
  6236. complete with examples and source.
  6237. The DDEAPP example allows you to initiate a session with Excel and actually
  6238. exchange cell data in multiple formats.
  6239. Tim Kuntz
  6240. University of Pittsburgh
  6241.  
  6242. Listing 1
  6243. main()
  6244. {
  6245. char c = 'é';
  6246. char c1 = '\x8A';
  6247.  
  6248. if (c == 'é'
  6249.  
  6250. printf("\n Equal %d %d", c, 'é');
  6251. else
  6252. printf("\n Unequal %d %d", c, 'é');
  6253. if (c == (char) 'é')
  6254. printf("\n (char) Equal %d %d", c, (char) 'é');
  6255. else
  6256. printf("\n (char) Unequal %d %d", c, 'é');
  6257.  
  6258. if (c == '\x8A')
  6259. printf("\n Hex Equal %d %d", c, '\x8A');
  6260. else
  6261. printf("\n Hex Unequal %d %d", c, '\x8A');
  6262. if (c == (char) 'é')
  6263. printf("\n Hex (char) Equal %d %d", c, (char)'\x8A'
  6264. else
  6265. printf("\n Hex (char) Unequal %d %d", c, '\x8A');
  6266. }
  6267.  
  6268.  
  6269. Listing 2
  6270. /* Printer driver for Gemini 10x */
  6271. #include <<stdio.h>>
  6272. main(argc, argv)
  6273. {
  6274. unsigned int count;
  6275. FILE infile, outfile;
  6276. char c;
  6277.  
  6278. outfile = 5;
  6279. open(outfile, 4, 7, " ");
  6280. for(count = 0; count << argc; count++)
  6281. {
  6282. infile = fopen(argv[count], "r");
  6283. while((c = getc(infile)) != EOF)
  6284. {
  6285. switch(c)
  6286. {
  6287. case '{':
  6288. c + 123;
  6289. break;
  6290. case '}':
  6291. c = 125;
  6292. break;
  6293. case '\\':
  6294. c = 92;
  6295. break;
  6296. case '~':
  6297. c = 126;
  6298. break;
  6299. case '':
  6300. c = 124;
  6301. break;
  6302. case '_':
  6303. c = 95;
  6304. break;
  6305. default:
  6306. if(islower(c))
  6307. c += 32;
  6308. else
  6309.  
  6310. c -= 128;
  6311. }
  6312. putc(c, outfile);
  6313. }
  6314. close(infile);
  6315. }
  6316. close(outfile);
  6317. }
  6318.  
  6319.  
  6320.  
  6321.  
  6322.  
  6323.  
  6324.  
  6325.  
  6326.  
  6327.  
  6328.  
  6329.  
  6330.  
  6331.  
  6332.  
  6333.  
  6334.  
  6335.  
  6336.  
  6337.  
  6338.  
  6339.  
  6340.  
  6341.  
  6342.  
  6343.  
  6344.  
  6345.  
  6346.  
  6347.  
  6348.  
  6349.  
  6350.  
  6351.  
  6352.  
  6353.  
  6354.  
  6355.  
  6356.  
  6357.  
  6358.  
  6359.  
  6360.  
  6361.  
  6362.  
  6363.  
  6364.  
  6365.  
  6366.  
  6367.  
  6368.  
  6369.  
  6370.  
  6371.  
  6372.  
  6373. New Releases
  6374.  
  6375.  
  6376. Prolog And 'Curses' Added To Library
  6377.  
  6378.  
  6379.  
  6380.  
  6381. Kenji Hino
  6382.  
  6383.  
  6384.  
  6385.  
  6386. New Releases
  6387.  
  6388.  
  6389.  
  6390.  
  6391. CUG297 -- Small Prolog
  6392.  
  6393.  
  6394. Henri de Feraudy (France) has submitted a public domain Prolog interpreter.
  6395. His Small Prolog follows a Cambridge syntax (LISP-like syntax) that has
  6396. advantages for meta-programming and small code. The Small Prolog includes most
  6397. of standard built-in (predicates) based on Clocksin and Mellish's descriptions
  6398. in Programming in Prolog, although it can be extended by creating more user
  6399. defined built-ins. The disk includes C source files, make files,
  6400. documentation, and many Prolog example files that demonstrate Prolog features
  6401. for C programmers who may be unfamiliar with Prolog. The source code is very
  6402. portable and will compile under Turbo C v1.5 and Mark William Let's C v4 on PC
  6403. clones, Mark William C v3.0 and Megamax Laser C on Atari ST and Sun C compiler
  6404. on Sun-3.
  6405.  
  6406.  
  6407. CUG298 -- PC Curses
  6408.  
  6409.  
  6410. Jeffrey S. Dean has contributed PC Curses, v0.8. This shareware release of PC
  6411. curses is a C window functions library designed to provide compatibility with
  6412. the UNIX curses package. By fully utilizing the PC features, this package is
  6413. coded much simpler than the UNIX version. For example, there is no need for
  6414. cursor motion and screen output optimization on PC. Currently, there are two
  6415. major versions of curses database under UNIX; one is termcap, the other
  6416. terminfo. However, PC curses derives primarily from the former version, with
  6417. some features of the latter version. Moreover, additional routines (not in the
  6418. original curses package) are provided for the PC user. The distribution disk
  6419. includes a couple of demo programs, Small and Large model library for
  6420. Microsoft C v5.0 and Turbo C v1.5 compilers, and documentation that describes
  6421. all the functions in the library. The source code is obtained by paying a $20
  6422. fee directly to the author.
  6423.  
  6424.  
  6425. Updates
  6426.  
  6427.  
  6428.  
  6429.  
  6430. CUG220 -- Window BOSS
  6431.  
  6432.  
  6433. Phillip A. Mongelluzzo (CT) from Star Guidance Consulting has submitted
  6434. Revision 07.01.89 of The Window Boss. This release provides additional data
  6435. entry routines along with support for user-defined physical sizes (i.e. 43 and
  6436. 50 line EGA/VGA screen sizes).
  6437.  
  6438.  
  6439. CUG198 -- MicroEmacs Source
  6440.  
  6441.  
  6442. Willam Bader has extensively updated a text editor, MicroEmacs v3.9. His
  6443. update includes not only bug fixes of the old version, but also additional
  6444. commands, portability improvement, and performance enhancement.
  6445. The new feautures of MicroEmacs are built-in emulation DEC EDT editor support
  6446. for VT100/VT200 keypads, function keys and scrolling regions, better VMS
  6447. support such as filter-buffer command and preservation of record format
  6448. attributes, extra commands such as insert a C format octal escape sequence,
  6449. scroll the screen horizontally, callable interface of Emacs (you can call
  6450. Emacs as a function), VMS subshell routines, support for ANSI color, BINARY
  6451. mode for MS-DOS, pull-down menu, and more.
  6452. The enhancements include a faster search routine, faster lookup for normal
  6453. keys and FNC macro, faster display routine.
  6454. Bader has tested the new version of MicroEmacs using the following compilers
  6455. and operating systems:
  6456. VAX11c under VMS4.1 on VAX-11/750, Microsoft C 5.0 under MS-DOS 3.20, Turbo C
  6457. 1.5 under MS-DOS 3.20, CI86 2.30J under MS-DOS 3.20, Microsoft C under XENIX
  6458. 386, cc under SunOS 3.5 on Sun 3/360C, cc under SunOS 4.0 on Sun 386i, cc
  6459. under BSD 2.9 on PDP-11/70.
  6460. In order to create an executable code for your environment, you need to turn
  6461. on/off the switches of Machine/OS definitions, Compiler definitions, Terminal
  6462. Output definitions, and Configuration options in the header file, estruct.h.
  6463. The distribution setting is to compile under MS-DOS using Turbo C.
  6464.  
  6465.  
  6466.  
  6467.  
  6468.  
  6469.  
  6470.  
  6471.  
  6472.  
  6473.  
  6474.  
  6475.  
  6476.  
  6477.  
  6478.  
  6479.  
  6480.  
  6481.  
  6482.  
  6483.  
  6484.  
  6485.  
  6486.  
  6487.  
  6488.  
  6489.  
  6490.  
  6491.  
  6492.  
  6493.  
  6494.  
  6495.  
  6496.  
  6497.  
  6498.  
  6499.  
  6500.  
  6501.  
  6502.  
  6503.  
  6504.  
  6505.  
  6506.  
  6507.  
  6508.  
  6509.  
  6510.  
  6511.  
  6512.  
  6513.  
  6514.  
  6515.  
  6516.  
  6517.  
  6518.  
  6519.  
  6520.  
  6521.  
  6522.  
  6523.  
  6524.  
  6525.  
  6526.  
  6527.  
  6528.  
  6529.  
  6530.  
  6531.  
  6532.  
  6533.  
  6534.  
  6535. On The Networks
  6536.  
  6537.  
  6538. How To Get Net Software
  6539.  
  6540.  
  6541.  
  6542.  
  6543. Sydney S. Weinstein
  6544.  
  6545.  
  6546. Sydney S. Weinstein, CDP, CCP is a consultant, columnist, author and President
  6547. of Datacomp Systems, Inc., a consulting and contract programming firm
  6548. specializing in databases, data presentation and windowing, transaction
  6549. processing, networking, testing and test suites and device management for UNIX
  6550. and MS-DOS. He can be contacted care of Datacomp Systems, Inc., 3837 Byron
  6551. Road, Huntingdon Valley, PA 19006-2320.
  6552.  
  6553.  
  6554. First, an introduction, and a thank you. I am the new "Contributing Editor" of
  6555. the "On The Networks" column. I have written before for The C Users Journal
  6556. so, hopefully I won't be a total stranger to you. And, as David Fiedler said
  6557. in the last CUJ, I am the Elm coordinator. (Elm, itself, is a large piece of
  6558. freely distributable software.) I can be reached at syd@DSI.COM, for those
  6559. with Internet access, or at {bpa, vu-vlsi}!dsinc!syd for those without
  6560. Internet access.
  6561. I don't plan any change in the scope or content of this column. I will attempt
  6562. to report on the latest freely distributable software available on Usenet and
  6563. the Internet. Also as David did, I am willing to forward a list of neighboring
  6564. sites for access, provided you send me a self-addressed, stamped envelope. If
  6565. you have net access but need a news neighbor, I will reply to electronic mail
  6566. asking for nearby news sites.
  6567. To David Fiedler, a well earned thank-you for his two year tenure in this
  6568. spot. Many megabytes of useful software were highlighted here. His tireless
  6569. attempts to find neighbors for those sites that requested it, is also
  6570. gratefully appreciated. It was with his help that our site found its first
  6571. news neighbor several years ago. However, I highly doubt I can keep up with
  6572. his run of puns.
  6573.  
  6574.  
  6575. Some Definitions
  6576.  
  6577.  
  6578. For the past two years, the terms Usenet, Internet, internet, and "the net"
  6579. have been bantered about in this column. I would like to add a new one:
  6580. "freely distributable software." Some definitions are in order.
  6581. Usenet, often times referred to as "the net" is a loose collection of
  6582. cooperating computers. In the past, all of Usenet ran UNIX, but now with other
  6583. computers and operating systems supporting UUCP, hosts could be running
  6584. anything from MS-DOS to VAX/VMS. All that is required to be considered a
  6585. computer on Usenet is that you communicate via the UNIX to UNIX Communications
  6586. Protocol (UUCP) to another computer. Usenet consists of electronic mail, file
  6587. transfers, and network news. It is via network news that most of the programs
  6588. you read about in this column are distributed.
  6589. If your computer talks to Usenet or to another computer via some protocol
  6590. other than UUCP, you are considered to be on an internet (lower case "i"),
  6591. short for inter-network. This just means that you are using some network other
  6592. than the UUCP-based Usenet. This generic internet includes "the Internet" and
  6593. several other networks such as CSNET and BITNET. The actual connection to
  6594. Usenet is via a gateway computer that talks to both the network you use and
  6595. Usenet.
  6596. The Internet (capital "I") is the computer network loosely managed by the
  6597. Network Information Center at SRI. The Internet is a collection of networks
  6598. that grew out of the Defense Department's ARPANET (Advanced Projects Research
  6599. Agency Network). Usenet sites make phone calls to other computers; the
  6600. Internet is mostly machines connected with dedicated leased lines. These lines
  6601. usually run faster than the dial-up lines used by UUCP. The Internet has many
  6602. sub-networks associated with it, including NSFNET, the National Science
  6603. Foundation Networks. These newer networks run at much higher speeds and
  6604. currently also pick up a lot of the long distance traffic for Usenet's Network
  6605. News. In my area, the local NSFNET related network is called PREPnet and has a
  6606. backbone consisting of 1.544Mb/s (million bit per second) data links and each
  6607. site either has a 1.544Mb/s or a 56kb/s (thousand bit per second) hookup to
  6608. the network. The main backbone NSFNET is now all 1.544Mb/s data links and is
  6609. quickly upgrading to 45Mb/s data links as they become available. Whereas only
  6610. mail and news is usually available over the Usenet via UUCP, the Internet runs
  6611. the TCP/IP protocol and supports news (NNTP, Network News Transfer Protocol),
  6612. mail (SMTP, Simple Mail Transfer Protocol), remote logins to any computer on
  6613. the network provided you have an account there (telnet), and remote file
  6614. transfer (FTP, file transfer protocol), and many other services. All of these
  6615. services coexist and work in real time.
  6616. The problem with the Internet providing much of the bulk transfers for Usenet
  6617. is that they use two different addressing methods. Since a large amount of the
  6618. software mentioned in this column comes from Usenet or the Internet, you'll
  6619. need to understand how to format the two types of addresses. A UUCP or Usenet
  6620. address is made up of site names separated by exclamation points, as in
  6621. bpa!dsinc!syd. If a site wants to mention more than one "well-known site" to
  6622. use as a route, it usually lists them in curly braces as in {bpa,
  6623. vu-vlsi}!dsinc!syd (meaning you can use either bpa!dsinc!syd or
  6624. vu-vlsi!dsinc!syd). Such addresses assume that you know the complete path from
  6625. your site to one of the named "well-known sites". Some systems run programs to
  6626. help with this routing, and Usenet's UUCP Mapping Project publishes maps to
  6627. automate this process. However, not all sites have registered to be listed in
  6628. these maps. Registration is free and accomplished by sending your entry to
  6629. rutgers!uucpmap. The maps are continuously updated and distributed via the
  6630. comp.mail.maps news group.
  6631. On the Internet, all sites have a unique "Fully Qualified Domain Name" which
  6632. is administered by the NIC. My site's domain name is node.DSI.COM, where node
  6633. is the individual computer at my site. Thus, my full current address is
  6634. syd@dsinc.DSI.COM, but our mailer, like the mailers at a lot of Internet
  6635. sites, is smart and knows how to forward the mail to me even if you send it to
  6636. syd@DSI.COM. This allows me to move around within the DSI.COM domain without
  6637. having to tell everyone a new address. The Internet does not require users to
  6638. know the complete path to the site; it is sufficient to know the domain name.
  6639. Now a word of warning. Mixing both @ and ! in the same address leads to
  6640. trouble. Not everyone follows the standard and processes the addresses
  6641. correctly. Converting sitea!user@DSI.COM to a UUCP address would produce
  6642. dsinc!sitea!user. Note that the @ has higher precedence than the !. Many sites
  6643. get this wrong, causing your mail to bounce (be returned to you as
  6644. undeliverable). Some sites, ours included, allow UUCP mail to have addresses
  6645. including domain names in the ! path, as in dsinc!host.domain.type!user. Where
  6646. allowed, this convention is usually more reliable than mixing the ! and @s.
  6647. Lastly, what is Public Domain Software and what is Freely Distributable
  6648. Software? Much of the software described in this column is free in that no
  6649. licensing fee is required for personal users. In some cases even commercial
  6650. users aren't required to pay a licensing fee. However, almost all of the
  6651. software mentioned in this column is not in the Public Domain. For software to
  6652. be in the Public Domain, either the copyright must expire (and not be renewed)
  6653. or the authors must specifically renounce copyright protection. The copyright
  6654. to most software mentioned in this column is reserved by the author or some
  6655. group. Though the copyright is reserved, the holders have given the user the
  6656. right to use and distribute the software without fee. This does not place the
  6657. software in the public domain. You still cannot sell this software nor pretend
  6658. that you wrote it. Many of the licensing agreements restrict how the software
  6659. can be used for business purposes. Freely Distributable Software is also
  6660. different from Shareware. Shareware expects (but doesn't require) the user to
  6661. pay a fee if they intend to continue using the program. Freely distributable
  6662. software does not.
  6663. Now, how do you get the software mentioned in this column? Much of the
  6664. software mentioned in this column is distributed in Usenet's network news,
  6665. especially in the comp.sources.unix or the comp.sources.misc news groups. Game
  6666. software is in the comp.sources.games group. There are also groups for amigas,
  6667. atari sts, macs, suns, and computers running the X windowing system. The
  6668. Usenet news groups are distributed via a store-and-forward broadcast from
  6669. Usenet neighbor to Usenet neighbor either via UUCP or NNTP. However, news
  6670. articles are kept online at a particular site for only a short period of time,
  6671. usually less than two weeks. By the time a piece of software appears in this
  6672. column, it will have been expired and deleted for a long time.
  6673. Thus, it is necessary to access a news archive site. Many sites around the
  6674. country have agreed to archive specific news groups. These sites are listed in
  6675. the comp.archives news group. Many sites are also identified as archive sites
  6676. in their Usenet Mapping Project map entry. Some have even been listed in this
  6677. column. These sites allow access to their archives to retrieve the sources.
  6678. How one accesses the archives depends on where they are and how that site has
  6679. set up access. Most archives are available for either FTP or UUCP access and a
  6680. few even allow both.
  6681. If a site supports FTP access, you need to be on the Internet to access them.
  6682. FTP allows for opening up a direct connection to the FTP server on their
  6683. system and transferring the files directly to your system. FTP will prompt for
  6684. a user name and optionally a password. Most FTP archive sites allow a user
  6685. name of anonymous. If it then prompts for a password, any password will work,
  6686. but convention and courtesy dictate that you use your name and site address
  6687. for the password.
  6688. If a site supports UUCP access, anyone with UUCP can access the archives. Most
  6689. sites of this type publish a sample entry for the Systems (L.sys) file showing
  6690. the system name, phone number of their modems, the connection speeds
  6691. supported, and the login sequence. Using the uucp command, one can poll the
  6692. system directly and retrieve the software. Many sites post hour restrictions
  6693. on when you should access the modems. Courtesy dictates that you follow their
  6694. requests, and some sites enforce the limit with programs. Be sure to call far
  6695. enough before the end of the period to complete your transfer in time.
  6696. A third method, used for smaller files, allows access to an electronic
  6697. mail-based archive server. With these sites, you send an electronic mail
  6698. message to the archive server's mailbox name specifying the files you wish.
  6699. The files are then returned to you via electronic mail. Remember that many
  6700. sites have a limit on the size of a single mail message, so don't ask for too
  6701. much at once. Also remember that the archive server is a program, so phrase
  6702. your request exactly as specified in the instructions for that archive server,
  6703. and limit your message to exactly that request. Other comments in the message
  6704. could confuse the program and it might not honor your request.
  6705. Lastly, for those sites not connected to any network, some sites will copy the
  6706. software onto your media if you send them a disk or tape along with return
  6707. postage and a mailer. Other sites sell media with the software already copied
  6708. onto it. This is especially useful for the largest distributions, such as the
  6709. X windowing system, which runs multiple tapes.
  6710. For those sites without Internet access but who do subscribe to UUNET, UUNET
  6711. will retrieve the files via FTP for you and make them available for UUCP
  6712. access.
  6713.  
  6714.  
  6715. And to come...
  6716.  
  6717.  
  6718. Starting in February, back to more new software from Usenet's source
  6719. newsgroups and news from the Internet and public access sites. If you have an
  6720. archive of UUCP-accessible software and would like even more accesses to it,
  6721. drop me a note via electronic mail and I'll try to get it into an upcoming
  6722. column. Until then, a slight paraphrase of David's tag line: see you on the
  6723. nets!
  6724.  
  6725.  
  6726.  
  6727.  
  6728.  
  6729.  
  6730.  
  6731.  
  6732.  
  6733.  
  6734.  
  6735.  
  6736.  
  6737.  
  6738.  
  6739.  
  6740.  
  6741.  
  6742. PC-METRIC -- A Measuring Tool For Software
  6743.  
  6744.  
  6745. Larry Versaw
  6746.  
  6747.  
  6748. Larry Versaw is a systems engineer at Electronic Data Systems' Corporate
  6749. Communications Division. His 1984 masters thesis was entitled Measuring the
  6750. Size, Structure, and Complexity of Software. He may be contacted care of 5400
  6751. Legacy Drive, Plano, TX 75024.
  6752.  
  6753.  
  6754. Have you ever wanted to compare the complexity of two programs or to tell how
  6755. long it took to develop them? Have you ever needed a precise measure of
  6756. programmer productivity? No one yet can produce truly reliable answers to
  6757. these problems, but researchers in the 1970s invented many software metrics
  6758. and have since conducted hundreds of experiments to see what information could
  6759. be derived by analyzing program source code. Some metrics purport to measure
  6760. software complexity; others gauge program size or calculate how well
  6761. structured a program is. The researchers developed many static code analyzers
  6762. for use in their software metrics experiments, but few such tools were
  6763. commercially marketed. PC-METRIC, developed by SET Laboratories, Inc., is one
  6764. of the few stand-alone software metrics programs, if not the only one, said
  6765. today.
  6766. To evaluate PC-METRIC, I tried it out on 80 source files containing 25,000
  6767. lines of working C code. That exercise proved PC-METRIC to be a reliable
  6768. product, an efficient program measurement tool that would be indispensable to
  6769. anyone wishing to use software metrics in his work.
  6770.  
  6771.  
  6772. The Product
  6773.  
  6774.  
  6775. For this article I evaluated v1.1, then v2.3 of PC-METRIC. In addition to the
  6776. C language versions which I examined, SET Laboratories has produced metrics
  6777. programs for Ada, Assembler, COBOL, FORTRAN, Modula-2, and Pascal. Some
  6778. languages are supported on systems other than MS-DOS.
  6779. PC-METRIC specializes in static code analysis; that is, it reports certain
  6780. quantifiable attributes of program source code without executing it. These
  6781. attributes include number of source lines, number of executable statements,
  6782. and a dozen other quantities which are derived by counting certain program
  6783. elements. Software metrics experiments have usually shown correlations between
  6784. these kinds of metrics and actual, observed software management factors, such
  6785. as programmer skill, number of remaining bugs, and actual programming effort.
  6786. PC-METRIC is based on the work of several of the pioneers in software metrics,
  6787. notably Tom McCabe and Maurice Halstead. McCabe [McCabe 1976] proposed a
  6788. measure of program control flow complexity based on a program's directed
  6789. control flow graph. This metric, called cyclomatic complexity, may be
  6790. calculated as one plus the number of branches (if statements, loops,
  6791. alternatives in case statements) in a program. PC-METRIC reports two variants
  6792. of cyclomatic complexity for each function it analyzes. McCabe's metric is
  6793. widely accepted and intuitively satisfying as a complexity measure because it
  6794. represents the amount of program logic that must be understood and retained to
  6795. understand an algorithm.
  6796. One of the most imaginative and ingenious models of software, including
  6797. software size, was developed by the late Professor Halstead [Halstead 1977].
  6798. Halstead's system, labeled software physics, is ultimately based on counts of
  6799. operators and operands in program source code. Several of PC-METRIC's metrics,
  6800. including length, estimated length, purity ratio, volume, the effort metric,
  6801. estimated time to develop, and estimated errors, are implementations of
  6802. Halstead's software physics formulae. Some have seriously questioned the
  6803. theoretical basis underlying Halstead's model, and Halstead's attempt to bring
  6804. theory from the realm of psychology to bear on software development has been
  6805. widely discounted [Coulter 1981, Perlis 1981]. On the other hand, some rather
  6806. impressive correlations have been observed between certain of Halstead's
  6807. metrics and such management factors as code quality, programming time, and
  6808. debugging effort [Gordon 1979, Curtis 1979, Funami 1976, Paige 1980].
  6809. If you are experienced with software metrics, you may find some of your
  6810. favorite metrics missing from PC-METRIC's repertoire. However, PC-METRIC
  6811. supports more measures of program size and complexity than are actually
  6812. needed. Most size and complexity metrics are highly correlated with each
  6813. other, so that beyond the first two or three, additional size and complexity
  6814. metrics are redundant. In a study which analyzed great quantities of source
  6815. code written by diverse programmers in C, Ada, PL/I and Pascal, no
  6816. statistically significant differences were found among the reliability of
  6817. different size metrics [Versaw 1984]. They all measure the same attribute,
  6818. after all. Variations in programming style notwithstanding, it is my belief
  6819. that lines of code remains as good a measure of program size as any other
  6820. measure we have today, and is almost as good a measure of complexity as any
  6821. other. Research continues on the subject, but on a smaller scale than ten
  6822. years ago.
  6823. Installing and using PC-METRIC is simplicity itself. A user must learn only
  6824. one command, CMET, which runs interactively or in batch mode. PC-METRIC is
  6825. configurable to different dialects of C, by modifying a table of key words and
  6826. symbols, which is stored in an ASCII text file.
  6827. As PC-METRIC analyzes source code, it produces two reports. The complexity
  6828. analysis report lists metrics values calculated for each function, and the
  6829. combined values for the entire module being considered. In the new version of
  6830. PC-METRIC, SET has remedied the worst problem with version 1.1, which was its
  6831. inability to analyze units of source code larger than one file. The second
  6832. report file, called the exceptions report, highlights all measured values
  6833. which lie outside of predetermined, user-defined limits.
  6834. Both the analysis report and exceptions report are output as ASCII files. In
  6835. the current version of PC-METRIC, these reports are suitable for printing
  6836. without any manual editing or reformatting. The current version of PC-METRIC
  6837. provides a CONVERT utility which can convert the report data into a
  6838. comma-delimited text file suitable for uploading into many spreadsheet or
  6839. database packages. This is an especially valuable addition to the PC-METRIC
  6840. package.
  6841. Program attributes which cannot be measured by simply counting certain
  6842. operators and identifiers are beyond the scope of PC-METRIC, unfortunately.
  6843. These would include attributes such as the degree of information hiding,
  6844. module coupling, function binding, and efficiency. If we could only measure
  6845. these attributes objectively and automatically, it would greatly enhance the
  6846. practice of software engineering. Where PC-METRIC does excel is in calculating
  6847. reliably the most common size and complexity metrics with a minimum of fuss at
  6848. a reasonable speed (4000 lines per minute on a 10 MHz AT type computer).
  6849.  
  6850.  
  6851. System Requirements
  6852.  
  6853.  
  6854. PC-METRIC requires far less memory and disk space than any C compiler would,
  6855. so hardware requirements do not limit the use of PC-METRIC.
  6856.  
  6857.  
  6858. The Audience
  6859.  
  6860.  
  6861. PC-METRIC is intended primarily for two kinds of users. The first is software
  6862. developers who would use a statistical analysis of their code as a help in
  6863. identifying overly complex modules or functions. The PC-METRIC manual
  6864. correctly identifies programmer feedback as an important application of
  6865. PC-METRIC. The second kind of person who needs PC-METRIC is the manager or
  6866. software project leader who would use software metrics as an tool to monitor
  6867. programmer compliance to local standards of function size, module complexity,
  6868. or other quantifiable program aspects.
  6869.  
  6870.  
  6871. Documentation
  6872.  
  6873.  
  6874. All bases are covered in PC-METRIC's three-part manual. Part 1 provides a
  6875. well-written tutorial on the field of software metrics, concentrating on the
  6876. specific metrics obtainable with PC-METRIC. It even includes a brief annotated
  6877. bibliography of software metrics literature. Users with little prior exposure
  6878. to the field of software metrics should be sure to read this part.
  6879. Part 2 describes how to install, configure, and use PC-METRIC. It also is
  6880. well-organized and gives the right amount of examples. PC-METRIC's counting
  6881. strategy is documented toward the end of this section.
  6882. Part 3, "Applying PC-METRIC", instructs users on what to do with all those
  6883. numbers PC-METRIC generates. It first documents the indispensable new CONVERT
  6884. utility mentioned above. Then it explains ways to interpret the results: how
  6885. to properly use software measures as a feedback tool or resource estimation
  6886. tool, in practice.
  6887.  
  6888.  
  6889. Support
  6890.  
  6891.  
  6892. SET Labs offers technical support by telephone for their products and will
  6893. answer general questions on software metrics. SET offers site licensing as
  6894. well as individual licenses. If you have a particular machine or language for
  6895. which you would like a version of PC-METRIC, SET Labs will usually do a port
  6896. for the price of a single site license.
  6897.  
  6898.  
  6899. Conclusions
  6900.  
  6901.  
  6902. PC-METRIC is an indispensable tool, and perhaps the only tool in its class,
  6903. for analyzing program size and complexity by those software metrics it
  6904. provides. By cleaning up the reports and by providing the CONVERT utility, the
  6905. new version of PC-METRIC has enhanced users' ability to analyze and apply
  6906. program metrics.
  6907.  
  6908. PC-METRIC applies state of the art methods for objectively measuring two basic
  6909. attributes of program source code: size and complexity. The usefulness of
  6910. these measures is variable, but not because of any deficiency in PC-METRIC
  6911. itself. PC-METRIC, and counting programs in general, find their surest
  6912. application in measuring adherence to a specific coding standard.
  6913. I recommend PC-METRIC for programmers and managers as a tool for monitoring
  6914. adherence to their coding standard which could, and probably should, include
  6915. some complexity metrics. I recommend it also as a tool for identifying overly
  6916. complex modules that need extra testing or rewriting. The list price is $199.
  6917. You can contact SET Labs for more information at P.O. Box 86327, Portland, OR
  6918. 97283 (503) 289-4758.
  6919. References
  6920. Coulter, Neal S., Applications of Psychology in Software Science, Proceedings
  6921. of IEEE COMPSAC 81, (1981), 50-51.
  6922. Curtis, Bill; Sheppard, Sylvia; and Milliman, Phil, Third Time Charm: Stronger
  6923. Prediction of Programmer Performance by Software Complexity Metrics,
  6924. Proceedings of Fourth International Conference on Software Engineering,
  6925. (1979), 356-360.
  6926. Funami, Y., and Halstead, M.H., A Software Physics Analysis of Akiyama's
  6927. Debugging Data, Proceedings of the Symposium on Computer Software Models,
  6928. (1976), 133-138.
  6929. Gordon, Ronald, A Quantitative Justification for a Measure of Program Clarity,
  6930. IEEE Transactions on Software Engineering, IV (March 1979), 121-128.
  6931. Halstead, Maurice, Elements of Software Science, New York, Elsevier, 1977.
  6932. McCabe, T.J., A Complexity Measure, IEEE Transactions on Software Engineering,
  6933. II (December 1976), 308-320.
  6934. Paige, M., A Metric for Software Test Planning, Proceedings of IEEE COMPSAC
  6935. 80, (1980), 499-504.
  6936. Perlis, Alan J.; Sayward, Frederick G.; and Shaw, Mary, editors, Software
  6937. Metrics: An Analysis and Evaluation, Cambridge, Massachusetts, MIT Press,
  6938. 1981.
  6939. Versaw, Larry, A Tool for Measuring the Size, Structure and Complexity of
  6940. Software, thesis, Denton, Texas, North Texas State University, 1984.
  6941.  
  6942.  
  6943.  
  6944.  
  6945.  
  6946.  
  6947.  
  6948.  
  6949.  
  6950.  
  6951.  
  6952.  
  6953.  
  6954.  
  6955.  
  6956.  
  6957.  
  6958.  
  6959.  
  6960.  
  6961.  
  6962.  
  6963.  
  6964.  
  6965.  
  6966.  
  6967.  
  6968.  
  6969.  
  6970.  
  6971.  
  6972.  
  6973.  
  6974.  
  6975.  
  6976.  
  6977.  
  6978.  
  6979.  
  6980.  
  6981.  
  6982.  
  6983.  
  6984.  
  6985.  
  6986.  
  6987.  
  6988.  
  6989.  
  6990.  
  6991.  
  6992. GRAD Graphics Library
  6993.  
  6994.  
  6995. Ron Burk and Helen Custer
  6996.  
  6997.  
  6998. Ron Burk has a BSEE from the University of Kansas and has been a programmer
  6999. for the past 10 years. He is currently president of Burk Labs, a small
  7000. software consulting firm.
  7001.  
  7002.  
  7003. Helen Custer holds degrees in Computer Science, English, and Psychology from
  7004. the University of Kansas and is currently a Senior Software Technical Wrter
  7005. for a Fortune 500 company. She has coauthored books on C, GW-BASIC, and
  7006. Z-BASIC.
  7007.  
  7008.  
  7009. Both may be contacted at Burk Labs, P.O. Box 3082; Redmond, WA 98073-3082.
  7010.  
  7011.  
  7012. The GRAD Graphics Library, written by Conrad Kwok, is a shareware package for
  7013. drawing simple graphics images, including circles, lines, ellipses, arcs, and
  7014. rectangles. It can also fill regions, display characters, and dump screen
  7015. graphics to your printer. The 50-odd graphics functions are carefully
  7016. documented in a 100-page users manual.
  7017. The functions are written for PC/XT/AT clones using Microsoft C v4.0. The
  7018. package is written in Microsoft C and 8088 assembly language. GRAD can also be
  7019. compiled with Turbo C, and directions for doing so are included with the
  7020. disks; a fewer minor changes are required.
  7021. GRAD supports both CGA (640 x 200) and HGA (720 x 348) graphics cards, but
  7022. unfortunately, it only supports one device at a time. You link with the
  7023. library that corresponds to the device you want to use; there is no
  7024. auto-detect of the graphics card adaptor. The routines are modularized in such
  7025. a way that it may be possible to make them work with other graphics devices by
  7026. changing one or two files of source code. However, a graphics device is not
  7027. absolutely necessary, as GRAD allows you to define up to nine virtual graphics
  7028. screens at run time.
  7029. GRAD also supports several printers, including the Epson FX-80, the Okidata
  7030. ML192, and compatibles, or laser printers using the JLASER card. You can also
  7031. configure other printers to work with GRAD.
  7032. The GRAD user's manual and assorted documentation files thoroughly document
  7033. the functions that are available in GRAD. The writing is friendly and, in
  7034. addition to GRAD, the files document a number of concepts relating to graphics
  7035. libraries in general. It describes how fonts are viewed by a graphics package,
  7036. how to use a graphics coordinate system, and what a virtual graphics screen
  7037. is, among other things. Example code is provided for functions that are
  7038. difficult to describe.
  7039.  
  7040.  
  7041. Pixels Vs. Lines
  7042.  
  7043.  
  7044. The graphics screen on most personal computers is pixel-oriented; it is made
  7045. up of dots that you can turn on or off. A pen plotter, on the other hand, is
  7046. line-oriented; everything it draws is made up of line segments. The GRAD
  7047. library is oriented towards pixel graphics. For example, it supports the
  7048. ability to "grab" a rectangular portion of the screen and transfer it to
  7049. another part of the screen. That sort of operation could not be implemented
  7050. with a pen plotter.
  7051. You could, however, use GRAD as a PC device driver for a more general set of
  7052. line-oriented library functions. GRAD supplies almost all of the primitives
  7053. you would need for such a project. Also, most printers support pixel graphics,
  7054. so they can serve as hard-copy devices for programs that use pixel graphics.
  7055. If your printer is similar to the Epson FX-80 or the Okidata ML192, you can
  7056. adapt the software to work with your printer. An appendix at the back of the
  7057. manual documents that process. In the general case, however, you may have to
  7058. buy the source code from the author to make GRAD work with your printer.
  7059.  
  7060.  
  7061. Standard Transformations
  7062.  
  7063.  
  7064. In some kinds of graphics, you find yourself drawing the same basic symbol in
  7065. slightly different ways (different proportions, different locations on the
  7066. screen, and so on). Three types of transformations of graphics are commonly
  7067. supported by high-level graphics libraries:
  7068. Translation--Moving a graphic element (a square, for example) to a new
  7069. location.
  7070. Scaling--Making a graphic element appear shorter and fatter, or taller and
  7071. thinner.
  7072. Rotation--Turning a graphic element around an axis.
  7073. GRAD supports graphics translation by allowing you to change the value for the
  7074. upper left corner, or "origin", of your frame. For example, if you wish a
  7075. graphic element to appear multiple times in your final drawing, you can create
  7076. a subroutine that draws the element, then call that subroutine multiple times.
  7077. Between calls to the subroutine, you simply change the origin for the element.
  7078. GRAD does not support graphics scaling or rotation. If you want to draw the
  7079. same symbol with different heights or widths, you must implement the scaling
  7080. with your own code. Likewise, if you want to rotate a graphic image so that it
  7081. appears sideways or upside-down, you'll have to write your own code to do
  7082. this.
  7083. One reason you might want to do a transformation such as scaling is to solve
  7084. the problem of aspect ratio. Aspect ratio is the ratio of a pixel's height to
  7085. its width. GRAD assumes that each pixel is square, the same height as width.
  7086. However, on a typical CGA monitor, each pixel is rectangular instead of
  7087. square, that is, its aspect ratio is not 1:1.
  7088. The aspect ratio problem becomes very clear when you ask GRAD to draw a circle
  7089. on a CGA monitor. It draws a true circle, but because the pixels are not
  7090. square, the result on the screen is a "stretched" circle (an ellipse). In a
  7091. line-based graphics library, this problem can be solved by applying the
  7092. appropriate scaling transformation just before translating the line into
  7093. pixels. In GRAD, however, there isn't much you can do except take the problem
  7094. into account in your code and draw a rectangle to get a square, an ellipse to
  7095. get a circle, and so on.
  7096.  
  7097.  
  7098. Virtual Screens
  7099.  
  7100.  
  7101. A virtual screen is just like the real screen in every way--you just can't see
  7102. it. Suppose you want your graphics program to have the ability to undo the
  7103. last drawing request the user made. One way to accomplish the visual part of
  7104. this task is to use a virtual screen. For each user request that is not an
  7105. undo request, you first perform the previous request on the virtual screen,
  7106. then perform the new request on the real screen. If the request is an undo,
  7107. you could simply copy the virtual screen to the real screen.
  7108. GRAD provides virtual screens which it calls "frames". A frame is a
  7109. rectangular memory area where a graphic image is stored. If the memory area
  7110. corresponds to video memory, then the graphic is visible on the screen. If the
  7111. memory area is regular memory, the frame is a virtual graphics screen. A
  7112. graphic image created in this area can only be seen by dumping it to the
  7113. printer or by copying it to the video memory. Frames are especially useful for
  7114. windowing operations, as described in the following section.
  7115.  
  7116.  
  7117. Drawing Attributes
  7118.  
  7119.  
  7120. GRAD allows you to specify line styles and writing modes. Normally, when you
  7121. draw a line across the screen, you get a solid line. A line style, however,
  7122. allows you to specify that all lines are dotted lines, or dashed lines, or
  7123. almost any pattern of dots and dashes you like.
  7124. Another drawing attribute that GRAD lets you specify is the writing mode. On a
  7125. pen plotter, a line is a line--you can never erase an existing line. On a
  7126. graphics screen, however, there are several interesting possibilities.
  7127. Usually, you want the screen to look like it would on a pen plotter. This is
  7128. called OR mode, since it is accomplished by bitwise ORing the pixels to be
  7129. drawn with the screen pixels' existing value. GRAD also supports an XOR mode
  7130. and an AND mode.
  7131. The XOR mode can be used to "erase" lines, because if you draw a line in OR
  7132. mode and then redraw the line in XOR mode, the line disappears. This isn't
  7133. perfect, however; if there is a second line on the screen that intersects the
  7134. first one, it will have a "hole" in it, because the pixel where the two lines
  7135. intersected is turned off. You can also use XOR mode to achieve a kind of
  7136. reverse-video effect, by turning on a block of pixels, switching to XOR mode,
  7137. then drawing on the block.
  7138. Drawing lines in AND mode doesn't make much sense, because the only pixels
  7139. that will get turned on are those that were already on. In other words, it
  7140. will look as though nothing got drawn. AND mode is useful for Bit-Block
  7141. Transfers, however.
  7142. Bit-Block Transfers, or bitblts (pronounced "bitblits"), are at the heart of
  7143. windowing systems that operate in graphics mode. For example, moving a window
  7144. from one place to another is a bitblt operation; so is removing a window
  7145. (copying a block of background pattern to it). GRAD provides basic bitblt
  7146. operations that allow you to transfer blocks between virtual screens and to
  7147. and from files. GRAD's bitblt operations obey the current writing mode, so you
  7148. can combine the block transfers with the bit-wise modes to do things like
  7149. erase a window or cause a window to appear in reverse-video.
  7150.  
  7151.  
  7152.  
  7153. Clipping
  7154.  
  7155.  
  7156. Clipping is the ability to restrict graphics output to a specific (usually
  7157. rectangular) region of the screen. For example, if you are using an inch-high
  7158. strip along the bottom of the screen to display status information about your
  7159. program, you want to ensure that no other part of your output strays into that
  7160. area. If your graphics library supports clipping, you can define a clipping
  7161. rectangle. Your program can then continue to draw anywhere it likes, but only
  7162. that portion of the drawing that lies within the clipping region appears.
  7163. GRAD allows you to specify a single, rectangular clipping region called a
  7164. "window". There is no on/off function to disable or enable the defined
  7165. clipping region. Instead, GRAD supplies a ResetWin() function that redefines
  7166. the clipping region to be the entire virtual screen (which effectively turns
  7167. clipping off).
  7168.  
  7169.  
  7170. Drawing Text
  7171.  
  7172.  
  7173. Whether you are drawing business charts or flowcharts, you inevitably need to
  7174. display text along with your graphics. There are two general ways to draw text
  7175. in graphics mode on a pixel-oriented device. Bitmapped fonts are the kind you
  7176. normally see in text mode on a PC screen. As the name implies, they are
  7177. defined in terms of a set of bits that are on or off, each bit corresponding
  7178. to a pixel of the overall character. Bitmapped fonts are easy to define and
  7179. fast to display, but difficult to scale up and down in size, and difficult to
  7180. clip except on character boundaries. Stroke fonts, on the other hand, are
  7181. stored as line segments and, therefore, can usually be scaled up and down in
  7182. size, stretched in any direction (to form slanted text, for example), and even
  7183. rotated to arbitrary angles.
  7184. GRAD has no stroke fonts but supports bitmapped fonts. These fonts can be
  7185. stored on files and loaded into memory dynamically, as needed. This is useful
  7186. when you want to use many fonts, but don't want to consume a lot of memory.
  7187. You can get the effect of rotated fonts (you can get one of four, 90-degree
  7188. rotations) by using a specially rotated font file. There are 18 font files on
  7189. the GRAD disk. Although most of these are variations on a couple of fonts,
  7190. they provide good examples of what you can do. You can also make bitmapped
  7191. fonts that have variable width. This looks more professional, especially with
  7192. larger fonts.
  7193. GRAD also supplies a graphics input function that reads from the keyboard.
  7194. This is very handy when you need to query the user while you are drawing
  7195. graphics, since you will want the keyboard input to be echoed on the screen
  7196. with graphic text. Remember that just calling gets() probably won't produce
  7197. the desired result when the screen is in graphics mode.
  7198.  
  7199.  
  7200. Picture Segments
  7201.  
  7202.  
  7203. If you are writing a program that allows the user to manipulate the graphics
  7204. drawn on the screen, you may want to provide a way for them to control units
  7205. of the picture more complicated than individual pixels or lines. For example,
  7206. the user of an architectural program may want to move an entire wall
  7207. (including windows and doors) as a unit. Picture segments support this type of
  7208. operation. A picture segment is just a sequence of drawing commands that you
  7209. can store, retrieve, and use to draw the same object in a variety of places on
  7210. the screen. GRAD does not provide picture segments as such, but it defines a
  7211. draw() function which is a step in that direction.
  7212. draw() takes three arguments: a C string containing drawing commands and two
  7213. integer arguments that can be used to parameterize the commands in the string.
  7214. The key feature of the graphics commands that you store in the string is that
  7215. they are relative to the current drawing coordinate. For example, here is a
  7216. command that draws a rectangle at the current location.
  7217. Draw("RT10 DN5 LF10 UP5", 0, 0);
  7218. It always draws the same size rectangle; however, it could be parameterized
  7219. like this:
  7220. Draw("RT%OX DN%OY, LF%OX, UP%OY", 3, 10);
  7221. In this case, the arguments to draw() alter the symbol that is specified in
  7222. the command string.
  7223. Notice that you could build up command strings, save them to files, and bring
  7224. them back later -- just as you would use a symbol library. GRAD just supplies
  7225. the basic command string ability, though. You would have to design your own
  7226. functions to manage a symbol library.
  7227.  
  7228.  
  7229. Graphics Environment
  7230.  
  7231.  
  7232. If you were going to implement a symbol library, you would want the drawing of
  7233. symbols to be modular. The symbol might draw in a different line style,
  7234. graphics mode, or font, or use a different clipping window than the calling
  7235. routine. A modular library would ensure that each symbol routine resets all
  7236. these attributes back to their original values after the symbol is drawn.
  7237. Fortunately, there is an easier solution. GRAD groups attributes like the
  7238. current origin, clip region, line style, font, and so on, into a bundle which
  7239. it calls an environment. The modular symbol or graphics routine can simply
  7240. save the current environment before it begins drawing, and restore it after
  7241. all its graphics operations are complete.
  7242.  
  7243.  
  7244. Utility Programs
  7245.  
  7246.  
  7247. The GRAD disk contains several utility programs, which Conrad Kwok wrote as
  7248. sample programs for the library. The first program, Interp, is an interpreter
  7249. for GRAD library functions. You can place a series of GRAD function calls in a
  7250. file, then give that file name as an argument to Interp. Interp interprets the
  7251. graphics commands and draws the resulting graphic on the screen. This is a
  7252. fast way to experiment with the library, since you don't have to recompile
  7253. anything to make changes to what you're drawing.
  7254. The input to the interpreter mimics the analagous C functions. Whenever a
  7255. particular function returns a value, you can simply write:
  7256. var1 = function(val1, val2, ...)
  7257. The variable that you name (in this case, var1) is created and initialized by
  7258. the value returned by the function. Similarly, for functions that return
  7259. values through pointers, you can type something like this:
  7260. function (&var1, &var2, ...)
  7261. The variable names available for use are hard-coded in the program, but the
  7262. source to the interpreter is supplied, so you could easily extend it. Listing
  7263. 1 shows a sample input file which draws an ellipse around the text "The C
  7264. User's Journal".
  7265. MPrint (Merge Print) is a variation of Interp. MPrint allows you to specify a
  7266. file containing lines of text that are merged with the graphics drawn by the
  7267. interpreter. In other words, you can print graphics in graphics mode on the
  7268. printer and print the text portions in text mode, which is much faster than
  7269. printing text in graphics mode.
  7270.  
  7271.  
  7272. Distribution and Licensing
  7273.  
  7274.  
  7275. The GRAD graphics library is a good, basic, integer graphics system. It
  7276. contains a complete set of primitives which could be used as a base for a more
  7277. sophisticated graphics package, a floating-point package, for example. The
  7278. main disadvantage of the library is that it is not written for multiple
  7279. graphics adaptors. You must compile the library for a specific adaptor.
  7280. Conrad Kwok, GRAD's author, is distributing this graphics package as
  7281. shareware. If you find his program useful, he requests that you send a
  7282. contribution of $20 to him. If you send $20 or more, you will receive updates
  7283. to the library. If you send a contribution of $60 or more, you will get the
  7284. source for the latest version of GRAD, as well as a programmer reference
  7285. manual which documents the internal data structures and algorithms used in the
  7286. library. The source is copyrighted.
  7287. The licensing terms for GRAD are as follows:
  7288. You may freely copy and distribute the GRAD library and related programs
  7289. provided the documentation and sample programs are not modified in any way.
  7290. However, you may write additions to the library and distribute those along
  7291. with the original library.
  7292. You may not charge a fee for distributing the library or your enhancements to
  7293. it. However, you may charge a small fee for the cost of the disk, shipping,
  7294. and handling.
  7295. Your program must be in the public domain and must contain a message
  7296. indicating that it contains code from GRAD, written by Conrad Kwok.
  7297. If your program does not meet the above requirements, you must get written
  7298. permission from Conrad Kwok before distributing it.
  7299.  
  7300.  
  7301.  
  7302.  
  7303.  
  7304.  
  7305.  
  7306.  
  7307.  
  7308.  
  7309.  
  7310.  
  7311.  
  7312.  
  7313.  
  7314.  
  7315.  
  7316.  
  7317.  
  7318.  
  7319.  
  7320.  
  7321.  
  7322.  
  7323.  
  7324.  
  7325.  
  7326.  
  7327.  
  7328.  
  7329.  
  7330.  
  7331.  
  7332.  
  7333.  
  7334.  
  7335.  
  7336.  
  7337.  
  7338.  
  7339.  
  7340.  
  7341.  
  7342.  
  7343.  
  7344.  
  7345.  
  7346.  
  7347.  
  7348.  
  7349.  
  7350.  
  7351.  
  7352.  
  7353.  
  7354.  
  7355.  
  7356.  
  7357.  
  7358.  
  7359.  
  7360.  
  7361.  
  7362.  
  7363.  
  7364. Publisher's Forum
  7365. To show our appreciation for your readership and to commemorate The C Users
  7366. Journal's second anniversary, we've bound a combination calendar and reference
  7367. card into this issue. P.J. Plauger prepared the reference card. It summarizes
  7368. calling conventions for Standard C library functions. Susan, our staff artist,
  7369. prepared the calendar. We hope you find at least one side useful.
  7370. This issue begins our third year of publishing The C Users Journal. It also
  7371. marks our first issue on a monthly publication cycle.
  7372. Two years ago, when we first combined The C Journal and The C Users Group
  7373. Newsletter, the Journal was 72 pages and went to 6800 subscribers. This issue
  7374. of 144 pages will be distributed to over 23,000 subscribers; another 5400
  7375. copies will go to newsstand distributors.
  7376. The magazine and related activities now employ 16 persons -- up from about ten
  7377. a year ago. To accommodate this extra staff, we've just moved into larger
  7378. quarters, about two blocks from our old office. (We're all moved in, but we're
  7379. not yet 100 percent functional. There are still little things missing -- like
  7380. my terminal, and Donna's doorknob, and Kenji's return air vent, and ...)
  7381. We think all these signs are cause to celebrate. (Well, maybe all but the
  7382. moving ... that's pretty traumatic.) Since it's your interest in C that has
  7383. stimulated this activity, we wanted to share the celebration with you.
  7384. Unfortunately, it's difficult to coordinate a celebration involving over
  7385. 23,000 persons scattered around the globe. We considered mailing you each a
  7386. party favor with instructions about when to toot your whistle, but the
  7387. reference card seemed more practical. If nothing else, we're always practical.
  7388. (Personally, I'm celebrating by trying to catch up on some lost sleep.)
  7389. We hope you like the card. We offer it with our heartfelt gratitude; thanks
  7390. for reading the magazine, thanks for writing for the magazine, and thanks for
  7391. advertising in the magazine. We'll be doing our best to earn your continued
  7392. participation.
  7393. Sincerely yours,
  7394. Robert Ward
  7395. Editor/Publisher
  7396.  
  7397.  
  7398.  
  7399.  
  7400.  
  7401.  
  7402.  
  7403.  
  7404.  
  7405.  
  7406.  
  7407.  
  7408.  
  7409.  
  7410.  
  7411.  
  7412.  
  7413.  
  7414.  
  7415.  
  7416.  
  7417.  
  7418.  
  7419.  
  7420.  
  7421.  
  7422.  
  7423.  
  7424.  
  7425.  
  7426.  
  7427.  
  7428.  
  7429.  
  7430.  
  7431.  
  7432.  
  7433.  
  7434.  
  7435.  
  7436.  
  7437.  
  7438.  
  7439.  
  7440.  
  7441.  
  7442.  
  7443.  
  7444.  
  7445.  
  7446.  
  7447. New Products
  7448.  
  7449.  
  7450. Industry-Related News & Announcements
  7451.  
  7452.  
  7453.  
  7454.  
  7455. Oasys Offers Green Hills C++
  7456.  
  7457.  
  7458. Oasys, Inc. has introduced the Green Hills C++ compiler, which supports cross
  7459. and native mode development. Green Hills C++ is integrated with the Oasys
  7460. 680x0 and 88000 Cross Tool Kits, enabling embedded systems developers to take
  7461. advantage of object-oriented techniques. Green Hills C++ supports Kernighan
  7462. and Ritchie C and complies with ANSI C standard.
  7463. Green Hills C++ provides object oriented programming features such as data
  7464. abstraction, strong type checking, and overloading of function names and
  7465. operators. New C++ features include classes with scope, and overloading new
  7466. and arrow operators. Green Hills C++ also includes compiler optimizing
  7467. techniques such as inlining, loop unrolling and register caching.
  7468. Green Hills C++ compiler is available from Oasys on the Sun-3. Oasys claims
  7469. that the compiler will be ported to other UNIX workstation and minicomputers
  7470. soon.
  7471. Oasys supports Designer C++, the C++ translator developed by Glockenspiel,
  7472. Ltd. Oasys will provide current customers with the ability to upgrade to the
  7473. Green Hills C++ compiler.
  7474. For more information contact Oasys at 230 Second Ave., Waltham, MA 02154 (617)
  7475. 890-7889; FAX (617) 890-4644.
  7476.  
  7477.  
  7478. Library Brings UNIX Functions To Hercules Card Users
  7479.  
  7480.  
  7481. Certified Scientific Software has announced a subroutine package that allows
  7482. programmers using most PC-based UNIX systems to take full advantage of
  7483. Hercules-type monochrome graphics adapters. The package includes the standard
  7484. UNIX plot(3) subroutines plus many enhancements, such as patterned fills of
  7485. circles, rectangles and user-defined shapes; two fonts -- 8x8 pixel and 8x16
  7486. pixel -- for labels; clipping windows; five pixel write-modes, including
  7487. bit-set, bit-clear and exclusive-or; and routines to support double buffering
  7488. using the Hercules adapter's two graphics pages, making animation effects
  7489. possible.
  7490. The subroutines use only integer code, so they will run efficiently whether or
  7491. not floating-point hardware is installed. A 10-page manual and demonstration C
  7492. code is included.
  7493. The package is currently available for Interactive Systems 386/ix; AT&T Sytem
  7494. V/386; Microport System V/AT; XENIX 286 v2.2/2.3 and 386 v2.3; and VENIX
  7495. v2.3/2.4.
  7496. A single-user license is priced at $99, plus $2 shipping and handling. The
  7497. subroutines may be licensed for incorporation in programs for resale by
  7498. special arrangement.
  7499. For more information or a review copy, contact Certified Scientific Software,
  7500. P.O. Box 802168, Chicago, IL 60680 (312) 326-6098. Send e-mail to:
  7501. UUCP:{seismo,harpo,ihnp4, linus,allegra}!harvard!certif!herc
  7502. INTERNET:certif!herc@ harvard. harvard.edu
  7503.  
  7504.  
  7505. Screen Manager Professional Updated To Version 1.5B
  7506.  
  7507.  
  7508. Logical Alternatives, Inc. has released version 1.5B of the Screen Manager
  7509. Professional for C programmers. The S.M.P. is a tool box of over 150
  7510. pre-written functions for complex windowing, menu generation and interactive
  7511. context sensitive help features.
  7512. To maximize performance and minimize memory overhead, the windowing functions
  7513. are written in assembly language. The smallest possible program size using the
  7514. S.M.P. functions is approximately 7K. The menu system, on the other hand, is
  7515. written in C, providing flexibility and allowing the programmer to customize
  7516. the function.
  7517. Other features include: keyboard filtering for data entry systems, OS and
  7518. compiler independence, full video support, background processing,
  7519. reconfigurable memory allocation, and a 300-page ring bound manual. This
  7520. product also includes an event driven mouse support system, which makes S.M.P.
  7521. comparable to a text-based Microsoft Windows programming interface. Full
  7522. technical support is available including a new bulletin board for professional
  7523. programmers: The LAB (814) 234-1881.
  7524. The introductory price for S.M.P. v1.5B is $250, (with source code, $350).
  7525. Screen Manager Professional supports Microsoft C, Borland's Turbo C, Watcom C,
  7526. Lattice C, and Zortech C++.
  7527. For more information contact Donald McCandless, Marketing Director, Logical
  7528. Alternatives, Inc., Calder Square, P.O. Box 10674, State College, PA 16805
  7529. (814) 234-8088, BBS: (814) 234-1881, FAX: (814) 234-6864.
  7530.  
  7531.  
  7532. TE Version 3.0 Announced
  7533.  
  7534.  
  7535. Sub Systems, Inc. has released TE Developer's Kit v3.0. The new version
  7536. includes a TES small window editor routine. An application program can utilize
  7537. TES without programming changes to the routine. The application program passes
  7538. a set of parameters which specifies the window coordinates, maximum file size
  7539. and an input buffer or an input file. The output is either a buffer or a file.
  7540. The TES routine supports screen scrolling functions, word-wrapping, and block
  7541. commands. It requires 60K of memory and supports Microsoft and Borland C
  7542. compilers. The package includes the complete source code.
  7543. This version of TE Developer's Kit retains TE text editor source code and
  7544. library routines from the earlier version. The package lists for $125. For
  7545. more information contact Sub Systems, 159 Main St. #8C, Stoneham, MA 02180
  7546. (800) 447-6819 or (617) 438-8901.
  7547.  
  7548.  
  7549. Powerline Updates Source Utilities
  7550.  
  7551.  
  7552. Powerline Software, Inc. has released new versions of their programming
  7553. utilities Source Print v4.0 and Tree Diagrammer v3.0.
  7554. Powerline has added graphics drivers to support over 400 printers. These new
  7555. features include support for many printers (including laser printer), support
  7556. for C, Pascal, and dBASE from a variety of language development companies.
  7557. Both Source Print (a source code formatting utility) and Tree Diagrammer (an
  7558. "organizational chart" diagrammer) are software tools for all PC programmers
  7559. coding in C, C++, dBASE, Pascal, BASIC, FORTRAN, and Modula-2.
  7560. For more information contact Powerline Software Inc. at their new address: 826
  7561. Douglass Street, San Francisco, CA 94114 (415) 346-8325.
  7562.  
  7563.  
  7564. Emulator Mimes Xenix Console
  7565.  
  7566.  
  7567.  
  7568. Hansco Information Technologies, Inc. has released its new terminal emulator
  7569. system, HIT/Ansi.
  7570. HIT/Ansi is a memory-resident program for MS-DOS compatible computers that
  7571. emulate the Xenix color console. The program may be called up while running
  7572. any MS-DOS application with a hot key so that the computer functions as a
  7573. terminal to a host Xenix machine. When the hot key is pressed again, the
  7574. computer returns to MS-DOS and to whatever program was running.
  7575. Using less than 48K of RAM, HIT/Ansi supports color (CGA, EGA, and VGA) or
  7576. monochrome systems, 12 function keys and local printers in the foreground or
  7577. background through the parallel port.
  7578. A descriptive brochure and demonstration diskette for the product are
  7579. available upon request.
  7580. For more information contact Hansco Information Technologies, Inc., 185 West
  7581. Ave., Ste. 304, Ludlow, MA 01056 (800) 548-9754 or (413) 547-8991.
  7582.  
  7583.  
  7584. Saber And TI Join Efforts
  7585.  
  7586.  
  7587. Saber Software, Inc., developer of Saber-C has announced a joint software
  7588. development agreement with Texas Instruments, Inc.
  7589. Engineering teams from both companies are using Saber-C for cooperatively
  7590. developing new software technology that will be used in software products TI
  7591. and Saber plan to introduce in the future.
  7592. Texas Instruments will also use Saber-C widely for its own internal
  7593. development projects. Saber-C runs on UNIX, Sun Microsystems Sun-3, Sun-4, Sun
  7594. 386i and SPARCstation workstations. Saber-C is also available for DEC's
  7595. VAXstation, and Ultrix.
  7596. For more information, contact Saber Software, Inc., 185 Alewife Brook Parkway,
  7597. Cambridge, MA 02138 (617) 876-7636; FAX (617) 547-9011.
  7598.  
  7599.  
  7600. Watcom Ships v7.0 For 386 Hosts
  7601.  
  7602.  
  7603. Watcom is now shipping the Watcom C v7.0/386 optimizing compiler and run-time
  7604. library for the Intel 80386 architecture. Already available for the 16-bit
  7605. MS-DOS environment with the 80X86 processors, Watcom C v7.0 is now available
  7606. for the 32-bit 80386 processor.
  7607. Watcom C v7.0/386 ports MS-DOS applications to 32-bit native mode, enabling
  7608. full 386 performance without 640K limitations.
  7609. Watcom C v7.0/386 generates code for 32-bit protect mode and can access large
  7610. data areas without source modification or special compiler options. Watcom C
  7611. v7.0 possesses 386-specific instructions, sophisticated addressing modes and
  7612. 32-bit linear addresses. Porting to the 386 architecture involves recompiling
  7613. existing programs and linking with the 386 library to enable addressing of up
  7614. to 4 gigabytes of memory.
  7615. Applications compiled with Watcom C v7.0/386 operate with MS-DOS extenders
  7616. which enable use of 80386 protect mode. Both the 80386 software tools from
  7617. Phar Lap Software and OS/386 from A.I. Architects support use of Watcom C
  7618. v7.0/386 32-bit protect-mode with MS-DOS.
  7619. Watcom C v7.0/386 includes the compiler run-time library, a "compile and link"
  7620. utility, and Touch utilities, an object file disassembler, a patch utility,
  7621. and the Watcom C Preprocessor.
  7622. The list price for Watcom C v7.0 /386 is $895. For more information, contact
  7623. Watcom at 415 Phillip Street, Waterloo, Ontario, Canada, N2L 3x2 (519)
  7624. 886-3700, FAX (519) 747-4971, or call the Watcom C order and inquiry line toll
  7625. free: (800) 265-4555.
  7626.  
  7627.  
  7628. Sterling Castle Offers Logic Gem In Single Language Versions
  7629.  
  7630.  
  7631. Sterling Castle is shipping a "single language edition" of Logic Gem v1.5, its
  7632. logic processor and code generator.
  7633. This edition includes one of BASIC, FORTRAN, Pascal, dBase and C, plus English
  7634. for documenting procedures, writing pseudocode, and building rule bases for
  7635. expert systems. The products are identical except that one programming
  7636. language choice appears in the language menu instead of five.
  7637. LogicGem includes an editor, interpreter and compiler and runs on PC, XT, AT,
  7638. PS/2 or compatibles. LG requires 640K of RAM, PC/MS-DOS 2.0 or greater and can
  7639. be used with a color or monochrome monitor. LG's "Programmer's Edition"
  7640. complete with documentation has a suggested retail of $99. The single language
  7641. edition, sold only directly from Sterling Castle, is $49.95 with complete
  7642. documentation and on 3.5" or 5.25" disks. The full purchase price of the
  7643. single language edition is applicable against a later purchase of the
  7644. multi-language programmer's edition.
  7645. There is a 90-day money-back guarantee, free technical support and 24 hour
  7646. bulletin board service. Upgrades to v1.5 are free to registered users.
  7647. Contact Sterling Castle, 702 Washington St., Ste. 174, Marina Del Rey, CA
  7648. 90292. Inside CA (213) 306-3020 or (800) 323-6406; Outside CA (800) 722-7853;
  7649. FAX (213) 821-8122.
  7650.  
  7651.  
  7652. CI Adds Profiler To QNX
  7653.  
  7654.  
  7655. Computer Innovations has added a new utility which provides statistical
  7656. profiling of a program to the Computer Innovations C86 C Compiler for QNX. The
  7657. profiler points out parts of the program that use the most CPU time, done in
  7658. terms of source file constructs that the programmer can easily relate to: by
  7659. module, function, or line number.
  7660. The profiler is currently included with the C86 C Compiler package, and is
  7661. available for downloading (by registered C86 users) from the Computer
  7662. Innovations Bulletin Board Update System.
  7663. For more information contact Computer Innovations, Inc., 980 Shrewsbury Ave.,
  7664. Tinton Falls, NJ 07724 (201) 542-5920.
  7665.  
  7666.  
  7667. Spell Checker Works With C
  7668.  
  7669.  
  7670. Geller Software Laboratories, Inc. has introduced SpellCode, a spell checker.
  7671. SpellCode works with C, Pascal, BASIC, databases and Lotus spreadsheets as
  7672. well as dBase and all work-alike interpreters and compilers.
  7673. SpellCode includes a comprehensive English dictionary and a special dictionary
  7674. of common computer terms. The user can also create as many customer
  7675. dictionaries as needed.
  7676. It is available from Geller Software Laboratories, Inc., 35 Stephen St.,
  7677. Montclair, NJ 07042 for a special introductory price -- $49.95. For more
  7678. information call (201) 746-7402.
  7679.  
  7680.  
  7681. MetaWare Available On SystemV/386
  7682.  
  7683.  
  7684. MetaWare's High C compiler will be offered on the Santa Cruz Operation (SCO)
  7685. and AT&T UNIX System V/386 operating system.
  7686. The High C compiler features over a dozen different global optimizations,
  7687. including global allocation of values to registers, removal of invariant
  7688. expressions from loops, live/dead analysis, dead code elimination, and
  7689. constant and copy propagation.
  7690.  
  7691. MetaWare's High C compiler also features a code generator that makes use of
  7692. 386/387 instruction sets including support of in-line transcendentals and
  7693. floating-point long doubles (80 bits). The code generator also features
  7694. in-line intrinsic function; in certain cases, the compiler replaces a call to
  7695. the C library with the actual in-line instructions, resulting in code that is
  7696. smaller and performs fewer operations.
  7697. The High C compiler provides ANSI compatibility, cross-language calling,
  7698. acccurate and helpful diagnostics, and maximum configurability. Developers can
  7699. select from a wide variety of compiler features through the use of toggles and
  7700. programs.
  7701. MetaWare supports the complete Intel 80x86 microprocessor family including the
  7702. 8086, 80186, 80286, 80386, and 80486, and the Intel i860; Advanced Micro
  7703. Devices' Am29K; Sun Microsystem's Sun386i, Sun-3, and Sun-4 workstations;
  7704. Motorola's 680x0 family of processors; IBM's PS/2, RT, and 370; and DEC's VAX.
  7705. Operating system support includes UNIX 4.x BSD, UNIX System V.x, SunOS, IBM's
  7706. AIX, DEC's Ultrix, MS/PC-DOS, OS/2, DRI's FlexOS, AIA's OS/286 & 386, Phar
  7707. Lap's 386DOS-Extender, DEC's VMS, and others. Most platforms are supported
  7708. with native and cross compilers.
  7709. For more information contact MetaWare Incorporated, 2161 Delaware Avenue,
  7710. Santa Cruz, CA 95060-5706 (408) 429-6382; FAX (408) 429-9273.
  7711.  
  7712.  
  7713. FairCom Announces Update For c-tree File Handler
  7714.  
  7715.  
  7716. FairCom has announced c-tree File Handler/Server v4.3, which provides
  7717. functions to store, update and retrieve fixed or variable length data in
  7718. random or sequential order. c-tree comes with source code and employs portable
  7719. client/server architecture.
  7720. The new version has a high speed sorted key load routine enabling virtually
  7721. linear time index creation regardless of the number of index entries. Another
  7722. function returns the key value at an approximate given percentile of the
  7723. ordered key value list. The new version also estimates the number of entries
  7724. between two key values.
  7725. c-tree v4.3 has new make files and scripts for OS/2, Watcom, MPW v3.0 and
  7726. Commando tool support for all of MPW. There is server support for LightSpeed C
  7727. on the Mac and server/client support for Turbo C. Reuse of depleted nodes in
  7728. single-user and c-tree Server modes of operation is possible.
  7729. Version 4.3C lists at $395 (plus shipping and handling). To order contact
  7730. FairCom Corp, 4006 W. Broadway, Columbia, MO 65203, (800) 234-8180 FAX (314)
  7731. 445-9698.
  7732.  
  7733.  
  7734. Coromandel Releases C-Trieve For MS-Windows Environment
  7735.  
  7736.  
  7737. Coromandel has announced the release of its C-Trieve-ISAM file manager for
  7738. MS-Windows. C-Trieve/Windows, now shipping, is based on the X/Open standard.
  7739. It also runs under MS-DOS, XENIX, UNIX and DESQview. C-Trieve can be used by
  7740. both C and C++ programmers.
  7741. C-Trieve/Windows is a library of routines that allows the programmer to build
  7742. custom data management applications. C-Trieve/Windows is based on a
  7743. Client-server model. A single server can support multiple clients and maintain
  7744. application integrity using locking and transactions.
  7745. C-Trieve/Windows is based on C-Trieve which is the native file manager of
  7746. Coromandel's RDBMS, C-SQL. The current offering includes dBase and Btrieve.
  7747. C-Trieve users can upgrade to C-SQL and continue to use their files; no need
  7748. exists to translate or modify the data for SQL access.
  7749. For more information contact Coromandel Industries, Inc., 108-27, 64th Road,
  7750. Forest Hills, NY 11375 (718) 997-0699; FAX (718) 997-0793.
  7751.  
  7752.  
  7753. Eigenware Tech Offers CSL Buyer's Guide
  7754.  
  7755.  
  7756. Eigenware Technologies now has available a 45 page buyer's guide for the C
  7757. Scientific Programming Library. This guide provides a description of the CSL
  7758. product and several other related products and services.
  7759. These other products include compilers, editors, technical monograph, and TeX
  7760. typesetting software used for CSL documentation.
  7761. Detailed ordering and international shipping information is also supplied in
  7762. the buyer's guide. The guide is available for $5 from Eigenware Technologies,
  7763. 13090 La Vista Drive, Saratoga, CA 95070. For more information call (408)
  7764. 867-1184.
  7765.  
  7766.  
  7767. QuickGeometry Receives Upgrade
  7768.  
  7769.  
  7770. Building Block Software has released QuickGeometry Library v1.01, a collection
  7771. of math subroutines for developing CAD/CAM, parametric design, NC programming,
  7772. post processing, finite element analysis or other similar programs.
  7773. The major enhancements are the addition of support for Turbo C, and internal
  7774. changes that simplify interfacing to graphics libraries.
  7775. The QuickGeometry Library provides CAD/CAM programmers with routines for
  7776. standard geometric operations required for CAD/CAM software development. In
  7777. addition, the QuickGeometry Library provides routines that read and write DXF
  7778. files, and that manage lists.
  7779. Selling for $199, the product includes source code, object code for MS-DOS,
  7780. extensive documentation, working example programs, one hour of telephone
  7781. support and a 30-day money-back guarantee.
  7782. For more information contact Building Block Software, PO Box 1373, Somerville,
  7783. MA 02144 (617) 628-5217.
  7784.  
  7785.  
  7786.  
  7787.  
  7788.  
  7789.  
  7790.  
  7791.  
  7792.  
  7793.  
  7794.  
  7795.  
  7796.  
  7797.  
  7798.  
  7799.  
  7800.  
  7801.  
  7802.  
  7803.  
  7804.  
  7805.  
  7806.  
  7807. We Have Mail
  7808. [Editor's Note: Yes, we omitted the listing from last month's letters column.
  7809. It appears as a separate article in this issue, Dealing With Memory Allocation
  7810. Problems. -- rlw]
  7811. Dear Sir,
  7812. It has been many years since I sent a letter to a periodical, some 16 or 17
  7813. years to be precise. I have some 22 years of programming background, ranging
  7814. from systems programming to applications and telecommunications. As the
  7815. original designer and author of SHADOW (IBM mainframe telecommunications
  7816. system), and co-designer of MANAGE-IMS I feel I can speak with some
  7817. experience.
  7818. I mention my background not to attempt to impress, but to add some weight to
  7819. my words about the latest fad in the C world. C++.
  7820. When C was first inflicted on us I welcomed it and disliked it, however, two
  7821. facts stand out. First, K&R are undoubtedly very bright people with much
  7822. insight. Secondly, ANSI cleaned up the loose ends and now C is a serious
  7823. commercial language. C is now one of the four that the IBM SAA endorses. I
  7824. have written in C since 1982, using MVS, UNIX and the micro versions.
  7825. Many years ago we in the mainframe world discovered the benefits of control
  7826. blocks, pointers and vector tables. In fact the control block structure of any
  7827. dynamic operating system is, no ifs, no ors, no buts about it, is an object
  7828. oriented programmed system. This "new" concept of O.O.P. (object oriented
  7829. programming) is what worries me.
  7830. First, it is not new. We have used object oriented systems for all the 22
  7831. years I have been in the industry. I have a fear that OOP will become OOPS. I
  7832. feel that as far as C goes, C++ is violating the cardinal rule "IF IT ISN'T
  7833. FIXED, DON'T BREAK IT"!
  7834. I have studied OOP systems, the new window systems are OOP, and on the whole
  7835. well done. They exist without the ?benefit? of C++. As is stands, C supports
  7836. objects very well. I have an example in the C language forum of Compuserve,
  7837. complete in and of itself for anyone who cares to study it. In short, C++ is a
  7838. farce. C++ I feel was implemented by some well intentioned people who have no
  7839. serious commercial programming expertise, and certainly no IBM mainframe
  7840. internals experience.
  7841. C++ is a random collection of items, a mixed bag of minor changes, and the OOP
  7842. extension. The minor additions attack the heart of structured programming (for
  7843. example allowing data to be defined anywhere code may exist). They had some
  7844. good ideas, existing for a quarter of a century in the mainframe world, such
  7845. as defaults. Yet the defaults are positional as opposed to pure keyword! When
  7846. keyword parameters are introduced into functions and macros then a whole new
  7847. world is opened up. C++ felt it was better to stick to methods flying in the
  7848. face of good mainframe experience and thus limit its abilities. The data
  7849. reference, the change to casting, the inline functions are questionable at
  7850. best, and ignore the potential increase in power of the processor and the
  7851. optimisation ability of future compilers. Programmers are made to get involved
  7852. with optimisation, not the machine.
  7853. Overloaded functions I admit are a benefit. They are the base of the C++
  7854. object implementation. I ask myself if that benefit isn't perhaps the only
  7855. benefit of C++. The object oriented side of C++ does nothing, except
  7856. inheritance, that any C compiler today can do. And if serious preprocessors
  7857. were defined with global symbols then inheritance can also be implemented.
  7858. What I am saying is that rather than C++, let us have a full preprocessor with
  7859. typical mainframe abilities, and skip the rest. C was designed to be bare
  7860. bones, enhanced (very successfully) with functions. The quantum jump should be
  7861. a preprocessor and proper macro and language preprocessor such as the IBM
  7862. assembler macro facility. The next quantum leap is not the poorly thought out
  7863. ideas of C++. In creating an object based system, much thought has to go into
  7864. the structure, and this is true whether C++ is inheritance and scope, easily
  7865. controlled in other ways if C is used, employing run time inheritance and
  7866. binding.
  7867. I am getting suspicious that perhaps AT&T felt it was losing control of its
  7868. brilliant child, "C" and needed to show that perhaps they were still in the
  7869. lead. I suspect that since OOP was becoming more the rage that they jumped on
  7870. the bandwagon. They used that to reestablish their leadership. The C++ authors
  7871. wanted to become the next generation of venerated programmers, to be the next
  7872. K & R. I am sorry, but as senator Benson put it, "they are no K&R".
  7873. OOP was not invented by AT&T, it is a long established method for handling
  7874. interrupt and interrupt driven systems. The resurgence of OOP came about with
  7875. among other things the need to handle the dynamic world of dynamic objects
  7876. such as in windowing systems and the like. OOP is a good discipline where
  7877. applicable. It has many uses in the distributed processing world of the
  7878. future.
  7879. I hope that the readers will take a closer look at C++ and study some OOP
  7880. systems implemented in C and realise that C++ is a farce, a joke being
  7881. perpetrated on the data processing world. I am all for positive change, this
  7882. isn't it. I am recommending to my company that C++ not be implemented. I note
  7883. that there will be no ANSI C++, they have seen the light.
  7884. I thank you for your patience,
  7885. Simon Wheaton-Smith
  7886. 2902 N. Manor Dr. West
  7887. Phoenix, AZ 85014
  7888. You're welcome to my patience, but not to any support for your position.
  7889. I wonder if K&R had any IBM mainframe internals experience? If not, perhaps we
  7890. should make them rescind C? -- rlw
  7891. Dear CUJ,
  7892. Please allow me to introduce myself. My name is Chris Proctor. I'm an IBM
  7893. mid-range systems contractor. I felt compelled to write you a letter to tell
  7894. you why I would not be renewing my C Users Group subscription.
  7895. I am relatively new to C programming and I was hoping that your magazine would
  7896. provide me with helpful hints and programming tips that would help me become a
  7897. better C programmer. Unfortunately, in most issues I found nothing that was
  7898. beneficial to me. Please believe me when I tell you that I am not "knocking"
  7899. your magazine at all. I'm sure that if I was more knowledgeable in C, your
  7900. magazine would be very interesting. But, quite frankly I don't understand half
  7901. of the articles in each issue.
  7902. What I would like to see is an article or section of each issue dedicated to
  7903. the basics of C, or at least programming tips that the layman can understand.
  7904. I can't believe that I am the only one that has not renewed my subscription
  7905. because the articles are "over my head". Perhaps, something like I have
  7906. mentioned may even increase subscriptions just from people glancing through
  7907. the C Users Journal on the magazine rack.
  7908. I realize that you have to appeal to the masses and not the exceptions and if
  7909. that's the case, I'll probably subscribe to the magazine when I feel that it
  7910. would be of some use to me.
  7911. You have an excellent magazine. Keep up the good work.
  7912. Sincerely yours,
  7913. Chris Proctor
  7914. 21352 Avenida Ambiente
  7915. El Toro, CA 92630
  7916. I too would like to see some quantum of good tutorial material in every issue,
  7917. in addition to the more demanding copy. Unfortunately, we don't get very many
  7918. well-written tutorial submissions. If my readership includes some willing but
  7919. uninspired authors, here's your chance. Send us a concise but thorough
  7920. tutorial on some aspect of C. We need more such submissions than we are
  7921. currently receiving. -- rlw
  7922. Dear Howard,
  7923. I was pleased that my article, "The C Programmer's Reference: A Bibliography
  7924. of Periodicals," appeared in print in your January, 1990 issue. However, I was
  7925. dismayed to learn that I had inadvertently omitted a couple of worthy entries.
  7926. These annotations, with the appropriate citations, are as follows:
  7927. C Gazette (quarterly, $6.50/issue, $21.00/year) C Gazette, 1341 Ocean Avenue
  7928. #257, Santa Monica, CA 90401.
  7929. A "code-intensive" quarterly which thrives on printing lots of C code (and
  7930. some C++). Specializes in MS-DOS and OS/2, but no UNIX. An in-depth
  7931. publication aimed at intermediate and advanced C programmers. Few
  7932. advertisements and few reviews. For programmers who are serious about their C
  7933. code.
  7934. Journal of C Language Translation ($235.00/year) Journal of C Language
  7935. Translation, 2051 Swan's Neck Way, Reston, VA 22091.
  7936. An academic quarterly which just recently commenced publication. Aimed at
  7937. compiler writers and programmers who must implement the ANSI standard in
  7938. language products. Covers extensions to the standard, such as implementation
  7939. of numerical representation, etc. No advertisements and few reviews. An
  7940. important resource for programmers in this narrow niche.
  7941. I had compiled the original bibliography some time ago, and from the holdings
  7942. of a corporate library. I assumed that the library's holdings were relatively
  7943. complete, and I overlooked the two periodicals above.
  7944. I hope that this letter will fill the gap. I regret it if anyone was offended,
  7945. and I trust that this information will further assist readers of The C Users
  7946. Journal in their language research.
  7947. Sincerely,
  7948. Harold C. Ogg
  7949. Chicago State University
  7950. The Paul and Emily Douglas Library
  7951. Ninety-Fifth Street at King Drive
  7952. Chicago, Illinois 60628-1598
  7953. (For those wondering, Howard is our editorial coordinator. I should let him
  7954. respond to this letter, but he's buried somewhere under some manuscripts and
  7955. pasteups.)
  7956. I appreciate the information. In addition to his column for CUJ, Rex Jaeschke
  7957. also writes a C column for DEC Professional -- not a "C magazine", but at
  7958. least another C resource.
  7959. If you regularly refer to a C-related information source we failed to include,
  7960. please write and we'll mention it here in a future issue. -- rlw
  7961. Dear Mr. Ward;
  7962. I'm glad the C Users Journal is starting to publish articles on the Macintosh,
  7963. its development environment, and its operating system. Keep 'em coming! Nice
  7964. article by Allan Brown [Bruton] in the October '89 edition. True, the
  7965. Macintosh toolbox does add some additional complexity, but once one becomes
  7966. accustomed to it -- and it may take quite a bit of time becoming fluent in
  7967. "toolboxese" -- one can be assured, though, that there is less likelihood of
  7968. code obsolescence and greater possibilities for code portability among the
  7969. various Macintosh hardware platforms and operating systems by following the
  7970. development guidelines and using the toolbox calls for performing window
  7971. manipulations.
  7972. Anyway, I tried executing the code presented on page 99 (Listing 1), and the
  7973. code as written does not draw a set of nested rectangles as promised at the
  7974. beginning of the article. When one executes the code specified in Listing 1,
  7975. nested triangles are drawn on the screen. To obtain nested rectangles the
  7976. variable yb will have to initialized to read
  7977. yb = 25;
  7978. rather than
  7979. yb = 300;
  7980. as printed in the article. That's the only change necessary for having the
  7981. Macintosh draw nested rectangles.
  7982. Thanks again for printing an article of interest to programmers who program
  7983. the Macintosh in C.
  7984.  
  7985. Yours truly,
  7986. Clifford J. Campo
  7987. 123 Fennerton Road
  7988. Paoli, PA 19310
  7989. Gee, you mean rectangles have four sides? Maybe I should spend more time
  7990. watching Sesame Street with my son.
  7991. Thanks for the correction, and thanks for noticing our Macintosh coverage.
  7992. We've really worked hard to get those stories. -- rlw
  7993. Dear Robert:
  7994. I'd like to offer several comments to your "Publisher's Forum" in the August
  7995. 1989 issue.
  7996. I like the new glossier paper; I think it makes the pages easier to turn
  7997. because there's less friction between them. Goodness knows, we readers don't
  7998. want too much friction. (Truly, I do like it better.)
  7999. I can't tell you what a relief it is to read that you're refusing to get
  8000. involved in C puns. At least in your articles. Your advertisers more than make
  8001. up for it. (Of course, it's not just CUJ advertisers...) Too bad X3J11 didn't
  8002. outlaw C puns as part of the ANSI standard.
  8003. Regarding swimsuits, etc.: I agree that would be out of place in CUJ. There's
  8004. plenty available elsewhere. However, your comment, "Wouldn't you rather
  8005. explore lex than sex?" leaves me concerned. Have you somehow arrived at the
  8006. assumption that real programmers are so obsessed with digital high tech that
  8007. they will forego sex? Of course not. How do you think we burn off all of that
  8008. Jolt and pizza? Not at a keyboard surely!
  8009. Speaking of sex and assumptions, and here I am finally being serious, there's
  8010. a big one or two in your comment, "We've even considered running pictures of
  8011. all the staff (especially the women since most of them are single and most of
  8012. you are male).", namely that all male CUJ readers are straight. I assure you,
  8013. it ain't so! About 10% of most any population is gay and lesbian, and while I
  8014. haven't seen any polls to confirm that this is true of programmers, I have no
  8015. reason to feel I should believe otherwise. So, if you were to do swimsuits, it
  8016. would only be fair to include your female and male staff. Fair to your
  8017. straight women readers too, don't forget them!
  8018. CUJ is great, please keep it up (speaking of standards and high ones at that)!
  8019. Sincerely
  8020. Bill Lee
  8021. 5132 106A Street
  8022. Edmonton
  8023. Alberta, CA T6H 2W7
  8024. What can I say? -- rlw
  8025. To The C_Users Group,
  8026. Concerning Numerical Software Tools in C. It is a fine book for those starting
  8027. to program in C. Any book in your Advanced topic area, I as well as all
  8028. others, assume that Advanced means just that -- Advanced! An advanced book
  8029. would be like Numerical Recipes in C by Press et.al. from Cambridge University
  8030. Press. You truly need to re-analyze what is considered advanced considering
  8031. that more and more books actually treating advanced topics are coming out. In
  8032. the past, few knew anything about C. Since it is now the #1 language of
  8033. choice, advanced isn't the advanced of yesterday. The book which I'm sending
  8034. back should be considered elementary to intermediate. Even though it was
  8035. published in 1987, does not mean that it is advanced. Further, four routines
  8036. of the most elementary type, does not in my view constitute "Tools". Tools to
  8037. me are a compendium of primitives that one may use in developing one's own
  8038. applications. This book falls way short of that. Again further, the price is
  8039. outrageous for what one receives.
  8040. Jerry Rice, PHD.
  8041. 504 Eastland St.
  8042. El Paso, Texas, 79907
  8043. In all truth, I haven't read this book. In fact there are more then a few
  8044. books among the 100 or so that we carry that I haven't read. Except when I
  8045. have personal knowledge of the book's contents, we rely upon publisher's
  8046. descriptions when categorizing the book. -- rlw
  8047.  
  8048.  
  8049.  
  8050.  
  8051.  
  8052.  
  8053.  
  8054.  
  8055.  
  8056.  
  8057.  
  8058.  
  8059.  
  8060.  
  8061.  
  8062.  
  8063.  
  8064.  
  8065.  
  8066.  
  8067.  
  8068.  
  8069.  
  8070.  
  8071.  
  8072.  
  8073.  
  8074.  
  8075.  
  8076.  
  8077.  
  8078.  
  8079.  
  8080.  
  8081.  
  8082.  
  8083.  
  8084.  
  8085. User Interface Language Eases Prototyping
  8086.  
  8087.  
  8088. Vincent Guarna and James Krause
  8089.  
  8090.  
  8091. This article is not available in electronic form.
  8092.  
  8093.  
  8094.  
  8095.  
  8096.  
  8097.  
  8098.  
  8099.  
  8100.  
  8101.  
  8102.  
  8103.  
  8104.  
  8105.  
  8106.  
  8107.  
  8108.  
  8109.  
  8110.  
  8111.  
  8112.  
  8113.  
  8114.  
  8115.  
  8116.  
  8117.  
  8118.  
  8119.  
  8120.  
  8121.  
  8122.  
  8123.  
  8124.  
  8125.  
  8126.  
  8127.  
  8128.  
  8129.  
  8130.  
  8131.  
  8132.  
  8133.  
  8134.  
  8135.  
  8136.  
  8137.  
  8138.  
  8139.  
  8140.  
  8141.  
  8142.  
  8143.  
  8144.  
  8145.  
  8146. Using 'Screen Machine'
  8147.  
  8148.  
  8149. Rick Knoblaugh
  8150.  
  8151.  
  8152. Rick Knoblaugh is a Systems Engineer specializing in systems programming for
  8153. PCs. He is the coauthor of Screen Machine, a screen design/prototyping/code
  8154. generation utility. He may be reached at 15014 River Park Dr., Houston TX
  8155. 77070.
  8156.  
  8157.  
  8158. Prototypes and code generators can significantly reduce development costs. In
  8159. this article I'll discuss a recent consulting project and show how the "Screen
  8160. Machine" -- a prototyping tool which I am making available to other
  8161. programmers as shareware -- assisted in prototyping, generating C code for the
  8162. user interface, and documenting the system.
  8163.  
  8164.  
  8165. The Application
  8166.  
  8167.  
  8168. My project was a student grade tracking application for a high school. The
  8169. software allows student names and grades to be scanned into a PC clone using
  8170. an optical mark reader, a scanning device which reads forms which have been
  8171. marked with a pencil. Student names and grades can also be manually entered or
  8172. edited.
  8173. The product enables teachers to maintain their grade books on a PC. Grade
  8174. tracking and printing tasks, such as letters to parents, are all handled in a
  8175. menu-driven environment. Thus, the application required menus, data entry
  8176. screens and help screens.
  8177. I began by planning the major components of the software, such as the scanner
  8178. communications and the decoding of the scanned data. Next I needed to develop
  8179. a user interface from which all program functions could be selected. For this
  8180. phase the user interface prototyping software was invaluable.
  8181.  
  8182.  
  8183. Benefits Of Prototyping
  8184.  
  8185.  
  8186. In the past programmers who have developed interactive programs, have
  8187. painstakingly designed the appearance of screen displays on paper and then
  8188. written the code for these user screens.
  8189. Today, many developers are using some type of screen prototyping software.
  8190. Most prototyping tools permit screen design using a powerful screen editor.
  8191. Screen editors make it much easier to manipulate blocks of data, to center
  8192. screen data, and to experiment with color and other aspects of screen
  8193. appearance.
  8194. In addition to a screen editor, prototyping packages usually include some
  8195. control facility that allows branches to various screens to depend on user
  8196. input. This allows the developer to create the "look and feel" of a user
  8197. interface before any code is written. Prototyping also lets the user become
  8198. more involved in the design of the user interface. More importantly, it allows
  8199. the programmer to be more creative and to develop an interface that makes
  8200. sense.
  8201. Some prototyping tools also provide code generation for the screen displays.
  8202. Once the screen design is finalized, the program automatically generates the
  8203. associated source code.
  8204.  
  8205.  
  8206. Screen Machine
  8207.  
  8208.  
  8209. Screen Machine runs under MS-DOS and consists of a screen editor/code
  8210. generator, a mini-language for prototyping the flow of application screens,
  8211. and a TSR screen capture program which allows any text mode screen to be
  8212. imported into the screen editor.
  8213. Screen Machine can generate source code for screens in your choice of C,
  8214. BASIC, Turbo Pascal, 8086 assembler, and dbase. Screen Machine is limited to
  8215. handling display portions of screens only; it does not handle data input. The
  8216. prototyping module permits the input of single keystrokes, allowing screens to
  8217. be displayed when the operator selects a menu option or presses a specific
  8218. key.
  8219.  
  8220.  
  8221. Designing Screens With SCREEN
  8222.  
  8223.  
  8224. I experimented with the appearance of the grade tracking application screens
  8225. using Screen Machine's screen editor and code generator, SCREEN.EXE. As with
  8226. most applications, I started with the main menu (Figure 1).
  8227. The SCREEN box drawing feature makes it easy to put borders around menus and
  8228. other screens. Text can be centered on a given line of the screen or within
  8229. the graphics character borders of a drawn box. You can even shift the entire
  8230. screen left or right to aid in centering screen data and attributes. Other
  8231. screen editor features include: inserting and deleting lines, copying and
  8232. moving blocks, selection of color, reverse video, undo of last editing
  8233. function, key stroke macros, and online help.
  8234. I saved my designed application screens in Screen Machine screen data files.
  8235. (Screens can be saved with or without attributes). If no color or reverse
  8236. video is needed, the screens can be saved as ASCII text files.
  8237.  
  8238.  
  8239. Prototyping The Interface
  8240.  
  8241.  
  8242. Once the data files for all application screens are complete, the programmer
  8243. develops an executable simulation of the application interface using the
  8244. Screen Machine's mini-prototyping language module, SHOW.COM. The completed
  8245. simulation will display the main menu, accept keystrokes, and based on these
  8246. keystrokes, select other application screens for similar processing.
  8247. The SHOW mini-language consists of display/keystroke input statements, case
  8248. statements, and goto and gosub statements. The heart of these is the
  8249. display/keystroke input statement, whose syntax is:
  8250. Filespec [basekey max] [/Tn]
  8251. [/An] [/Xn]
  8252. Filespec names the screen data file to be displayed. (e.g. I saved my main
  8253. menu screen data file in C:\GRADE\MAINMENU.SRN.) The basekey is optional and
  8254. represents the lowest-valued key accepted as input from the user when the
  8255. screen is displayed. The basekey is one of these:
  8256. A specific key, enclosed in quotation marks (e.g. "1").
  8257. A decimal scan code value (unquoted) (e.g. 59 for the <F1> key).
  8258. An unquoted asterisk (*), which is taken to mean "any key".
  8259. The max cannot be specified unless basekey is specified; it is the
  8260. highest-valued key accepted as input. If input from a given screen falls
  8261. neatly within a range of keystrokes (e.g. if on my main menu only "1" to "9"
  8262. were used, and not <Alt><H>), specifying basekey and max eliminates all
  8263. unwanted keystrokes.
  8264. The T switch specifies a time value in seconds -- useful for creating timed
  8265. "slide shows". SHOW will display the screen data file and then wait n seconds
  8266. (0-255) before displaying the next screen.
  8267.  
  8268. The A switch displays a screen data file in a certain attribute. This is
  8269. generally only used if you have not saved attributes in your screen data
  8270. files.
  8271. The X switch is the key on which a "getout" is performed. "n" is specified in
  8272. the same manner as basekey and max, i.e. either a quoted character or an
  8273. unquoted scan code. A "getout" is accepted as a valid key press and performs
  8274. any pending return or else returns to the operating system.
  8275. Case statements allow branches to other portions of a SHOW command file to
  8276. depend upon keystrokes input via the display/keystroke input statement. The
  8277. syntax for the case statement is:
  8278. case [key] [range] [S: G: R:]
  8279. [label name]
  8280. If a keystroke matches key or falls within range, control is transferred to
  8281. label name. If S: is present, the transfer is executed as a gosub, meaning the
  8282. address of the next display/keystroke input statement is put onto the "stack"
  8283. and control is transferred to the label. G: does a goto transfer to the label.
  8284. R: returns to the label (similar to BASIC).
  8285. The syntax for labels is the same as in MS-DOS batch files (i.e. a ":"
  8286. followed by a label name).
  8287. The grade tracking SHOW command file appears in Listing 1. The top of the
  8288. command file displays the main menu which is stored in the Screen Machine
  8289. screen data file, MAINMENU.SRN. The asterisk after the file name instructs the
  8290. SHOW program to wait after displaying the main menu until some key is pressed.
  8291. The /X indicates that if a 9 is pressed, the SHOW command file should
  8292. terminate and return to MS-DOS. The case statements perform gosubs to other
  8293. labels in the command file. For example, if the user presses a 6, SHOW will
  8294. gosub to the label otherprint where the print options menu is displayed and
  8295. processed.
  8296. The strange looking NUL screen data file name followed by the case * G:top is
  8297. necessary because the limited SHOW command set only allows unconditional
  8298. branching to be initiated in case statements. Case statements can only be
  8299. performed after a screen data file has been displayed by a display/keystroke
  8300. input statement. The reserved screen data file NUL only satisfies the case
  8301. statement by simulating a screen display and a key stroke entry.
  8302. The asterisk indicates that if any key is pressed, a goto should be performed
  8303. to the label top. After the appropriate gosub is processed from the main menu,
  8304. control transfers back to the top of the command file.
  8305.  
  8306.  
  8307. Generating The Source Code
  8308.  
  8309.  
  8310. A SCREEN program configuration option allows you to select the language to be
  8311. generated.
  8312. When SCREEN generates C code, it declares a structure _scrn and defines a
  8313. global array of structures of type _scrn (Listing 2). Notice that the array of
  8314. structures is named with screen_ followed by the name of the screen data file
  8315. to prevent naming conflicts.
  8316. After including these statements in your program, you can either write a
  8317. routine to display the arrays of structures, or include the routine supplied
  8318. with Screen Machine in your program as in Listing 3.
  8319. The routine uses the BIOS software interrupt 10h function 9 to display the
  8320. arrays of structures. Function 9 writes a character and attribute at the
  8321. current cursor position. The Microsoft C library function _settextposition is
  8322. used to position the cursor.
  8323. The function disp_screen is called passing the name of the array of structures
  8324. to be displayed and a flag indicating whether the screen should be cleared
  8325. prior to displaying the data. disp_screen clears the screen using the
  8326. background color defined in the variable color_back_grnd. This should be set
  8327. to the desired background color.
  8328.  
  8329.  
  8330. Data Entry
  8331.  
  8332.  
  8333. Because Screen Machine handles only display portions of screens, I used my own
  8334. general-purpose data entry routines for those portions of the application
  8335. where data entry was required.
  8336.  
  8337.  
  8338. Screen Capture
  8339.  
  8340.  
  8341. A third Screen Machine module CAPTURE.COM is a TSR program that allows text
  8342. mode screen displays to be captured and stored on disk. This utility makes it
  8343. easy to include application screens, complete with sample data in the users
  8344. manual.
  8345. CAPTURE takes over the shift print screen function (interrupt 5). When the
  8346. program is invoked and becomes resident, command line options specify the file
  8347. name under which screen data files are to be stored and whether or not
  8348. attributes should be included in the screen data files. If attributes are not
  8349. desired, screen data is stored in ASCII text files.
  8350. Captured screens can also be used as input into the screen editor/code
  8351. generator. This means that any text mode screen can be translated into source
  8352. code which will display that screen in one or all five supported programming
  8353. languages. This capability can be used when translating an application from
  8354. one language to another or if you want to generate source code for screens
  8355. created with a prototyping tool that doesn't support source code generation.
  8356.  
  8357.  
  8358. Conclusion
  8359.  
  8360.  
  8361. Screen Machine does several things satisfactorily. Its lack of support for
  8362. input fields may preclude your using it for some applications. Certainly, if
  8363. you need really detailed simulations of your programs such as sound effects
  8364. and emulation of disk I/O, you should use a more full-featured commercial
  8365. prototyping program. Also, if you require a graphics interface then Screen
  8366. Machine will not help you.
  8367. Figure 1
  8368. Grade Book Main Menu
  8369.  
  8370. 1) Scan Grades
  8371. 2) Edit/View Grades
  8372. 3) Print Grade Book
  8373. 4) Scan Names
  8374. 5) Print Rosters
  8375. 6) Other Print Functions
  8376. 7) Set Teacher Information
  8377. 8) Drop Lowest Grade
  8378. 9) Exit
  8379.  For help, press < Alt > < H >.
  8380.  
  8381. Listing 1
  8382. /*SHOW command file for grade tracking program.
  8383. /* ----------------------------------------------
  8384.  
  8385. :top
  8386.  
  8387. mainmenu.srn * /x"9" /*display main menu, accept any key,
  8388. /*exit to dos if "9"
  8389.  
  8390. case "1" s:scangrades /*gosub to display the appropriate screens
  8391. case "2" s:editgrades
  8392. case "3" s:printgrades
  8393. case "4" s:scannames
  8394. case "5" s:printrosters
  8395. case "6" s:otherprint
  8396. case "7" s:setteacher
  8397. case "8" s:droplow
  8398. case 35 s:mainhelp /*<alt><h> help
  8399.  
  8400. /*When all gosubs return, branch back to top. You can only branch
  8401. /*as part of a case statement and you can only have a case statement
  8402. /*after display/keystroke input statement. Thus, the special NUL
  8403. /*screen name can be used to branch anytime.
  8404.  
  8405. nul /*special reserved display/keystroke input
  8406. statement
  8407. case * g:top /*branch back to top of command file
  8408.  
  8409. /*-----------------------------------------------
  8410. :scangrades
  8411. scangrad.srn /*display scan grades screen and wait for
  8412. a key
  8413. case * r: /*return
  8414.  
  8415. /*-----------------------------------------------
  8416. :editgrades
  8417. editgrad.srn * /x1 /*display edit/view grades screen and wait for
  8418. /*a key, return to caller if esc (scan
  8419. /* code 1) is pressed
  8420.  
  8421. case 35 s:edithelp /*if <alt><h> (scan code 35) is pressed, go
  8422. /*display the edit/view grade help
  8423.  
  8424. nul
  8425. case * g:editgrades /*go back to edit/view grade screen
  8426.  
  8427. /*-------------------------------------------------
  8428. :printgrades /*display print grades screen
  8429. prtgrade.srn *
  8430. case * r:
  8431.  
  8432. /*-----------------------------------------------
  8433. :scannames /*display scan names screen
  8434. scanname.srn *
  8435. case * r:
  8436.  
  8437. /*-----------------------------------------------
  8438. :printrosters /*display print rosters screen
  8439. prtrost.srn *
  8440. case * r:
  8441.  
  8442. /*----------------------------------------------
  8443. :otherprint /*display other print options menu
  8444. prtmenu.srn "1" "6" /x"6" /*accept only 1-6, return to caller if
  8445. 6
  8446.  
  8447. case "1" s:report1 /*branch to report 1 screen
  8448. case "2" s:report2 /*branch to report 2 screen
  8449. case "3" s:report3 /*branch to report 3 screen
  8450. case "4" s:report4 /*branch to report 4 screen
  8451. case "5" s:report5 /*branch to report 5 screen
  8452.  
  8453. nul
  8454. case * g:otherprint
  8455.  
  8456. /*------------------------------------------------
  8457. :report1
  8458. reportl.srn *
  8459. case * r:
  8460.  
  8461. /*------------------------------------------------
  8462. :report2
  8463. report2.srn *
  8464. case * r:
  8465.  
  8466. /*------------------------------------------------
  8467. :report 3
  8468. report3.srn*
  8469. case * r:
  8470.  
  8471. /*------------------------------------------------
  8472. :report4
  8473. report4.srn *
  8474. case * r:
  8475.  
  8476. /*------------------------------------------------
  8477. :report5
  8478. report5.srn *
  8479. case * r:
  8480.  
  8481. /*------------------------------------------------
  8482. :setteacher
  8483. setteach.srn * /*display set teacher information screen
  8484. case * r:
  8485.  
  8486. /*------------------------------------------------
  8487. :droplow
  8488. droplow.srn * /*display drop lowest grade screen
  8489. case * r:
  8490.  
  8491. /*------------------------------------------------
  8492. :edithelp
  8493. edithelp.srn * /*display edit/view help screen and return to
  8494. case * r: /*caller when any key is pressed
  8495.  
  8496. /*------------------------------------------------
  8497. :mainhelp
  8498. mmhelp.srn * /*display main menu help screen and return to
  8499. case * r: /*caller when any key is pressed
  8500.  
  8501.  
  8502. Listing 2
  8503. struct _scrn {
  8504. char *chrs; /*pointer to screen text*/
  8505. char cw; /*column where text appears*/
  8506.  
  8507. char rw; /*row where text appears*/
  8508. char att; /*attribute in which text appears*/
  8509. };
  8510.  
  8511. struct _scrn screen_mainmenu[]={
  8512.  
  8513. Click Here for Figure
  8514.  
  8515.  
  8516. Listing 3
  8517. /*various include files*/
  8518. #include <stdio.h>
  8519. #include <graph.h>
  8520. #include <bios.h>
  8521. #include <dos.h>
  8522.  
  8523. #define FALSE 0
  8524. #define TRUE 1
  8525. #define VIDEO 0x10 /*software interrupt 0x10 */
  8526. #define WRITE_ATTR_CHAR 9 /*function 9 */
  8527.  
  8528. void disp_screen(struct _scrn *, unsigned short );
  8529.  
  8530. struct _scrn {
  8531. char *chrs;
  8532. char cw;
  8533. char rw;
  8534. char att;
  8535. };
  8536.  
  8537. struct _scrn screen_mainmenu[]={
  8538.  
  8539. {"èëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëë£",21,6,31},
  8540. {" ",21,7,31},
  8541. {" Grade Book Main Menu ",21,8,31},
  8542. {" ",21,9,31},
  8543. {" 1) Scan Grades ",21,10,31},
  8544. {" 2) Edit/View Grades ",21,11,31},
  8545. {" 3) Print Grade Book ",21,12,31},
  8546. {" 4) Scan Names ",21,13,31},
  8547. {" 5) Print Rosters ",21,14,31},
  8548. {" 6) Other Print Functions ",21,15,31},
  8549. {" 7) Set Teacher Information ",21,16,31},
  8550. {" 8) Drop Lowest Grade ",21,17,31},
  8551. {" 9) Exit ",21,18,31},
  8552. {" ",21,19,31},
  8553. {" For help, press <Alt><H>. ",21,20,31},
  8554. {"àëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëx",21,21,31},
  8555. {"\0",0,0,0}
  8556. }";
  8557. main()
  8558. {
  8559.  
  8560. disp_screen(screen_mainmenu, TRUE); /*clear screen and then display the
  8561. screen defined by screen_mainmenu*/
  8562. }
  8563. long color_back_grnd= 1; /*all screens will use a blue
  8564. background*/
  8565. /*-----------------------------------------------------------
  8566.  
  8567.  disp_screen - Use ptr passed to array of structures 
  8568.  containing &text; col; row; and attribute. 
  8569.  Use BIOS int 10h function 9 to display the 
  8570.  data. 
  8571.  
  8572.  If cls_flag is TRUE, clear the screen before
  8573.  displaying the data. When clearing the 
  8574.  screen, use the attribute defined in the 
  8575.  variable color_back_grnd 
  8576. ------------------------------------------------------------*/
  8577.  
  8578. void disp_screen(p, cls_flag)
  8579. struct _scrn *p;
  8580. unsigned short cls_flag;
  8581. {
  8582. char wcol;
  8583. char * wsptr;
  8584.  
  8585. union REGS inregs, outregs;
  8586.  
  8587.  
  8588. if (cls_flag)
  8589. {
  8590. _setbkcolor(color_back_grnd);
  8591. _clearscreen(_GCLEARSCREEN);
  8592. }
  8593.  
  8594. inregs.h.ah = WRITE_ATTR_CHAR; /*print char and attribute*/
  8595. inregs.x.cx = 1; /*print 1 char*/
  8596. while ( *(p->chrs) )
  8597. {
  8598. wsptr=p->chrs; /*get ptr to string*/
  8599. wcol=p->cw;
  8600.  
  8601. inregs.h.bh = 0; /*video page 0*/
  8602. inregs.h.bl = p->att; /*attribute to use */
  8603.  
  8604. while (inregs.h.al = *wsptr++) /*char to print*/
  8605. {
  8606.  
  8607. /*position the cursor*/
  8608. _settextposition( (short) p->rw, (short) wcol++);
  8609.  
  8610. int86 ( VIDEO, &inregs, &outregs ); /*print with BIOS*/
  8611. }
  8612.  
  8613. p++;
  8614. }
  8615. }
  8616.  
  8617.  
  8618.  
  8619.  
  8620.  
  8621.  
  8622.  
  8623.  
  8624.  
  8625.  
  8626.  
  8627.  
  8628.  
  8629.  
  8630.  
  8631.  
  8632.  
  8633.  
  8634.  
  8635.  
  8636.  
  8637.  
  8638.  
  8639.  
  8640.  
  8641.  
  8642.  
  8643.  
  8644.  
  8645.  
  8646.  
  8647.  
  8648.  
  8649.  
  8650.  
  8651.  
  8652.  
  8653.  
  8654.  
  8655.  
  8656.  
  8657.  
  8658.  
  8659.  
  8660.  
  8661.  
  8662.  
  8663.  
  8664.  
  8665.  
  8666.  
  8667.  
  8668.  
  8669.  
  8670.  
  8671.  
  8672.  
  8673.  
  8674.  
  8675.  
  8676.  
  8677.  
  8678.  
  8679.  
  8680.  
  8681.  
  8682.  
  8683.  
  8684.  
  8685.  
  8686.  
  8687.  
  8688.  
  8689.  
  8690. Prototyping Experiences
  8691.  
  8692.  
  8693. Brett Martensen
  8694.  
  8695.  
  8696. Brett Martensen is a Senior Systems Consultant with SRI Strategic Resources,
  8697. Inc. He specializes in tools and techniques, including CASE, prototyping and
  8698. JAD, to develop database applications. Areas such as entity relationship data
  8699. modeling are his forte. He has a M.Sc. in Computer Science (1976) from Queen's
  8700. University (Kingston, Ontario).
  8701.  
  8702.  
  8703. When developing a prototype, one is faced with reaching a maximum level of
  8704. functionality across the maximum scope of the application, but within a
  8705. minimum time frame. Two productivity tools help reach these conflicting goals:
  8706. the CASE (Computer Aided Software Engineering) tool, which is the
  8707. specification engine, and a DBMS (DataBase Management System), which is the
  8708. application engine.
  8709. I recently participated with a team to develop a prototype system for Canada
  8710. Post Corporation. This prototype had to be robust enough to be used across the
  8711. country, at a number of different user sites during a three-month trial. Thus,
  8712. it had to be more functionally complete than would normally be expected of a
  8713. prototype.
  8714.  
  8715.  
  8716. Background
  8717.  
  8718.  
  8719. A prototype is a miniature system which approximates the final system but
  8720. provides only a subset of the application's scope and functionality. As such,
  8721. a prototype comes with all the benefits associated with modeling. A model is
  8722. easier and certainly less expensive to change than a real system.
  8723. Prototyping permits developers to elicit, model and then capture user
  8724. requirements for a system. Like the buildings on a movie set, a prototype must
  8725. look real even though it is only a facade. On a movie set, certain buildings
  8726. have rooms, some of which are furnished. Similarly some features in a
  8727. prototype are fully implemented, while others remain as images only. There are
  8728. four levels of functionality used when describing a prototype:
  8729. Level Functionality
  8730. one Screens only
  8731. two Screens with field entry and
  8732.  edit, some controllable flow.
  8733. three Level two plus Create, Retrieve,
  8734.  Update and Delete of data
  8735.  and Menu linking screens
  8736.  together.
  8737. four Level three plus Integrity
  8738.  checking, a correctly
  8739.  structured database and some
  8740.  application specific algorithms
  8741.  working.
  8742. Most prototypes end up as a mixture of these levels applied to different parts
  8743. of the application.
  8744. The prototype developer must be able to respecify the data model and quickly
  8745. regenerate the database structure because a prototype is not a static model;
  8746. it goes through a number of iterations.
  8747. A typical prototype development iteration consists of analyzing the
  8748. requirements (read documentation, conduct interviews); specifying (data model,
  8749. functional specifications); designing (display layout, reports); developing
  8750. (program, fill in tables of data); demonstrating and using; and finally,
  8751. reviewing the design with users using Joint Application Design (JAD)
  8752. techniques.
  8753. The JAD is a meeting in which a consensus can be reached amongst the user
  8754. population as to the system requirements. The results of the JAD provide the
  8755. requirements for the start of the next iteration.
  8756. Theoretically, this cycle should be repeated three times during a prototyping
  8757. project. Ideally, each iteration of the prototype progresses closer to the
  8758. specification for the final system. A rule of thumb is that sixty percent of
  8759. the remaining requirements are captured in each cycle. After the second cycle,
  8760. the system should be 84 percent correct and after the third cycle, 93+ percent
  8761. correct (rather like golf where each shot gets you closer to the hole). At
  8762. some point, however, diminishing returns make further iterations pointless.
  8763.  
  8764.  
  8765. The Canada Post Prototype
  8766.  
  8767.  
  8768. The Canada Post Corporation prototype was developed using E-R Designer from
  8769. Chen and Associates, a CASE tool for data modeling to specify the data model;
  8770. and ZIM from Sterling Software, Zanthe Systems Division, a powerful 4GL DBMS
  8771. to develop the prototype.
  8772. ZIM is a natural choice for prototyping projects. It directly implements an
  8773. Entity-Relationship data model which it keeps in an Active and Integrated Data
  8774. Dictionary. An entity-relationship database structure allows transferring the
  8775. conceptual data model produced in the specification stage directly into the
  8776. database data dictionary. An Active data dictionary is important because the
  8777. programs can access all the metadata. An Integrated data dictionary stores
  8778. display specification information as well as the database structure. An
  8779. advantage of choosing E-R Designer is that the data model can be exported and
  8780. used to create the ZIM database description without rekeyboarding.
  8781. ZIM also runs code either in interpreted or compiled mode. Since prototype
  8782. performance is not a consideration, the interpreted mode is used. This mode
  8783. has a macro substitution capability which allows names of entities,
  8784. relationships, displays, and ZIM routines to be substituted in the programs
  8785. and resolved at run time. The data dictionary stores all these names, as well
  8786. as segments of ZIM code for macro substitution. Thus, the data dictionary
  8787. becomes the repository of the prototype specifications, data, displays and
  8788. programs.
  8789. Other tools such as WordPerfect and internally developed ZIM utilities
  8790. increased productivity further. We used WordPerfect's line draw facility to
  8791. draw boxes and rapidly design the displays. Then below each display, the
  8792. position of all the fields and their prompts were specified. A short ZIM
  8793. program analyzed this information and created the display form specifications
  8794. in the ZIM data dictionary.
  8795. One of ZIM's most useful prototyping features is the way in which display
  8796. forms relate to entities in the database. If a field on a display form is
  8797. given the same name as a field in an entity, then the ZIM command CHANGE Form
  8798. FROM EntitySet fills in the fields of the current form from the fields of the
  8799. same name in the current record of the entity set. Similarly, ADD EntitySet
  8800. FROM Form creates a new record in the entity set from the data entered on the
  8801. form.
  8802. This functional relationship between display form fields and entity set fields
  8803. is re-inforced by maintaining the metadata in the data dictionary for each
  8804. entity set field. (See Table 1.)
  8805. The other ZIM database information normally needed length, decimals, indexed
  8806. and required are also stored in the data dictionary. Other useful attributes,
  8807. such as default value, data mask and validation rules, exist for the fields in
  8808. the display form.
  8809. We developed a number of generic, table-driven modules for the prototype's
  8810. functional side. The concept of table-driven software is extremely powerful:
  8811. by simply modifying the data in the tables, the developer can rapidly change
  8812. the way in which a module functions, what it operates on and how it appears to
  8813. the user. For example, we developed a menu program which was totally table
  8814. driven. By changing the tables, the routines executed when the menu items were
  8815. chosen, the menu structure and menu functions were modifiable.
  8816. With the table-driven approach, code can be reused. For example, only one
  8817. modify routine can be applied to any entity set, and an enhancement made to a
  8818. routine is universally available in the prototype system. Thus, the only
  8819. nonreusable code is the application-specific algorithms, which are all linked
  8820. in via the program name attribute attached to each field.
  8821. For the Canada Post Corporation prototype, generic table-driven routines were
  8822. developed for
  8823. Menu
  8824. Level 1 and 2 screens (slide show)
  8825. Entityset Lister, which provided
  8826. access to the functions of:
  8827. Sort list
  8828. Pick record
  8829.  
  8830. Print list
  8831. Find record
  8832. Add record
  8833. Modify record
  8834. Delete record
  8835. Help
  8836. This list doesn't include "Reporting" since programming a general-purpose
  8837. reporting module is difficult to perform using the table-driven approach. The
  8838. Print list routine provided simple reports. More complex reports which needed
  8839. to were hand-coded in ZIM. Reports that did not need to function were
  8840. presented as Level one screens.
  8841. There exist both a spreadsheet and business graphics package which take data
  8842. from a ZIM database and allow for its manipulation, analysis and presentation.
  8843. Given that the Canada Post Corporation application involved large quantities
  8844. of statistical information, both these packages were linked into the prototype
  8845. to assist the user in performing ad hoc inquiries and analysis of the data.
  8846. These two packages were especially useful for the design and creation of new
  8847. and existing reports to elicit user feedback. They added substantial
  8848. functionality to the prototype with very little effort.
  8849. The modules of the prototype were linked together in the calling sequence as
  8850. shown in Figure 1.
  8851. This prototype environment can be easily extended as new generic routines are
  8852. developed. A table-driven ad hoc inquiry function could be built and linked in
  8853. via the menu.
  8854. Since the purpose of a prototype is to develop a working model with frequent
  8855. feedback from the user population, it is appropriate to add a feature which
  8856. captures users' ideas while they are fresh in their minds. Since "help" was
  8857. available throughout the prototype, a suggestion box feature was added to the
  8858. "help" module which allowed for on-line and in-context idea capturing. These
  8859. ideas were collected, printed and analyzed during the JAD sessions. User
  8860. feedback was very general on the first iteration: "We also need to be able to
  8861. store information about our services," for example. In subsequent iterations,
  8862. suggestions became more specific: "Use the word 'item' rather than 'product'."
  8863. The final goods delivered from a prototyping project consist of the working
  8864. prototype and a large quantity of documentation. The documentation covers the
  8865. working prototype and user requirements that were not implemented in the
  8866. prototype, such as complicated application-specific algorithms or feedback
  8867. from the final JAD.
  8868.  
  8869.  
  8870. Conclusion
  8871.  
  8872.  
  8873. The combined use of CASE tools and 4GLs allows for greater productivity in
  8874. prototype development. Using generic table-driven modules results in less
  8875. software development. As a result, the workload shifts to the specification
  8876. and analysis tasks. As in so many situations where technology is applied to
  8877. the automation of the simpler tasks, fewer people are required, but the ones
  8878. remaining need more expertise. The skills of business and functional analysis,
  8879. data modeling, and design, become more important than programming.
  8880. Reference
  8881. Application Prototyping: A Requirements Definition Strategy for the 80's,
  8882. Bernard H. Boar, Wiley-Interscience Publication, John Wiley & Sons, New York,
  8883. 1984.
  8884. Figure 1
  8885.  
  8886. Name The unique name given to
  8887.  this data element.
  8888. Type Whether alpha-numeric,
  8889.  integer, character, etc.
  8890. Prompt The full username to appear
  8891.  on displays.
  8892. Column Header An abbreviated name to
  8893.  appear at the top of
  8894.  any list.
  8895. Program Name The name of the ZIM
  8896.  routine to be executed
  8897.  whenever the value of
  8898.  the field is changed.
  8899. Helptext A user-readable explanation
  8900.  of the data element,
  8901.  its possible values and
  8902.  purpose, if necessary.
  8903. Figure 2
  8904.  
  8905.  
  8906.  
  8907.  
  8908.  
  8909.  
  8910.  
  8911.  
  8912.  
  8913.  
  8914.  
  8915.  
  8916.  
  8917.  
  8918.  
  8919.  
  8920.  
  8921.  
  8922.  
  8923.  
  8924.  
  8925.  
  8926.  
  8927. The UI2 Code Generator
  8928.  
  8929.  
  8930. Paul Combellick
  8931.  
  8932.  
  8933. Paul Combellick has a BS in Petroleum Engineering from the University of
  8934. Alaska, Fairbanks. He is a contract programmer specializing in dBASE and C
  8935. local area network (LAN) database applications and can be reached at (602)
  8936. 280-2569 or via Compuserve at 70671,3054.
  8937.  
  8938.  
  8939. As a Network DBMS applications developer, I recently undertook my first major
  8940. C project. After having scoffed at UI2 for several months, I decided to give
  8941. it a try as a fast prototyping tool. The resulting productivity gains exceeded
  8942. my most optimistic expectations. I was able to produce about fifty percent of
  8943. the 20,000 lines of C code in this application using UI2.
  8944. I used UI2 with Vermont Views Screen library and Btrieve Record Manager to
  8945. build a Network DBMS application. As I was new to all three tools, I spent a
  8946. third of project time learning the new tools, with the remaining weeks
  8947. actually producing generated and hand-written code.
  8948. By the time I completed this single DBMS application, I had produced a set of
  8949. templates and template libraries, using UI2 terminology, that would allow me
  8950. to produce the next Network DBMS application in a few weeks, rather than in
  8951. months if the code were entirely written by hand.
  8952.  
  8953.  
  8954. Description Of UI2
  8955.  
  8956.  
  8957. UI Programmer Version Two, The Developer's Release, by Wallsoft is a
  8958. programmable code generator targeted toward dBASE programmers, but is flexible
  8959. enough to be used for many languages in the MS/PC-DOS environment including C.
  8960. UI2 contains four major components.
  8961. Screen Editor. The user can interactively draw screens and define screen
  8962. entities including menus, background text, boxes, variables, fields.
  8963. Templates. Code generation language files define how the code for a particular
  8964. screen will be created.
  8965. Template libraries. A library contains groups of functions written in UI2's
  8966. generation language that are called by the templates during the generation of
  8967. the target source code file.
  8968. Code Generator. An interpreter that executes the template language to generate
  8969. the target source code for a particular screen.
  8970. UI2 is shipped with a set of templates and template function libraries for
  8971. dBASE programmers. The C programmer will have to create his own templates
  8972. before any non-trivial C code, other than simple menus, can be generated.
  8973.  
  8974.  
  8975. Case Study
  8976.  
  8977.  
  8978. For this application the client specified C for portability to OS/2 and UNIX.
  8979. The target system is a Novell Network which supports the Btrieve database
  8980. server. Btrieve also has versions for OS/2 and Intel-based PCs running UNIX. I
  8981. chose Vermont Views screen library for its portability to UNIX and OS/2. This
  8982. networked DBMS application contains several types of screen input/output:
  8983. menu-only screens, data entry-only screens, combined menu and data entry
  8984. screens, and reports.
  8985. I created three templates, one for each of the first three screen types. These
  8986. templates are actually source code files, written in the code generation
  8987. language, that are executed by the code generator's interpreter. The templates
  8988. describe how the code generator should handle a particular screen object to
  8989. create the target source code language.
  8990. I painted the screens by drawing boxes, menus and their actions, background
  8991. text, and defining data entry fields. Through the screen editor the user may
  8992. specify menu attributes such as hot keys and the name of a function to call
  8993. when the menu is selected. The editor also allows specification of field
  8994. attributes such as type, width, picture and provision for such user-designed
  8995. features as begin-field and end-field trigger and validation functions. The
  8996. programmer designs the template language functions to take advantage of the
  8997. entities and their attributes defined in the screen editor.
  8998. UI2 has an interactive mode as well as a command line mode that allows UI2's
  8999. code generator to be accessed by make. A make response file can include the
  9000. dependencies for generating target source code files from screen definitions,
  9001. as well as compiling and linking the entire system. I can now modify a
  9002. template file and make will call UI2 to regenerate all the affected source
  9003. code modules. To modify a screen definition file -- either by adding text or a
  9004. data element, or changing one of the many field, form, or menu attributes -- I
  9005. don't edit the C source file; instead, I modify the screen definition file
  9006. using the UI2 screen editor. I design screens so that I never modify the UI2
  9007. generated C code files except through the UI2 screen editor.
  9008. UI2 is not limited to any particular coding style or third-party library and
  9009. is adaptable to many different compilers. UI2 was used on this project to
  9010. generate 40 screens and about 10,000 lines of C code. The code generation
  9011. language syntax is very dBASE-like and the learning period was brief.
  9012.  
  9013.  
  9014. UI2 Strengths
  9015.  
  9016.  
  9017. This type of code generator performs very well on repetitive tasks such as
  9018. building screens. I was able to build all the screens -- both menu and data
  9019. entry -- entirely with UI2. After learning Vermont Views, Btrieve, UI2 and
  9020. building templates, I will probably be able to reproduce 40 new screens for a
  9021. new application in a few days. More importantly, the generated C code will be
  9022. free of syntax errors and errant pointers. This bug-free code is at least as
  9023. important as the productivity in creating the code. Once the templates are
  9024. debugged, future screens will be virtually free of syntax errors. On the very
  9025. first project, I used UI2 to boost productivity significantly, despite a
  9026. learning period to become familiar with a new tool.
  9027.  
  9028.  
  9029. Limitations
  9030.  
  9031.  
  9032. In light of the fact that UI2 was designed with the dBASE programmer in mind,
  9033. it lacks a couple of features for the C programmer. The most obvious feature
  9034. missing is a full-featured dictionary that supports C data types, including
  9035. structures and scoping concepts, and general data file schema beyond the dBASE
  9036. file support. However, I was able to work around most of the data dictionary
  9037. limitations by creating a hidden box in each screen. The box, made up mostly
  9038. of #includes and external declarations, contained code for the generator to
  9039. insert literally into the generated C source code.
  9040.  
  9041.  
  9042. Conclusion
  9043.  
  9044.  
  9045. I am quite satisfied with UI2. I have created templates for my non-programmer
  9046. partner to fast prototype systems for prospective clients in order to
  9047. illustrate what a proposed system may look like. I believe that UI2 will boost
  9048. my coding and debugging productivity by factors of five to ten in the area of
  9049. screen generation and maintenance. On future projects I expect to realize
  9050. tremendous productivity gains now that I am familiar with this tool and have
  9051. created a set of templates and template libraries to create code that utilizes
  9052. Vermont Views Screen Library.
  9053.  
  9054. Listing 1 The fragment of the template in Listing 1 expands to produce the C
  9055. code in Listing 2.
  9056. /*********************** define the form ***************************/
  9057. <<menuform = 'form' ** set UI var to 'form' for vvdispc.tlb >>
  9058.  
  9059. /* define a form */
  9060.  
  9061. {menuname}_dfmp = fm_def( {formbox.row}, {formbox.col},
  9062. {formbox.height}, {formbox.width}, LNORMAL, BDR_NULLP );
  9063.  
  9064. /* define boxes around form items ****/
  9065. <<define_all_form_boxes()>>
  9066.  
  9067. /*********** define background text */
  9068. <<display_text()>>
  9069.  
  9070. sfm_help( "*DATA HELP" , {menuname}_dfmp ); /* define form help keyword */
  9071.  
  9072. <<define_form_options()>>
  9073.  
  9074. /******* define form data fields *********/
  9075. <<define_all_form_fields()>>
  9076.  
  9077.  
  9078. Listing 2
  9079. /*********************** define the form ***************************/
  9080.  
  9081. /* define a form */
  9082. CUG_dfmp = fm_def( 0, 0, 21,80, LNORMAL, BDR_NULLP );
  9083.  
  9084. /* define boxes around form items ****/
  9085. bg_boxdef( 0,0,21,80,LNORMAL,BDR_SPACEP,CUG_dfmp);
  9086. bg_boxdef( 5,14,11,52,LNORMAL,BDR_DLNP,CUG_dfmp);
  9087.  
  9088. /*********** define background text */
  9089. bg_txtdef( 1, 28, "C USER'S GROUP UI2 DEMO", LNORMAL, CUG_dfmp);
  9090. bg_txtdef( 2, 28, " ", LNORMAL, CUG_dfmp);
  9091. bg_boxdef( 5,14,11,52,LNORMAL,BDR_DLNP, CUG_dfmp);
  9092. bg_txtdef( 7, 19, "Name : [ ]", LNORMAL,
  9093. CUG_dfmp);
  9094. bg_txtdef( 8, 19, "Address : [ ]", LNORMAL,
  9095. CUG_dfmp);
  9096. bg_txtdef( 9, 19, "City : [ ]", LNORMAL,
  9097. CUG_dfmp);
  9098. bg_txtdef( 10, 19, "State : [ ] Zip : [ - ]", LNORMAL,
  9099. CUG_dfmp);
  9100. bg_txtdef( 12, 19, "Phone : [ ]", LNORMAL, CUG_dfmp);
  9101. bg_txtdef( 13, 19, "Fax : [ ]", LNORMAL, CUG_dfmp);
  9102.  
  9103. sfm_help( "*DATA HELP" , CUG_dfmp ); /* define form help keyword */
  9104.  
  9105.  
  9106. /******* define form data fields *********/
  9107. CUG_fld1 = fld_def( 7,33, NULLP , FADJACENT , "!!!!!!!!!!!!!!!!!!!!!!!!!",
  9108. F_STRING , (PTR) name, CUG_dfmp );
  9109. CUG_fld2 = fld_def( 8,33, NULLP , FADJACENT , "XXXXXXXXXXXXXXXXXXXXXXXXX" ,
  9110. F_STRING , (PTR) address, CUG_dfmp );
  9111. CUG_fld3 = fld_def( 9,33, NULLP , FADJACENT , "XXXXXXXXXXXXXXXXXXXXXXXXX",
  9112. F_STRING , (PTR) city, CUG_dfmp );
  9113. CUG_fld4 = fld_def( 10,33, NULLP , FADJACENT , "!!", F_STRING ,
  9114. (PTR) state, CUG_dfmp );
  9115. CUG_fld5 = fld_def( 10,48, NULLP , FADJACENT , "UUUUU-UUUU", F_STRING ,
  9116. (PTR) zip, CUG_dfmp );
  9117. CUG_fld6 = fld_def( 12,33, NULLP , FADJACENT , "(UUU)UUU-UUUU", F_STRING ,
  9118. (PTR) phone, CUG_dfmp );
  9119. CUG_fld7 = fld_def( 13,33, NULLP , FADJACENT , "(UUU)UUU-UUUU", F_STRING ,
  9120.  
  9121. (PTR) fax, CUG_dfmp );
  9122.  
  9123.  
  9124.  
  9125.  
  9126.  
  9127.  
  9128.  
  9129.  
  9130.  
  9131.  
  9132.  
  9133.  
  9134.  
  9135.  
  9136.  
  9137.  
  9138.  
  9139.  
  9140.  
  9141.  
  9142.  
  9143.  
  9144.  
  9145.  
  9146.  
  9147.  
  9148.  
  9149.  
  9150.  
  9151.  
  9152.  
  9153.  
  9154.  
  9155.  
  9156.  
  9157.  
  9158.  
  9159.  
  9160.  
  9161.  
  9162.  
  9163.  
  9164.  
  9165.  
  9166.  
  9167.  
  9168.  
  9169.  
  9170.  
  9171.  
  9172.  
  9173.  
  9174.  
  9175.  
  9176.  
  9177.  
  9178.  
  9179.  
  9180.  
  9181.  
  9182.  
  9183.  
  9184. MEL: A Metalanguage Processor
  9185.  
  9186.  
  9187. George Crews
  9188.  
  9189.  
  9190. George M. Crews received his bachelors in General Engineering from the
  9191. University of Nevada at Las Vegas, and his masters in Engineering Science from
  9192. the University of Tennessee at Knoxville. He is a "generalist" with over 15
  9193. years experience in mechanical and software engineering design and analysis.
  9194. He may be contacted at 109 Ashland Lane, Oak Ridge, TN 37830 (615) 481-0414.
  9195.  
  9196.  
  9197. As a mechanical engineer, my experience with analysis programs falls in the
  9198. areas of structural stress, fluid dynamics, heat conduction, and
  9199. thermal/hydraulic system simulation. Such programs present the technical
  9200. software developer with a number of unique problems, not least of which is
  9201. providing a user-friendly interface.
  9202. Though program users tend to be computer literate, input data can often be
  9203. voluminous and tedious to prepare; the typical user may make many runs with
  9204. only slight modifications as design optimization is often accomplished by
  9205. repeated analysis. Both input and output must be stored and presented in a
  9206. manner that allows independent verification and validation. Finally, the
  9207. information output from one program may be required as input by another.
  9208. Another big headache is that modern (i.e., graphical) user interfaces tend to
  9209. be hardware or system-software specific. A good universal interface would free
  9210. the developer from the nuances of different machines and operating systems,
  9211. while at the same time representing a standard that machine-specific routines
  9212. can work with.
  9213. MEL is my solution for making such technical programs more user-friendly and
  9214. modularized. MEL (for MEtaLanguage data processor) is a set of input/output
  9215. utilities that provides a standard interface between the program and the user.
  9216. It can translate input data written in "pseudo-English" (Example 1) making the
  9217. data available to the program as variables (Example 2). It can also translate
  9218. program variables (Example 3) into pseudo-English (Example 4). Effort was made
  9219. to provide data objects that could be easily incorporated into almost any
  9220. engineering analysis program (Example 5).
  9221. The pseudo-English look of MEL means that I/O will be more readable and
  9222. comprehensible to the user (or checker).
  9223. Secondly, MEL is object oriented in that it provides a structured and
  9224. encapsulated I/O interface. Thus, development time will be reduced and future
  9225. changes can be made to the program more easily.
  9226. Thirdly, MEL's grammar is simple and unambiguous, with both input and output
  9227. formats identical so that output from one program may serve directly as input
  9228. to another. Finally, MEL can read and write data directly to a file so that a
  9229. permanent record of a run and its results are available.
  9230.  
  9231.  
  9232. Description
  9233.  
  9234.  
  9235. In MEL the smallest unit of pseudo-English I/O is called a "descriptor." Its
  9236. purpose is to describe something, either data or a command, to a program. The
  9237. general format for descriptors is much like function calls in a typical
  9238. programming language. An I/O unit consists of a descriptor name (somewhat like
  9239. a function name), followed by a parameter list, followed by an end-of-unit
  9240. symbol (the semi-colon). For example, consider the following MEL descriptor,
  9241. which could be used as part of the input to a piping network analysis program:
  9242. pipe, length = 100 (ft), diameter = 6 (in);
  9243. This is a pipe descriptor whose parameters are length and diameter. The values
  9244. assigned to these parameters would be 100 and 6, and in units of feet and
  9245. inches, respectively. Although the tokens (names and parameters) making up
  9246. descriptors are customized by the developer for each individual application
  9247. program, the above grammar remains the same for all programs using MEL. (See
  9248. Example 1 and Example 4.)
  9249. MEL's format was chosen for its simplicity, while allowing for as much
  9250. flexibility as possible without introducing ambiguity. In MEL, tokens may be
  9251. abbreviated as long as they remain uniquely identifiable. MEL assumes a
  9252. default parameter order if parameter names are missing. Comments may be
  9253. included by enclosing them in double quotes; parameter values may be labeled
  9254. as "unknown," etc. These format choices are designed to make programs
  9255. incorporating MEL as convenient to the user as possible.
  9256.  
  9257.  
  9258. Incorporating MEL
  9259.  
  9260.  
  9261. In order to incorporate MEL into one of your own programs, you must customize
  9262. the mel.h header file to be included in your application source code file.
  9263. First create a "dictionary" for both input and output that defines the proper
  9264. spelling, number, and types (integer, array, etc.) of data associated with
  9265. each descriptor and parameter. (Note that by simply changing spellings in the
  9266. dictionary you could go from pseudo-English to "pseudo-French" or some other
  9267. "pseudo-language.") The task of defining dictionaries has been made as
  9268. painless as possible by providing complete instructions and an example program
  9269. on the MEL diskette available through the CUG library. (The diskette contains
  9270. MEL source code, header file, documentation and instructions, an example
  9271. program, and a conversion factor routine. Since a listing of all MEL routines
  9272. would run over 50 pages, a complete listing has not been included with this
  9273. article.) You will need to prepare documentation for the user, defining the
  9274. dictionaries and explaining what the tokens mean.
  9275. To obtain data from a descriptor, you must first read it and then extract the
  9276. data (see Example 2). An example of outputing data is shown in Example 3.
  9277. Allowing the user to input data with different units requires conversion to
  9278. internal units (ASTM, 1982). Included on the MEL diskette is a routine that
  9279. can convert more than 150 different units. Additional units and conversion
  9280. factors can easily be added to the source code.
  9281.  
  9282.  
  9283. How MEL Was Developed
  9284.  
  9285.  
  9286. An early decision was to write MEL in C. Fortran is the traditional language
  9287. for scientific programs; however, engineers like myself are beginning to
  9288. realize that there is more to technical software development than simply
  9289. correctly coding a complex algorithm. ANSI C has a number of significant
  9290. non-numerical advantages over Fortran (Kempf, 1987). C allows for more
  9291. flexible structured programing and data encapsulation techniques to be applied
  9292. (also see Jeffery, 1989). C has more operators and program control constructs
  9293. than Fortran. C allows indirection (pointers) where Fortran does not. C more
  9294. easily interfaces to existing system software since much of this software is
  9295. itself written in C. Also, C is a popular language for unconventional computer
  9296. architectures such as parallel processors (Lusk, 1987) and neural networks.
  9297. Let me also mention some of C's shortcomings, which are related to its
  9298. relative naivete for scientific purposes. Dynamic array dimensioning in C is
  9299. convoluted (Press, 1988). C does not have the numerical library that Fortran
  9300. does. And finally, C does not allow operator overloading for data structures
  9301. (complex numbers for example) nor does it have an exponentiation operator.
  9302. However, I do not think these deficiencies are difficult to overcome.
  9303. Partly as an experiment to form my own opinion about OOP, the design of MEL
  9304. incorporates the object-oriented paradigm. I chose to make use of C's
  9305. preprocessor to restrict the visibility of public type, function, and data
  9306. declarations to just those objects that the application program may need at a
  9307. certain place (see Example 5). (The private type, function, and variable data
  9308. needed by the MEL routines themselves are not shown in the example and are
  9309. hidden from your program by other defined/undefined manifest constants.) For
  9310. another approach refer to the article by Jeffery.
  9311.  
  9312.  
  9313. Summary And Future Enhancement
  9314.  
  9315.  
  9316. Software engineering is rapidly evolving and everyone seems to have his or her
  9317. own ideas about what makes a good user-interface. I believe MEL is a practical
  9318. answer to the spectrum of interface problems confronting the developer and
  9319. user of complex technical programs.
  9320. Some may criticize MEL for its verbosity (as compared to Fortran's fixed field
  9321. format), the time a user must spend learning to use MEL (versus a more
  9322. interactive interface), and the somewhat clumsy way objects must be (or at
  9323. least, were) encoded in C. These points are legitimate and are inherent in
  9324. MEL's design. No design can be all things to all people.
  9325. The next steps in MEL's evolution might be incorporating it into a language
  9326. sensitive editor, a graphical output post-processor, and perhaps later, into
  9327. an expert system shell specialized for the type of analysis being performed.
  9328.  
  9329.  
  9330. Bibliography
  9331.  
  9332.  
  9333. George M. Crews, "HAPN--A Hydraulic Analysis of Piping Networks Program,"
  9334. Masters Thesis in Engineering Science, University of Tennessee, Knoxville,
  9335. 1989. A portion of this thesis describes MEL and how it was developed and used
  9336. for a specific analysis program.
  9337. David Jeffery, "Object-Oriented Programming in ANSI C," Computer Language
  9338. Magazine, February, 1989. This article discusses the object-oriented paradigm
  9339. and a way to implement it in C.
  9340. James Kempf, Numerical Software Tools in C, Prentice-Hall, Inc., 1987. This
  9341. book contains an introduction to both numerical programming and C. The
  9342. emphasis of the text is on creating small routines that can be used as
  9343. building blocks for larger programs. Possible shortcomings are its lack of
  9344. data hiding and that it treats doubly dimensioned arrays statically rather
  9345. than dynamically.
  9346. Ewing Lusk, Overbeek, et al., Portable Programs for Parallel Processors, Holt,
  9347. Reinhart and Winston, Inc., 1987. This book describes a set of C tools for use
  9348. on a broad range of parallel machines.
  9349.  
  9350. William H. Press, Flannery, et al., Numerical Recipes in C, Cambridge
  9351. University Press, 1988. Based on an earlier Fortran edition, this is a great
  9352. cookbook giving a wide range of oven-tested recipes for the numerical gourmet.
  9353. It shows the correct way to handle multidimensioned arrays (dynamically). A
  9354. complaint sometimes heard is that a few of the algorithms are getting obsolete
  9355. due to rapid advances in numerical techniques being made.
  9356. ASTM E 380-82 Standard for Metric Practice, American Society for Testing
  9357. Materials, 1982. This standard contains many useful conversion factors between
  9358. English and metric units.
  9359.  
  9360. Listing 1
  9361. Example 1. An Example of MEL Input for a Hydraulic Analysis Program.
  9362. (Note that tokens will be unique to each application.)
  9363.  
  9364.  
  9365. title, 'Example Problem Illustrating MEL';
  9366. fluid, "water"
  9367. density = 62.4 (lbm/ft3),
  9368. viscosity = 1 (cp);
  9369. node, 1, pressure = 8.67 (psi); "20 ft of water"
  9370. branch, 100, from_node = 1, to_node = 2;
  9371. pipe,
  9372. length = 100 (ft),
  9373. id = 6 (in),
  9374. material = steel;
  9375. end_of_branch;
  9376. node, 2, pressure = 6.5 (psi); "15 ft of water"
  9377. next;
  9378.  
  9379.  
  9380. Listing 2
  9381. Example 2. Example of Obtaining Data From a MEL Descriptor:
  9382.  
  9383.  
  9384. Descriptor:
  9385.  
  9386. pipe, length = 100 (ft), diameter = 6 (in);
  9387.  
  9388. Code fragment:
  9389.  
  9390. double pipe_length, diameter;
  9391.  
  9392. union meli_param_data data; /* see Example 5. */
  9393. char units[MAX_STRING_LEN+1];
  9394. int array_len;
  9395. int unknown_flag;
  9396.  
  9397. meli(); /* reads descriptor */
  9398.  
  9399. meli_data("length", &data, units, &array_len,
  9400. &unknown_flag); /* gets pipe length */
  9401. pipe_length = data.real; /* will equal 100 */
  9402.  
  9403. meli_data("diameter", &data, units, &array_len,
  9404. &unknown_flag); /* gets pipe diameter */
  9405. diameter = data.real; /* will equal 6 */
  9406.  
  9407. /* note that units, array_len, and unknown_flag
  9408. are not considered (used). */
  9409.  
  9410.  
  9411. Listing 3
  9412. Example 3. Example of Outputting a MEL descriptor:
  9413.  
  9414. Code Fragment:
  9415.  
  9416.  
  9417. double pipe_length = 100, diameter = 6;
  9418.  
  9419. union melo_param_data data; /* see Example 5. */
  9420. char length_units[] = "ft";
  9421. char diameter_units[] = "in";
  9422. int array_len = 0;
  9423. int unknown_flag = 0;
  9424.  
  9425. melo_init("pipe"); /* initialize */
  9426.  
  9427. /* get data ready to output: */
  9428. data.real = pipe_length;
  9429. melo_data("length", &data, length_units, array_len,
  9430. unknown_flag);
  9431. data.real = diameter;
  9432. melo_data("diameter", &data, diameter_units,
  9433. array_len, unknown_flag);
  9434.  
  9435. melo(); /* translates data into string */
  9436.  
  9437. Descriptor:
  9438.  
  9439. pipe,
  9440. length = 100 (ft),
  9441. diameter = 6 (in);
  9442.  
  9443.  
  9444. Listing 4
  9445. Example 4. An Example of Output Generated by a Hydraulic Analysis
  9446. Program using MEL. (From the input data given in Example 1.)
  9447.  
  9448. program,
  9449. name = 'HAPN - Hydraulic Analysis of Piping Networks',
  9450. problem_title = 'Example Problem Illustrating MEL';
  9451. message,
  9452. text = 'Date: Thu Jul 13 09:02:11 1989';
  9453. message,
  9454. text = 'Input filename: input';
  9455. equations,
  9456. node = 0,
  9457. loop = 0,
  9458. iterations = 7;
  9459. branch,
  9460. number = 100,
  9461. type = 'independent_branch',
  9462. flow_rate = 436238 (lbm/h),
  9463. flow_change = -6.20476e-007 (%),
  9464. flow_dp = 2.17 (psi),
  9465. elevation_dp = 0 (psi);
  9466. component,
  9467. branch_number = 100,
  9468. component_number = 0,
  9469. type = 'pipe',
  9470. resistance = 4.95228 (Pa*s2/kg2),
  9471. change_resistance = -1.24095e-008 (%),
  9472. pressure_drop = 2.17 (psi);
  9473. node,
  9474. number = 1,
  9475. pressure = 8.67 (psi);
  9476.  
  9477. node,
  9478. number = 2,
  9479. pressure = 6.5 (psi);
  9480. next;
  9481.  
  9482.  
  9483. Listing 5
  9484. Example 5. Public Interface Between MEL and Any
  9485. Application Program Using It. (Excerpted from mel.h header file.)
  9486.  
  9487. /* if using MEL for input (#define MEL_INPUT), then must
  9488. define the MEL input data object: */
  9489. #ifdef MEL_INPUT
  9490.  
  9491. /* firstly, define input constants (all must be
  9492. CUSTOMIZED for specific application program): */
  9493.  
  9494. #define MELI_MAX_DESCRIP_STR_LEN 256
  9495. /* maximum number of characters in any input descriptor
  9496. string. */
  9497. #define MELI_MAX_PARAMS 6
  9498. /* maximum number of parameters for any descriptor (min
  9499. num = 1). */
  9500. #define MELI_MAX_PARAM_STR_LEN 80
  9501. #define MELI_MAX_PARAM_ARRAY_STR_LEN 1
  9502. /* largest allowable parameter string lengths (min size
  9503. = 1) */
  9504. #define MELI_MAX_PARAM_INT_ARRAY_LEN 1
  9505. #define MELI_MAX_PARAM_REAL_ARRAY_LEN 1
  9506. #define MELI_MAX_PARAM_STR_ARRAY_LEN 1
  9507. /* maximum number of elements in parameter data arrays
  9508. (min = 1). */
  9509. #define MELI_UNITS_STR_LEN 80
  9510. /* maximum length of units associated with any param
  9511. (min = 1) */
  9512.  
  9513. /* secondly, define input data structures: */
  9514.  
  9515. union meli_param_data {
  9516. int integer; /* also holds boolean type */
  9517. double real;
  9518. char string[MELI_MAX_PARAM_STR_LEN+1];
  9519. int integer_array [MELI_MAX_PARAM_INT_ARRAY_LEN];
  9520. double real_array[MELI_MAX_PARAM_REAL_ARRAY_LEN];
  9521. char string_array [MELI_MAX_PARAM_STR_ARRAY_LEN]
  9522. [MELI_MAX_PARAM_ARRAY_STR_LEN+1];
  9523. };
  9524. /* this is used for input parameter data. it may either be
  9525. an integer, real, string, array of integers, array of
  9526. reals, or an array of strings. (to save space a union was
  9527. used.) */
  9528.  
  9529. /* thirdly, define input variables: */
  9530.  
  9531. char meli_descriptor_string[MELI_MAX_DESCRIP_STR_LEN+1];
  9532. /* global storage for the input descriptor string. */
  9533.  
  9534. /* lastly, define input functions (typically they return 0
  9535. if no error encountered, else some nonzero error
  9536.  
  9537. code): */
  9538.  
  9539. int meli_file(FILE *meli_file_handle);
  9540. /* read a descriptor string from the input stream and
  9541. call meli(). also, put copy of string read into
  9542. meli_descriptor_string. */
  9543. int meli(void);
  9544. /* translate meli_descriptor_string and put information
  9545. into a private data structure (meli_datum). */
  9546. char *meli_descrip_type (void);
  9547. /* return pointer to name of type of descriptor read by
  9548. meli(). */
  9549. int meli_num_params(void);
  9550. /* return number of parameters read by meli(). */
  9551. int meli_param(int param_num, char *param, union
  9552. meli_param_data *data, char *units, int *array_len, int
  9553. *unknown_flag);
  9554. /* fill arguement list with param_num'th parameter read
  9555. by meli(). (start with param_num = 0.) */
  9556. int meli_data(char *param, union meli_param_data *data,
  9557. char *units, int *array_len, int *unknown_flag);
  9558. /* see if *param was input. if it was, then fill
  9559. argument list with data from meli_datum. */
  9560.  
  9561. #endif /* MEL_INPUT */
  9562.  
  9563. /* if using MEL for output, must define the MEL output data
  9564. object: */
  9565. #ifdef MEL_OUTPUT
  9566.  
  9567. /* firstly, define output constants (all must be
  9568. CUSTOMIZED): */
  9569.  
  9570. #define MELO_MAX_DESCRIP_STR_LEN 256
  9571. /* how many characters can be in an output descriptor
  9572. string? */
  9573. #define MELO_MAX_PARAMS 6
  9574. /* maximum number of parameters for any descriptor. */
  9575. #define MELO_MAX_PARAM_STR_LEN 80
  9576. #define MELO_MAX_PARAM_ARRAY_STR_LEN 1
  9577. /* largest allowable parameter string length. */
  9578. #define MELO_MAX_PARAM_INT_ARRAY_LEN 1
  9579. #define MELO_MAX_PARAM_REAL_ARRAY_LEN 1
  9580. #define MELO_MAX_PARAM_STR_ARRAY_LEN 1
  9581. /* maximum number of elements in array of parameter
  9582. data. */
  9583. #define MELO_UNITS_STR_LEN 80
  9584. /* maximum string length of any units associated with a
  9585. param. */
  9586.  
  9587. /* secondly, define output data structures: */
  9588.  
  9589. union melo_param_data {
  9590. int integer;
  9591. double real;
  9592. char string[MELO_MAX_PARAM_STR_LEN+1];
  9593. int integer_array[MELO_MAX_PARAM_INT_ARRAY_LEN];
  9594. double real_array[MELO_MAX_PARAM_REAL_ARRAY_LEN];
  9595. char string_array[MELO_MAX_PARAM_STR_ARRAY_LEN]
  9596.  
  9597. [MELO_MAX_PARAM_ARRAY_STR_LEN+1];
  9598. };
  9599. /* this is for output parameter data. it may either be an
  9600. integer, real, string, array of integers, array of reals, or
  9601. an array of strings. (to save space a union was used.) */
  9602.  
  9603. /* thirdly, define output variables: */
  9604.  
  9605. char melo_descriptor_string[MELO_MAX_DESCRIP_STR_LEN+1];
  9606. /* global storage for the output descriptor string. */
  9607.  
  9608. /* lastly, define output functions (typically return 0 if no
  9609. error): */
  9610.  
  9611. int melo_init(char *descrip_type);
  9612. /* initialize private data structure (melo_datum) to
  9613. accept parameter data from following functions.
  9614. output descriptor type will be descrip_type. returns
  9615. 0 if no errors were encountered. */
  9616. int melo_data(char *param, union melo_param_data *data, char
  9617. *units, int array_len, int unknown_flag);
  9618. /* put data for parameter *param into the proper place
  9619. in melo_datum. returns zero if no errors were
  9620. encountered. */
  9621. void melo(int melo_verbose_flag);
  9622. /* takes the information in melo_datum and translates it
  9623. into melo_descriptor_string. user must set
  9624. melo_verbose_flag = 1 to make output as readable as
  9625. possible, set it equal to zero to make output as
  9626. terse as possible (and still remain in MEL
  9627. format). */
  9628. int melo_file(FILE *melo_file_handle, int melo_verbose_flag);
  9629. /* take the information in melo_datum, translate it into
  9630. melo_descriptor string, and output it to file. */
  9631.  
  9632. #endif /* MEL_OUTPUT */
  9633.  
  9634. /* now define data objects common to both input and
  9635. output: */
  9636.  
  9637. /* if an error occurs, MEL will try and tell you what
  9638. happened. so define required error handling
  9639. information: */
  9640.  
  9641. #define MEL_MAX_ERR_MSG_LEN 80
  9642.  
  9643. struct mel_errors {
  9644. enum { /* which error occured? */
  9645. mel_no_err,
  9646. mel_read_err,
  9647. mel_write_err,
  9648. mel_end_of_file_err,
  9649. mel_end_of_data_err,
  9650. mel_syntax_err,
  9651. mel_unknown_descrip_name_err,
  9652. mel_unknown_param_name_err,
  9653. mel_missing_param_name_err,
  9654. mel_param_data_err,
  9655. mel_missing_paren_err,
  9656.  
  9657. mel_too_many_param_err,
  9658. mel_missing_bracket_err,
  9659. } type;
  9660. int, start_line; /* on which lines did err occur? */
  9661. int end_line; /* (meaningful for input only.) */
  9662. char msg[MEL_MAX_ERR_MSG_LEN+1];
  9663. /* additional info describing err */
  9664. } mel_err; /* (not same as messages below). */
  9665.  
  9666. #define MEL_MAX_NUM_ERR_MESSAGES 13
  9667.  
  9668. #ifdef MEL-INIT
  9669.  
  9670. /* the following describes each type of enumerated error: */
  9671. char mel_err_msg[MEL_MAX_NUM_ERR_MESSAGES]
  9672. [MEL_MAX_ERR_MSG_LEN+1]
  9673. ={"No errors encountered",
  9674. "Can't read file",
  9675. "Can't write file",
  9676. "Unexpected end of file encountered",
  9677. "End of input data encountered",
  9678. "Descriptor/parameter syntax error",
  9679. "Unknown descriptor name",
  9680. "Unknown parameter name",
  9681. "A (or another) parameter name was expected but is "
  9682. "missing",
  9683. "Unable to read parameter value(s) for this "
  9684. "descriptor",
  9685. "Missing right parenthesis while reading units",
  9686. "Too many (or duplicate) parameters given for this "
  9687. "descriptor",
  9688. "Missing brackets around array data"};
  9689.  
  9690. #else
  9691.  
  9692. extern char mel_err_msg[MEL_MAX_NUM_ERR_MESSAGES]
  9693. [MEL_MAX_ERR_MSG_LEN+1];
  9694.  
  9695. #endif /* MEL_INIT */
  9696.  
  9697.  
  9698.  
  9699.  
  9700.  
  9701.  
  9702.  
  9703.  
  9704.  
  9705.  
  9706.  
  9707.  
  9708.  
  9709.  
  9710.  
  9711.  
  9712.  
  9713.  
  9714.  
  9715.  
  9716.  
  9717.  
  9718.  
  9719.  
  9720. Object-Oriented Programming As A Programming Style
  9721.  
  9722.  
  9723. Eric White
  9724.  
  9725.  
  9726. Eric White is a software engineer at Advanced Programming Institute, Ltd. He
  9727. is working on a character-based version of XVT. XVT is a common programming
  9728. interface with implementations for various window systems, including
  9729. Macintosh, Microsoft Windows, Presentation Manager, OSF/Motif, and character
  9730. based on UNIX and MS-DOS. He can be reached at API at (303) 443-4223.
  9731.  
  9732.  
  9733. Object-oriented programming is a programming style that can be used in many
  9734. languages, including C and C++. Some programmers think that C++ gives them the
  9735. ability to do object-oriented programming. This isn't accurate -- C
  9736. programmers can already do object-oriented programming. I will demonstrate by
  9737. showing two identically structured object-oriented programs, one in C and the
  9738. other in C++.
  9739. Even though one can do object-oriented programming in C, C++ offers several
  9740. advantages: C++ supplies syntactic support for object-oriented programming and
  9741. C++ provides type checking where not possible in C.
  9742. I am assuming the reader has already read one of the numerous magazine
  9743. articles that introduce object-oriented programming. A good article is
  9744. "Mapping Object Oriented Concepts Into C++ Language Facilities", CUJ July '89
  9745. by Tsvi BarDavid. If you already know C, an example of object-oriented
  9746. programming in C can clarify exactly what is goes on in object-oriented
  9747. programming. Once you understand the C example, the identical example in C++
  9748. can make learning C++ easier. You can even imagine how the code generated by a
  9749. C++ translator looks.
  9750.  
  9751.  
  9752. The Example
  9753.  
  9754.  
  9755. I'll develop the comparison using a graphical application that could be the
  9756. beginnings of a drawing program such as Mac Draw. This example is constructed
  9757. with four classes of objects: graph_obj, circle, square, and double_circle.
  9758. Three instructions can be given to any one of these objects:
  9759. init, which takes as arguments the initial position and size of the object.
  9760. init initializes the object, then draws it.
  9761. move, which draws the object in black, modifies the position, then draws it in
  9762. white. move takes a change in the y and x coordinates as arguments.
  9763. draw, used by init and move. draw takes a color as an argument.
  9764.  
  9765.  
  9766. The Listings
  9767.  
  9768.  
  9769. Listing 1 is the pseudo-code for the example. The code in Listing 2 (obj.h)
  9770. and Listing 3 (obj.c) facilitates object-oriented programming in C, allowing
  9771. the creation of classes, methods, objects, and implementing inheritance.
  9772. Listing 4 (drawc.c) and Listing 5 (drawcxx.cxx) are two examples of
  9773. object-oriented code in C and C++ respectively. They perform identically.
  9774. In the pseudo-code, you can see:
  9775. We derive classes circle and square from class graph_obj.
  9776. We derive class double_circle from class circle.
  9777. All classes inherit the method move from class graph_obj. If method move needs
  9778. to be invoked for an object of class circle, then method move of class
  9779. graph_obj is actually the function called. We are able to reuse the move
  9780. method for every class in this example.
  9781. Class double_circle inherits the method init from class circle.
  9782. Class double_circle overrides the method draw from class circle. If method
  9783. draw needs to be invoked for an object of class double_circle, then the method
  9784. is not inherited from the super-class.
  9785. For portability, I isolate the graphics functions in a utility module. Listing
  9786. 6 (utility.h) is the interface to the utility module. Listing 7 (utility.c)
  9787. contains fatal() and the graphical functions. The utility module is compiled
  9788. and linked with either the C or C++ code. The isolation also makes it easier
  9789. to compare the two object-oriented examples.
  9790.  
  9791.  
  9792. Object-Oriented Programming In C
  9793.  
  9794.  
  9795. This system implements classes, methods, objects, inheritance, and messages.
  9796. The entire module that facilitates object-oriented programming is less than 90
  9797. lines of code.
  9798. I'll start with a simple data abstraction mechanism, then develop it into a
  9799. system that supports classes, inheritance, and messages.
  9800. The most natural means of creating an object and associating methods with it
  9801. is to put pointers to the methods (pointers to functions) in a structure along
  9802. with the data. A structure for an instance of a circle might look like this:
  9803. struct {
  9804. int y;
  9805. int x;
  9806. int radius;
  9807. void (*init)();
  9808. void (*draw)();
  9809. void (*move)();
  9810. } circle;
  9811. This implements an object that knows how to initialize itself, draw itself,
  9812. and move itself. The implementation could vary for different types (such as a
  9813. double circle). However, we might get tired of setting up the methods every
  9814. time we create a new instance of a circle. A solution is to design another
  9815. structure (called a class) that contains the pointers to the functions, and
  9816. place only a pointer to the class in each object. With this technique we may
  9817. create a class once, then create several objects and have them point to that
  9818. class.
  9819. To make the class structure more generic, we define an array of pointers to
  9820. functions, and by convention, define the methods as an index into this array.
  9821. The code now looks like
  9822. /* defines for methods */
  9823. #define INIT 0
  9824. #define DRAW 1
  9825. #define MOVE 2
  9826.  
  9827.  
  9828. struct class {
  9829. int nbr_methods;
  9830. void (**method)();
  9831. };
  9832.  
  9833. typedef struct class CLASS;
  9834.  
  9835. struct {
  9836. CLASS *class;
  9837. int y;
  9838. int x;
  9839. int radius; }
  9840. circle;
  9841. When creating a class, we need to initialize the array of pointers to
  9842. functions after allocating memory for it. If the method is implemented in the
  9843. class itself, then the pointer is set to the function address. If the method
  9844. is inherited from the super-class, then the pointer is loaded from the
  9845. super-class.
  9846. To make an object more generic, we'll take the definition of the data out of
  9847. the object and replace it with a pointer to the data. Space for the data is
  9848. allocated when the object is created and freed when the object is no longer
  9849. needed. Listing 2 contains the final definitions of structures for class and
  9850. object.
  9851.  
  9852.  
  9853. Classes
  9854.  
  9855.  
  9856. To define a class:
  9857. Define a structure to hold the information about the class. (Listing 6, lines
  9858. 15-18)
  9859. Write the methods (the functions associated with the class). An example is the
  9860. DRAW method for class circle. (Listing 6, lines 69-81)
  9861. Declare a structure of type class. (Listing 6, line 143)
  9862. Call new_class(), which loads the pointers to the inherited methods from the
  9863. super-class. It also saves the size of memory needed for each object in the
  9864. class. (Listing 6, line 160)
  9865. Call reg_method() to register each method that we want to implement in the
  9866. class being created. Registering a method means storing a pointer to a
  9867. function in the array of pointers to functions. reg_method() shouldn't be
  9868. called for methods inherited from the super-class. (Listing 6, lines 161-162)
  9869.  
  9870.  
  9871. Methods
  9872.  
  9873.  
  9874. A method is a function written specifically to go with the class. In this
  9875. example, methods don't return a value.
  9876. All methods should be aware that obj->data is a pointer to the data allocated
  9877. on the heap. For a particular class, this data is of an assumed structure
  9878. type. By casting obj->data to a pointer to a structure, the method can access
  9879. the object data correctly.
  9880. All methods receive the argument arg_ptr, which can be used with the macro
  9881. va_arg() if there are arguments to the method. See your documentation on
  9882. stdarg.h.
  9883.  
  9884.  
  9885. Objects
  9886.  
  9887.  
  9888. The structure that holds what we need to know about an object is:
  9889. typedef struct {
  9890. void *data; 
  9891. CLASS *class;
  9892. } OBJECT;
  9893. To create and use an object:
  9894. Declare a structure of type OBJECT. (Listing 6, line 148)
  9895. Call the function new_object(), which registers a class with the object and
  9896. allocates memory for the object. (Listing 6, line 174)
  9897. Send messages to the object. With the graphical objects in the example, the
  9898. first message that we want to send is the INIT message. (Listing 6, line 175).
  9899. After that, we can send MOVE or DRAW messages. (Listing 6, line 186)
  9900. When done with the object, we call free_object (), which frees the allocated
  9901. memory. (Listing 6, line 191)
  9902.  
  9903.  
  9904. Inheritance
  9905.  
  9906.  
  9907. Inheritance of methods is demonstrated here. circle inherits MOVE from class
  9908. graph_obj. double_circle inherits INIT and MOVE from its super-classes.
  9909. I implement inheritance of data structures by having a sub-class allocate more
  9910. memory than the super-class. The sub-class data consists of the parent-class
  9911. data followed by the data specific to the subclass.
  9912.  
  9913.  
  9914. Messages
  9915.  
  9916.  
  9917.  
  9918. There is a distinction between a message and a method. A message gets sent to
  9919. an object, and then something decides which method to invoke. Invoking a
  9920. method means that the function that is part of the class is called. In C++,
  9921. the translator decides which method to invoke. In the system implemented in C,
  9922. the function message() (Listing 3) decides, based on the class of the object.
  9923.  
  9924.  
  9925. Summary Of OOP In C
  9926.  
  9927.  
  9928. One disadvantage of doing object-oriented programming in C is that there is no
  9929. function prototyping. We have no idea what the arguments to a method are when
  9930. we declare the pointers to functions in the class structure. Programmers are
  9931. responsible for sending the correct parameters to a message.
  9932. Another disadvantage is that when writing methods, the programmer must access
  9933. the data in the object correctly. The pointer to the data in the object
  9934. structure must be cast as a pointer to the correct structure type.
  9935.  
  9936.  
  9937. Object-Oriented Programming In C++
  9938.  
  9939.  
  9940. The C++ example also demonstrates classes, methods, objects, inheritance, and
  9941. messages.
  9942. I'll explain a small subset of the syntax of C++, only what is essential to do
  9943. object-oriented programming. There are many features of C++ that have nothing
  9944. to do with object-oriented programming, and the object-oriented programming
  9945. part of C++ is elaborate, with useful but nonessential features. The subset
  9946. is:
  9947. Definition of a class, with and without a super-class.
  9948. Definition of a method.
  9949. Declaration of an object.
  9950. Sending a message to an object.
  9951.  
  9952.  
  9953. Classes
  9954.  
  9955.  
  9956. The three essential pieces of a class are:
  9957. The data structure of the class.
  9958. The super-class if there is one.
  9959. The methods.
  9960. The definition of a class in C++ looks like:
  9961. class graph_obj {
  9962. public:
  9963. int y;
  9964. int x;
  9965. void init(int y, int x);
  9966. void move(int y, int x);
  9967. virtual void draw(int color){};
  9968. };
  9969. y and x are the data that will be contained in an object of class graph_obj.
  9970. To define methods, you put the function prototype for the methods in the
  9971. definition of the class.
  9972. The class graph_obj doesn't have a super-class. When defining a class where
  9973. there is a super-class, you follow the name of the class by a colon (:), the
  9974. keyword public, and the name of the super-class. For example:
  9975. class circle : public graph_obj {
  9976. public:
  9977. int radius;
  9978. void init(int y, int x, int
  9979. radius);
  9980. void draw(int color);
  9981. };
  9982. Members of a class may be private or public. For simplicity's sake, all
  9983. members of all classes in this example are public. I'm not attempting to do
  9984. data-hiding in this example. Hiding data is a separate (and important) issue,
  9985. but is beyond the scope of this article. The keyword public before the name of
  9986. the super-class means that all the public members of the super-class are
  9987. public members of the sub-class.
  9988.  
  9989.  
  9990. Methods
  9991.  
  9992.  
  9993. The definition of a method looks similar to that of a function. To define the
  9994. name of the function, you follow the class name with the scope resolution
  9995. operator :: and the name of the method. For example, the draw method for class
  9996. circle would look like this:
  9997. void circle::draw(int color)
  9998. {
  9999. /* code to draw a circle */
  10000. ...
  10001.  
  10002. }
  10003. Here is an important note about coding a method. A hidden argument to every
  10004. method is the object. When a method gets invoked for a particular object, by
  10005. definition you get access to that object. You can access the members of that
  10006. object just by using the names of the members.
  10007. Methods are invoked much as functions are called in C. Sometimes, when writing
  10008. code for a method, we want to force a method to be invoked for a super-class,
  10009. and the class for which we are writing the method has a method of the same
  10010. name as the one in the super-class that we want to invoke. In this case, we
  10011. can use the scope resolution operator (::) and force the method to be invoked
  10012. for the super-class. For the init method for class circle, to invoke the init
  10013. method for class graph_obj, we specify the name of the class, followed by the
  10014. scope resolution operator, followed by the name of the method.
  10015. Sometimes the method to invoke at run time can't be determined because a
  10016. particular section of code could be operating on many types of objects. In
  10017. C++, code such as this must be operating on objects of a certain class, or of
  10018. a sub-class of that class. If you declare a method of a class highest in the
  10019. class hierarchy virtual, C++ will wait until run time to make the decision of
  10020. which method to invoke, and will invoke the correct method for the object
  10021. being operated on. To do this, C++ puts something in the object that indicates
  10022. which class it is. Resolution of the method to invoke at run time is called
  10023. late binding.
  10024. This is useful when you send messages to pointers to objects, where the
  10025. pointer could point to one of several classes of objects. It's also useful in
  10026. a method that serves a class and its subclasses. draw is virtual because the
  10027. method move (which uses the method draw) in class graph_obj also serves
  10028. classes circle, double_circle and square.
  10029. In C++, each class can have two special methods: the constructor and
  10030. destructor. Essentially the constructor is called automatically when an object
  10031. comes into scope, and the destructor is called when an object goes out of
  10032. scope. For example, if you declare an automatic object at the start of a
  10033. function, the constructor is called at the time of declaration, and the
  10034. destructor is called before the function returns to its calling function.
  10035. Constructors and destructors are not essential to object-oriented programming.
  10036. In other systems, programmers make a method specifically for initializing an
  10037. object when they need one, then send that message to the object after creating
  10038. it. In the C++ example that accompanies this article, I don't use the built-in
  10039. constructors and destructors. In both the C and C++ examples, I have a method
  10040. that initializes the values of the graphical object. I call this method INIT.
  10041. In the C example, I use a function that allocates memory for the object before
  10042. use and frees the memory after use. These functions aren't defined as part of
  10043. a class and should not be confused with methods.
  10044.  
  10045.  
  10046. Objects
  10047.  
  10048.  
  10049. An object declaration looks like a declaration of something for which there is
  10050. a typedef in C. A declaration of an object of class circle looks like:
  10051. circle c1;
  10052. In the graphics example, immediately after declaring a graphical object the
  10053. init message is sent to the new object. This gives the object its starting
  10054. position and size, and draws it on the screen. Listing 7, line 99 shows
  10055. initialization of a circle at position (40, 40), with a radius of 20.
  10056. After sending the init message, we can send a move message to the object,
  10057. causing it to move on the screen. (Listing 7, line 103-105).
  10058. In the C example, we use a pointer in an object to point to the data specific
  10059. to that instance of the object. new_object() allocates that data on the heap,
  10060. and the function free_obj () frees it.
  10061. In contrast, the C++ translator actually creates a structure that contains the
  10062. data. In our example, this structure is an automatic structure. Space for it
  10063. gets deallocated when main() returns. We don't need to free any data on the
  10064. heap as we needed to do in the C example.
  10065.  
  10066.  
  10067. Inheritance
  10068.  
  10069.  
  10070. Just as in the C example, the C++ example demonstrates inheritance of methods.
  10071. double_circle inherits init and move from class circle.
  10072.  
  10073.  
  10074. Messages
  10075.  
  10076.  
  10077. Sending a message in C looks like:
  10078. message(&c1, MOVE, 1, 1);
  10079. Sending a message in C++ looks like:
  10080. c1.move(1, 1);
  10081. We specify the same essential elements in both cases. They are:
  10082. The object (c1)
  10083. The message (MOVE or move)
  10084. The number of pixels to move in the y and x direction.
  10085.  
  10086.  
  10087. Summary Of OOP In C++
  10088.  
  10089.  
  10090. Data hiding and modularity are important issues in C++ as in other languages.
  10091. I am not addressing these issues and have put the entire program in one source
  10092. file. I want to focus on the object-oriented aspect and keep it simple.
  10093. Often in C++, when a message is sent to an object of a known type, the
  10094. compiler resolves the particular method to invoke at compilation time. This is
  10095. called early binding. In contrast, the function message() in the C scheme
  10096. presented here resolves the issue of which method to invoke at run time. This
  10097. is called late binding.
  10098. Because the C methodology always does late binding, a little more code must
  10099. always be executed at run time. The C code may be a bit slower than the code
  10100. generated by the C++ translator. However, when using virtual functions, I
  10101. believe that the speed of sending a message in C is comparable to C++.
  10102. C++ inherits many of the characteristics of C. In C++, you have the ability to
  10103. corrupt memory in the same ways that you can corrupt memory in C. This causes
  10104. temporal and referential non-localization of bugs. C++ offers the same
  10105. beneficial characteristics of C such as speed, compactness, and the
  10106. possibility of portability.
  10107.  
  10108.  
  10109. Portability
  10110.  
  10111.  
  10112. The C code is quite portable and runs on:
  10113. Microsoft C v5.1
  10114. Microsoft Quick C v2.0
  10115. Zortech C compiler
  10116. The C++ code runs on:
  10117. Zortech C++ compiler
  10118. Glockenspiel C++ translator using the Microsoft C compiler v5.1.
  10119.  
  10120. The graphics code works on CGA, EGA, Hercules and VGA.
  10121. The utility module can use either the graphics library that accompanies
  10122. Microsoft C v5.0 or the graphics library that comes with the Zortech C++
  10123. compiler.
  10124. If you are using the Microsoft graphics library and Hercules graphics, before
  10125. you can run these programs you need to run MSHERC.COM.
  10126. The Zortech graphics library has its origin at the lower-left corner.
  10127. Microsoft has its origin at the upper-left corner. Also, because pixels are
  10128. not square, neither the Zortech nor the Microsoft libraries create perfectly
  10129. round circles. Because this article is focusing on object-oriented techniques
  10130. and not on graphical techniques, I didn't address any of these problems.
  10131.  
  10132.  
  10133. Exercises
  10134.  
  10135.  
  10136. A few valuable exercises might be:
  10137. Make a new class such as a diamond.
  10138. Make a new method such as expand or contract that will change the size of an
  10139. object.
  10140. Adapt this system to another graphical system.
  10141.  
  10142.  
  10143. Acknowledgements
  10144.  
  10145.  
  10146. I thank Marc Rochkind and Tom Cargill, who taught me much of what I know about
  10147. object-oriented programming.
  10148.  
  10149. Listing 1
  10150. Class Graphical Object
  10151. Graphical Object is an abstract class. There will never
  10152. be any instances of this class. Classes Circle and
  10153. Square are subclasses of this class.
  10154. Graphical Object data:
  10155. y position
  10156. x position
  10157. Graphical Object methods:
  10158. INITIALIZE
  10159. Starting y position
  10160. Starting x position
  10161. DRAW
  10162. Only implemented by subclasses
  10163. MOVE
  10164. Arguments:
  10165. Increment in the y direction
  10166. Increment in the x direction
  10167. Send the draw black message to the object (erase
  10168. the object).
  10169. Modify the x and y position of the object per
  10170. the arguments passed to the MOVE method.
  10171. Send the draw white message to the object.
  10172.  
  10173. Class Circle
  10174. Circle is a subclass of class Graphical Object.
  10175. Circle data (in addition to Graphical Object data):
  10176. radius of the circle
  10177. Circle methods:
  10178. INITIALIZE
  10179. Arguments:
  10180. Starting y position
  10181. Starting x position
  10182. Radius
  10183. Send the INITIALIZE message to class Graphical
  10184. Object
  10185. Store the size in the Circle data.
  10186. Send the DRAW message to the Circle.
  10187. DRAW
  10188.  
  10189. Argument:
  10190. Color of the circle to be drawn.
  10191. Draw the circle on the screen.
  10192. MOVE
  10193. Inherited from the class Graphical Object.
  10194.  
  10195. Class Square
  10196. Square is a subclass of class Graphical Object.
  10197. Square data:
  10198. the length of a side of the square
  10199. Square methods:
  10200. INITIALIZE
  10201. Arguments:
  10202. Starting y position
  10203. Starting x position
  10204. Radius
  10205. Send the INITIALIZE message to class Graphical
  10206. Object
  10207. Store the size in the Square data.
  10208. Send the DRAW message to the Square.
  10209. DRAW
  10210. Argument:
  10211. Color of the square to be drawn.
  10212. Draw the square on the screen.
  10213. MOVE
  10214. Inherited from the class Graphical Object.
  10215.  
  10216. Class Double_circle
  10217. Class Double_circle is a subclass of class Circle.
  10218. Double_circle data:
  10219. Same a for a Circle.
  10220. Double_circle methods:
  10221. INITIALIZE
  10222. Inherited from class Circle.
  10223. DRAW
  10224. Argument:
  10225. Color of the Double_circle to be drawn.
  10226. Draw a circle on the screen.
  10227. Draw a slightly smaller concentric circle on the
  10228. screen.
  10229. MOVE
  10230. Inherited from class Circle.
  10231.  
  10232.  
  10233. Listing 2
  10234. 001 /* obj.h - Interface to module for object oriented
  10235. 002 programming in C. */
  10236. 003
  10237. 004 struct class {
  10238. 005 int size; /* size of data */
  10239. 006 int nbr_methods;
  10240. 007 void (**method)();
  10241. 008 };
  10242. 009
  10243. 010 typedef struct class CLASS;
  10244. 011
  10245. 012 typedef struct {
  10246. 013 void *data;
  10247. 014 CLASS *class;
  10248.  
  10249. 015 } OBJECT;
  10250. 016
  10251. 017 void new_class(CLASS *class, CLASS *super_class,
  10252. 018 int nbr_methods, int size);
  10253. 019 void reg_method(CLASS *class, int mth, void (*fcn)());
  10254. 020 void new_object(OBJECT *obj, CLASS *class);
  10255. 021 void message(OBJECT *obj, int msg, ...);
  10256. 022 void free_object(OBJECT *obj);
  10257. 023 void free_class(CLASS *class);
  10258.  
  10259.  
  10260. Listing 3
  10261. 001 #include <stdlib.h>
  10262. 002 #include <stdarg.h>
  10263. 003 #include <stdio.h>
  10264. 004 #include "utility.h"
  10265. 005 #include "obj.h"
  10266. 006
  10267. 007 void new_class(CLASS *class, CLASS *super_class,
  10268. 008 int nbr_methods, int size)
  10269. 009 {
  10270. 010 int x;
  10271. 011 class->nbr_methods = nbr_methods;
  10272. 012 class->size = size;
  10273. 013 class->method =
  10274. 014 (void (**)())malloc
  10275. 015 ((unsigned)(nbr_methods * sizeof (void (*)())));
  10276. 016 for (x = 0; x < nbr_methods; ++x)
  10277. 017 class->method[x] = (void *)NULL;
  10278. 018 if (super_class != NULL)
  10279. 019 for (x = 0; x < super_class->nbr_methods &&
  10280. 020 x < class->nbr_methods; ++x)
  10281. 021 class->method[x] = super_class->method[x];
  10282. 022 }
  10283. 023
  10284. 024 void free_class(CLASS *class)
  10285. 025 {
  10286. 026 free(class->method);
  10287. 027 }
  10288. 028
  10289. 029 /* register a method with a class */
  10290. 030 void reg_method(CLASS *class, int mth, void (*fcn)())
  10291. 031 {
  10292. 032 if (mth < 0 mth >= class->nbr_methods)
  10293. 033 fatal("attempting to register an invalid method");
  10294. 034 class->method[mth] = fcn;
  10295. 035 }
  10296. 036
  10297. 037 /* initialize an object */
  10298. 038 void new_object(OBJECT *obj, CLASS *class)
  10299. 039 {
  10300. 040 void *v;
  10301. 041 obj->class = class;
  10302. 042 v = malloc((unsigned)class->size);
  10303. 043 if (v == NULL)
  10304. 044 fatal("smalloc failed");
  10305. 045 obj->data = (void *)((unsigned char *)v);
  10306. 046 }
  10307. 047
  10308.  
  10309. 048 /* send a message to an object */
  10310. 049 void message(OBJECT *obj, int msg, ...)
  10311. 050 {
  10312. 051 va_list arg_ptr;
  10313. 052 va_start(arg_ptr, msg);
  10314. 053 if (obj->class->method[msg) == NULL)
  10315. 054 fatal("no method for this class");
  10316. 055 (*obj->class->method[msg])(obj, arg_ptr);
  10317. 056 va_end(arg_ptr);
  10318. 057 }
  10319. 058
  10320. 059 /* free the data allocated for an object */
  10321. 060 void free_object(OBJECT *obj)
  10322. 061 {
  10323. 062 free(obj->data);
  10324. 063 }
  10325.  
  10326.  
  10327. Listing 4
  10328. 001 /* interface to utility module */
  10329. 002
  10330. 003 extern int g_white;
  10331. 004 extern int g_black;
  10332. 005
  10333. 006 void fatal(char *s);
  10334. 007 void g_init(void);
  10335. 008 void cleanup(void);
  10336. 009 void g_circle(int y, int x, int radius, int color);
  10337. 010 void g_square(int y, int x, int size, int color);
  10338.  
  10339.  
  10340. Listing 5
  10341. 001 #include <stdlib.h>
  10342. 002 #include <stdarg.h>
  10343. 003 #include <stdio.h>
  10344. 004 #include "utility.h"
  10345. 005 #ifdef __ZTC______LINEEND____
  10346. 006 #include <fg.h>
  10347. 007 #else
  10348. 008 #include <graph.h>
  10349. 009 #endif
  10350. 010
  10351. 011 int g_white;
  10352. 012 int g_black;
  10353. 013
  10354. 014 void fatal(char *s)
  10355. 015 {
  10356. 016 printf("FATAL ERROR: %s\n", s);
  10357. 017 exit(1);
  10358. 018 }
  10359. 019
  10360. 020 void trace(char *fmt, ...)
  10361. 021 {
  10362. 022 static FILE *outfp = NULL;
  10363. 023 va_list arg_ptr;
  10364. 024 va_start(arg_ptr, fmt);
  10365. 025 if (outfp == NULL) {
  10366. 026 unlink("tf");
  10367. 027 if ((outfp = fopen("tf", "w")) == NULL)
  10368.  
  10369. 028 fatal("fopen failed\n");
  10370. 029 setbuf(outfp, NULL);
  10371. 030 }
  10372. 031 vfprintf(outfp, fmt, arg_ptr);
  10373. 032 va_end(arg_ptr);
  10374. 033 }
  10375. 034
  10376. 035 /* utility function to put screen in graphics mode */
  10377. 036 void g_init(void)
  10378. 037 {
  10379. 038 #ifdef_ZTC_____LINEEND____
  10380. 039 fg __init__all();
  10381. 040 g_white = FG_WHITE;
  10382. 041 g_black = FG_BLACK;
  10383. 042 #else
  10384. 043 struct videoconfig this_screen;
  10385. 044 _getvideoconfig(&this_screen);
  10386. 045 switch (this_screen.adapter)
  10387. 046 {
  10388. 047 case _CGA:
  10389. 048 case _OCGA:
  10390. 049 _setvideomode(_HRESBW);
  10391. 050 break;
  10392. 051 case _EGA:
  10393. 052 case _OEGA:
  10394. 053 _setvideomode(_ERESCOLOR);
  10395. 054 break;
  10396. 055 case _VGA:
  10397. 056 case _OVGA:
  10398. 057 case _MCGA:
  10399. 058 _setvideomode(_VRES2COLOR);
  10400. 059 break;
  10401. 060 case _HGC:
  10402. 061 _setvideomode(_HERCMONO);
  10403. 062 break;
  10404. 063 default:
  10405. 064 printf("This program requires a CGA, EGA, MCGA,");
  10406. 065 printf("VGA, or Hercules card\n");
  10407. 066 exit(0);
  10408. 067 }
  10409. 068 g_white = _getcolor();
  10410. 069 g_black = 0;
  10411. 070 #endif
  10412. 071 }
  10413. 072
  10414. 073 /* utility function - wait for a key so we can see
  10415. 074 graphics, set video mode back to character mode */
  10416. 075 void cleanup()
  10417. 076 {
  10418. 077 int ch;
  10419. 078 ch = getchar();
  10420. 079 #ifdef __ZTC______LINEEND____
  10421. 080 fg_term();
  10422. 081 #else
  10423. 082 _setvideomode(_DEFAULTMODE);
  10424. 083 #endif
  10425. 084 /*lint -esym(550,ch) */
  10426. 085 }
  10427. 086 /*lint +esym(550,ch) */
  10428.  
  10429. 087
  10430. 088 void g_circle(int y, int x, int radius, int color)
  10431. 089 {
  10432. 090 #ifdef __ZTC_____LINEEND____
  10433. 091 fg_drawarc((fg_color_t)color, FG_MODE_SET, ~0, x, y,
  10434. 092 radius, 0, 3600, fg_displaybox);
  10435. 093 #else
  10436. 094 _setcolor(color);
  10437. 095 _ellipse(_GBORDER, x - radius, y - radius, x + radius,
  10438. 096 y + radius);
  10439. 097 #endif
  10440. 098 }
  10441. 099
  10442. 100 void g_square(int y, int x, int size, int color)
  10443. 101 {
  10444. 102 #ifdef __ZTC______LINEEND____
  10445. 103 int hs;
  10446. 104 fg_box_t box;
  10447. 105 hs = size / 2;
  10448. 106 box[FG_X1] = x - hs;
  10449. 107 box[FG_Y1] = y - hs;
  10450. 108 box[FG_X2] = x + hs;
  10451. 109 box[FG_Y2] = y + hs;
  10452. 110 fg_drawbox((fg_color_t)color, FG_MODE_SET, ~0,
  10453. 111 FG_LINE_SOLID, box, fg_displaybox);
  10454. 112 #else
  10455. 113 int hs;
  10456. 114 hs = size / 2;
  10457. 115 _setcolor(color);
  10458. 116 _rectangle(_GBORDER, x - hs, y - hs, x + hs, y + hs);
  10459. 117 #endif
  10460. 118 }
  10461.  
  10462.  
  10463. Listing 6
  10464. 001 #include <stdio.h>
  10465. 002 #include <stdlib.h>
  10466. 003 #include <stdarg.h>
  10467. 004 #include "utility.h"
  10468. 005 #include "obj.h"
  10469. 006
  10470. 007 /* methods for graphical_object, circle, double_circle, square */
  10471. 008 #define INIT 0
  10472. 009 #define DRAW 1
  10473. 010 #define MOVE 2
  10474. 011
  10475. 012 /********************************************************/
  10476. 013 /* CLASS GRAPHICAL OBJECT */
  10477. 014
  10478. 015 struct graph_obj_s {
  10479. 016 int y;
  10480. 017 int x;
  10481. 018 };
  10482. 019
  10483. 020 typedef struct graph_obj_s GRAPH_OBJ_T;
  10484. 021 #define GRAPH_OBJ_SIZE sizeof(GRAPH_OBJ_T)
  10485. 022 #define GRAPH_OBJ_OFFSET 0
  10486. 023
  10487. 024 /* graph_obj_init(object, y_position, x_position); */
  10488.  
  10489. 025 void graph_obj_init(OBJECT *obj, va_list arg_ptr)
  10490. 026 {
  10491. 027 GRAPH_OBJ_T *g;
  10492. 028 g = (GRAPH_OBJ_T *)((unsigned char *)obj->data +
  10493. 029 GRAPH_OBJ_OFFSET);
  10494. 030 g->y = va_arg(arg_ptr, int);
  10495. 031 g->x = va_arg(arg_ptr, int);
  10496. 032 }
  10497. 033
  10498. 034 /* graph_obj_move(object, distance_y, distance_x); */
  10499. 035 void graph_obj_move(OBJECT *obj, va_list arg_ptr)
  10500. 036 {
  10501. 037 GRAPH_OBJ_T *g;
  10502. 038 g = (GRAPH_OBJ_T *)((unsigned char *)obj->data +
  10503. 039 GRAPH_OBJ_OFFSET);
  10504. 040 message(obj, DRAW, g_black);
  10505. 041 g->y += va_arg(arg_ptr, int);
  10506. 042 g->x += va_arg(arg_ptr, int);
  10507. 043 message(obj, DRAW, g_white);
  10508. 044 }
  10509. 045
  10510. 046 /********************************************************/
  10511. 047 /* CLASS CIRCLE */
  10512. 048
  10513. 049 struct circle_s {
  10514. 050 int radius;
  10515. 051 };
  10516. 052
  10517. 053 typedef struct circle_s CIRCLE_T;
  10518. 054 #define CIRCLE_SIZE sizeof(CIRCLE_T) + GRAPH_OBJ_SIZE
  10519. 055 #define CIRCLE_OFFSET sizeof(GRAPH_OBJ_T)
  10520. 056
  10521. 057 /* circle_init(object, y_position, x_position, radius); */
  10522. 058 void circle_init(OBJECT *obj, va_list arg_ptr)
  10523. 059 {
  10524. 060 CIRCLE_T *c;
  10525. 061 graph_obj_init(obj, arg_ptr);
  10526. 062 (void)va_arg(arg_ptr, int);
  10527. 063 (void)va_arg(arg_ptr, int);
  10528. 064 c = (CIRCLE_T *)((unsigned char *)obj->data + CIRCLE_OFFSET);
  10529. 065 c->radius = va_arg(arg_ptr, int);
  10530. 066 message(obj, DRAW, g_white);
  10531. 067 }
  10532. 068
  10533. 069 /* circle_draw(object, color); */
  10534. 070 void circle_draw(OBJECT *obj, va_list arg_ptr)
  10535. 071 {
  10536. 072 int color;
  10537. 073 CIRCLE_T *c;
  10538. 074 GRAPH_OBJ_T *g;
  10539. 075 c = (CIRCLE_T *)((unsigned char *)obj->data + CIRCLE_OFFSET);
  10540. 076 g = (GRAPH_OBJ_T *)((unsigned char *)obj->data +
  10541. 077 GRAPH_OBJ_OFFSET);
  10542. 078 color = va_arg(arg_ptr, int);
  10543. 079 /* g_circle(g->y, g->x, c->radius, va_arg(arg_ptr, int)); */
  10544. 080 g_circle(g->y, g->x, c->radius, color);
  10545. 081 }
  10546. 082
  10547. 083 /********************************************************/
  10548.  
  10549. 084 /* CLASS SQUARE (very similar to CIRCLE) */
  10550. 085
  10551. 086 struct square_s {
  10552. 087 int size;
  10553. 088 };
  10554. 089
  10555. 090 typedef struct square_s SQUARE_T;
  10556. 091 #define SQUARE_SIZE sizeof(SQUARE_T) + GRAPH_OBJ_SIZE
  10557. 092 #define SQUARE_OFFSET sizeof(GRAPH_OBJ_T)
  10558. 093
  10559. 094 /* square_init(object, y_position, x_position, size); */
  10560. 095 void square_init(OBJECT *obj, va_list arg_ptr)
  10561. 096 {
  10562. 097 SQUARE_T *s;
  10563. 098 graph_obj_init(obj, arg_ptr);
  10564. 099 (void)va_arg(arg_ptr, int);
  10565. 100 (void)va_arg(arg_ptr, int);
  10566. 101 s = (SQUARE_T *)((unsigned char *)obj->data + SQUARE_OFFSET);
  10567. 102 s->size = va_arg(arg_ptr, int);
  10568. 103 message(obj, DRAW, g_white);
  10569. 104 }
  10570. 105
  10571. 106 /* square_draw(object, color); */
  10572. 107 void square_draw(OBJECT *obj, va_list arg_ptr)
  10573. 108 {
  10574. 109 SQUARE_T *s;
  10575. 110 GRAPH_OBJ_T *g;
  10576. 111 s = (SQUARE_T *)((unsigned char *)obj->data + SQUARE_OFFSET);
  10577. 112 g = (GRAPH_OBJ_T *)((unsigned char *)obj->data +
  10578. 113 GRAPH_OBJ_OFFSET);
  10579. 114 g_square(g->y, g->x, s->size, va_arg(arg_ptr, int));
  10580. 115 }
  10581. 116
  10582. 117 /********************************************************/
  10583. 118 /* CLASS DOUBLE CIRCLE (sub-class of CIRCLE) */
  10584. 119
  10585. 120 #define DOUBLE_CIRCLE_SIZE CIRCLE_SIZE
  10586. 121
  10587. 122 /* double_circle_draw(object, color); */
  10588. 123 void double_circle_draw(OBJECT *obj, va_list arg_ptr)
  10589. 124 {
  10590. 125 int color;
  10591. 126 CIRCLE_T *c;
  10592. 127 GRAPH_OBJ_T *g;
  10593. 128 c = (CIRCLE_T *)((unsigned char *)obj->data + CIRCLE_OFFSET);
  10594. 129 g = (GRAPH_OBJ_T *)((unsigned char *)obj->data +
  10595. 130 GRAPH_OBJ_OFFSET);
  10596. 131 color = va_arg(arg_ptr, int);
  10597. 132 g_circle(g->y, g->x, c->radius, color);
  10598. 133 g_circle(g->y, g->x, c->radius - 2, color);
  10599. 134 }
  10600. 135
  10601. 136 /********************************************************/
  10602. 137
  10603. 138 int main(int argc, char **argv);
  10604. 139 int main(int argc, char **argv)
  10605. 140 {
  10606. 141 int x;
  10607. 142
  10608.  
  10609. 143 CLASS graph_obj;
  10610. 144 CLASS circle;
  10611. 145 CLASS square;
  10612. 146 CLASS double_circle;
  10613. 147
  10614. 148 OBJECT c1;
  10615. 149 OBJECT s1;
  10616. 150 OBJECT dc1;
  10617. 151
  10618. 152 g_init();
  10619. 153
  10620. 154 /* make class graphical object */
  10621. 155 new_class(&graph_obj, NULL, 3, GRAPH_OBJ_SIZE);
  10622. 156 reg_method(&graph_obj, INIT, graph_obj_init);
  10623. 157 reg_method(&graph_obj, MOVE, graph_obj_move);
  10624. 158
  10625. 159 /* make class circle */
  10626. 160 new_class(&circle, &graph_obj, 3, CIRCLE_SIZE);
  10627. 161 reg_method(&circle, INIT, circle_init);
  10628. 162 reg_method(&circle, DRAW, circle_draw);
  10629. 163
  10630. 164 /* make class square */
  10631. 165 new_class(&square, &graph_obj, 3, SQUARE_SIZE);
  10632. 166 reg_method(&square, INIT, square_init);
  10633. 167 reg_method(&square, DRAW, square_draw);
  10634. 168
  10635. 169 /* make class double_circle */
  10636. 170 new_class(&double_circle, &circle, 3, DOUBLE_CIRCLE_SIZE);
  10637. 171 reg_method(&double_circle, DRAW, double_circle_draw);
  10638. 172
  10639. 173 /* make a circle object */
  10640. 174 new_object(&c1, &circle);
  10641. 175 message(&c1, INIT, 40, 40, 20);
  10642. 176
  10643. 177 /* make a square object */
  10644. 178 new object(&s1, &square);
  10645. 179 message(&s1, INIT, 40, 100, 20);
  10646. 180
  10647. 181 /* make a double circle object */
  10648. 182 new_object(&dc1, &double_circle);
  10649. 183 message(&dc1, INIT, 40, 160, 20);
  10650. 184
  10651. 185 for (x = 0; x < 100; ++x) {
  10652. 186 message(&c1, MOVE, 1, 1);
  10653. 187 message(&s1, MOVE, 1, 0);
  10654. 188 message(&dc1, MOVE, 0, -1);
  10655. 189 }
  10656. 190
  10657. 191 free_object(&c1);
  10658. 192 free_object(&s1);
  10659. 193 free_object(&dc1);
  10660. 194
  10661. 195 free_class(&graph_obj);
  10662. 196 free_class(&circle);
  10663. 197 free_class(&square);
  10664. 198 free_class(&double_circle);
  10665. 199
  10666. 200 cleanup();
  10667. 201
  10668.  
  10669. 202 return (0);
  10670. 203 }
  10671.  
  10672.  
  10673. Listing 7
  10674. 001 #include <stdio.h>
  10675. 002 #include <stdarg.h>
  10676. 003 #include "utility.h"
  10677. 004
  10678. 005 /*********************************************************/
  10679. 006 /* CLASS GRAPHICAL OBJECT */
  10680. 007
  10681. 008 class graph_obj {
  10682. 009 public:
  10683. 010 int y;
  10684. 011 int x;
  10685. 012 void init(int y, int x);
  10686. 013 void move(int y, int x);
  10687. 014 virtual void draw(int color){};
  10688. 015 };
  10689. 016
  10690. 017 void graph_obj::init(int y2, int x2)
  10691. 018 {
  10692. 019 y = y2;
  10693. 020 x = x2;
  10694. 021 }
  10695. 022
  10696. 023 void graph_obj::move(int y_delta, int x_delta)
  10697. 024 {
  10698. 025 draw(g_black);
  10699. 026 x += x_delta;
  10700. 027 y += y_delta;
  10701. 028 draw(g_white);
  10702. 029 }
  10703. 030
  10704. 031 /*********************************************************/
  10705. 032 /* CLASS CIRCLE */
  10706. 033
  10707. 034 class circle: public graph_obj {
  10708. 035 public:
  10709. 036 int radius;
  10710. 037 void init(int y, int x, int radius);
  10711. 038 void draw(int color);
  10712. 039 };
  10713. 040
  10714. 041 void circle::init(int y2, int x2, int radius2)
  10715. 042 {
  10716. 043 graph_obj::init(y2, x2);
  10717. 044 radius = radius2;
  10718. 045 draw(g_white);
  10719. 046 }
  10720. 047
  10721. 048 void circle::draw(int color)
  10722. 049 {
  10723. 050 g_circle(y, x, radius, color);
  10724. 051 }
  10725. 052
  10726. 053 /*********************************************************/
  10727. 054 /* CLASS SQUARE */
  10728.  
  10729. 055
  10730. 056 class square: public graph_obj {
  10731. 057 public:
  10732. 058 int size;
  10733. 059 void init(int y, int x, int radius);
  10734. 060 void draw(int color);
  10735. 061 };
  10736. 062
  10737. 063 void square::init(int y2, int x2, int size2)
  10738. 064 {
  10739. 065 graph_obj::init(y2, x2);
  10740. 066 size = size2;
  10741. 067 draw(g_white);
  10742. 068 }
  10743. 069
  10744. 070 void square::draw(int color)
  10745. 071 {
  10746. 072 g_square(y, x, size, color);
  10747. 073 }
  10748. 074
  10749. 075 /*********************************************************/
  10750. 076 /* CLASS DOUBLE_CIRCLE */
  10751. 077
  10752. 078 class double_circle: public circle {
  10753. 079 public:
  10754. 080 void draw(int color);
  10755. 081 };
  10756. 082
  10757. 083 void double_circle::draw(int color)
  10758. 084 {
  10759. 085 g_circle(y, x, radius, color);
  10760. 086 g_circle(y, x, radius - 2, color);
  10761. 087 }
  10762. 088
  10763. 089 /********************************************************/
  10764. 090
  10765. 091 int main(void);
  10766. 092 int main(void)
  10767. 093 {
  10768. 094 int x;
  10769. 095 circle c1;
  10770. 096 square s1;
  10771. 097 double_circle dc1;
  10772. 098 g_init();
  10773. 099 c1.init(40, 40, 20);
  10774. 100 s1.init(40, 100, 20);
  10775. 101 dc1.init(40, 160, 20);
  10776. 102 for (x = 0; x < 100; ++x) {
  10777. 103 c1.move(1, 1);
  10778. 104 s1.move(1, 0);
  10779. 105 dc1.move(0, -1);
  10780. 106 }
  10781. 107 cleanup();
  10782. 108 return (0);
  10783. 109 }
  10784.  
  10785.  
  10786.  
  10787.  
  10788.  
  10789.  
  10790.  
  10791.  
  10792.  
  10793.  
  10794.  
  10795.  
  10796.  
  10797.  
  10798.  
  10799.  
  10800.  
  10801.  
  10802.  
  10803.  
  10804.  
  10805.  
  10806.  
  10807.  
  10808.  
  10809.  
  10810.  
  10811.  
  10812.  
  10813.  
  10814.  
  10815.  
  10816.  
  10817.  
  10818.  
  10819.  
  10820.  
  10821.  
  10822.  
  10823.  
  10824.  
  10825.  
  10826.  
  10827.  
  10828.  
  10829.  
  10830.  
  10831.  
  10832.  
  10833.  
  10834.  
  10835.  
  10836.  
  10837.  
  10838.  
  10839.  
  10840.  
  10841.  
  10842.  
  10843.  
  10844.  
  10845.  
  10846.  
  10847.  
  10848.  
  10849.  
  10850.  
  10851.  
  10852. Tools For MS-DOS Directory Navigation
  10853.  
  10854.  
  10855. Leor Zolman
  10856.  
  10857.  
  10858. Leor Zolman wrote "BDS C", the first C compiler designed exclusively for
  10859. personal computers. Since then he has designed and taught programming
  10860. workshops and has also been involved in personal growth workshops as both
  10861. participant and staff member. He STILL doesn't hold any degrees. His latest
  10862. incarnation is as a CUJ staff member.
  10863.  
  10864.  
  10865. As an MS-DOS user with a large amount of hard disk space to manage, I
  10866. frequently find myself cd-ing all over the system in pursuit of source files
  10867. and data. The standard MS-DOS command processor COMMAND.COM's repertoire of
  10868. options for facilitating system navigation is bare-bones and full of
  10869. idiosyncrasies. For instance, to change directly to an arbitrary drive and
  10870. user area, the user must enter the drive selector and path specification as
  10871. two separate commands. Switching from the root directory of drive C: to the
  10872. \work directory on drive D: requires the command sequence:
  10873. C:\>d: (select D:)
  10874. D:\>cd work (change to the desired directory)
  10875. D:\WORK>...
  10876. (All examples assume the PROMPT environment variable is set to $p$g so that
  10877. COMMAND.COM will display the current path as part of the system prompt.)
  10878. If the user attempts to select a different drive and a path with one command,
  10879. he will find that apparently nothing has happened:
  10880. C:\>cd d:\work
  10881. C:\>...
  10882. Actually, the system has selected the specified path to be active on the
  10883. specified drive, but the specified drive is not selected to be current! The
  10884. system maintains a current working directory for each logical drive. If the
  10885. user were then to select that other drive, i.e.,
  10886. C:\>d:
  10887. D:\WORK>...
  10888. then the selected path would show up as the current directory.
  10889. Another "missing" feature in the standard command environment is a simple
  10890. directory-name aliasing mechanism, so that one can switch quickly to
  10891. commonly-used directories even if the path name happens to be lengthy. MS-DOS
  10892. does provide a simplistic facility (the subst command) to relate an arbitrary
  10893. path to a new drive designator, but subst isn't really adequate: the alias
  10894. name is limited to a single letter and there is no facility for viewing all
  10895. active assignments. I would prefer to have the ability to assign arbitrary
  10896. mnemonics to arbitrary paths, and to have those mnemonics be recognized when
  10897. specified in cd commands.
  10898. I would also like some clean mechanism for instantly switching to the previous
  10899. directory -- even if I've forgotten what it was.
  10900.  
  10901.  
  10902. The Answer
  10903.  
  10904.  
  10905. To address these needs, I wrote an extended CD command that supports combined
  10906. drive and path specifications and a companion command that returns the user to
  10907. the previous directory (taking the directory specification from information
  10908. recorded in an environment variable by the extended cd command). The
  10909. cd-replacement stores the old full path name in an environment variable before
  10910. switching to a new specified path, and the companion command reads this
  10911. environment variable and returns to the original directory upon request. Since
  10912. the extended cd must modify its parent's environment, it uses the functions
  10913. for modifying the master environment which appeared in the July 1989 issue of
  10914. CUJ.
  10915. CDE (for CD Extended) works similarly to MS-DOS's cd command, except for the
  10916. following special cases:
  10917. When both a drive designator and a path name are specified, the specified
  10918. drive is immediately selected together with the path.
  10919. When the argument is identified as the name of an existing MS-DOS environment
  10920. variable, the named variable is assumed to contain a path name to be
  10921. substituted as the path to switch to.
  10922. In support of the "return to previous directory" feature, I decided to
  10923. implement a "directory stack" mechanism. This stack is maintained via
  10924. environment variables, and the user may select the naming convention for those
  10925. variables by customizing the #define statements in CDERET.H. (See Listing 1.)
  10926. One master environment variable (I call it CHAINS) specifies the maximum size
  10927. of the directory stack. When CDE is first invoked, CDE checks to see if the
  10928. CHAINS variable has been previously defined in the environment; if so, its
  10929. current value is used. If not, CDE initializes CHAINS to a default value (also
  10930. specified by a definition in the header file). Thus, the user has the option
  10931. of setting the value of CHAINS explicitly (using the standard built-in command
  10932. set) or allowing CDE to handle the initialization of CHAINS automatically.
  10933. (See Listing 2.)
  10934. A "stack" of size CHAINS is represented by a set of environment variables
  10935. named by a common base name (I use CHAIN) with position numbers appended.
  10936. Thus, with CHAINS=3, after several CDEs the environment variables CHAIN1,
  10937. CHAIN2 and CHAIN3 would be created to store the pertinent path names in the
  10938. environment.
  10939. Every time CDE is used to change directories, it "pushes" the old current
  10940. working directory "on the stack" by reassigning all the relevant environment
  10941. variables. CHAIN1 is always the top of the stack, CHAINn (where n = CHAINS) is
  10942. the base. Since there is no disk activity involved, this process is quite
  10943. fast.
  10944. The RET command (Listing 3) "returns" to the previous directory (either
  10945. specified by CHAIN1 or undefined), then "pops" the stack by reassigning all
  10946. the active environment variables in reverse order.
  10947. As long as CHAINS is greater than 1, then the directory stack behaves as
  10948. described above and successive uses of RET unravel the stack.
  10949. When CHAINS is set to 1, RET considers this a special case: after returning to
  10950. the directory specified by CHAIN1, CHAIN1 is reset to the name of the
  10951. directory that was current at the time of the RET call. Thus, repeated uses of
  10952. RET with CHAINS equal to 1 effect a "toggle" between two directories.
  10953. Depending on the way your system is organized, this toggling mechanism may be
  10954. more useful to you than the directory stack mechanism.
  10955.  
  10956.  
  10957. Icing
  10958.  
  10959.  
  10960. The directory aliasing feature is activated by simply setting an environment
  10961. variable to the full path desired, then using that environment variable name
  10962. as a parameter to CDE. For example,
  10963. C:\>set WORK=d:\project\subproj\
  10964. new\testing
  10965. C:\>cde work
  10966. D:\PROJECT\SUBPROJ\NEW\TESTING>...
  10967. As a special case, for convenience, giving the CDE command without any
  10968. arguments will cause CDE to look for a special environment variable (I call it
  10969. HOME) and switch to the directory it specifies. If you spend much of your time
  10970. headquartered at one particular directory, this is an easy way to go back to
  10971. it from anywhere in the system, regardless of the state of the directory
  10972. stack. The current directory at the time this special form of CDE is given
  10973. will, as usual, be recorded in the environment by CDE in case you want to use
  10974. RET from the HOME directory.
  10975. When setting environment variables in general, be careful not to type spaces
  10976. between the end of the variable name and the = sign. DOS would keep the space
  10977. as part of the variable name, and things wouldn't work. The CDE program will
  10978. handle spaces after the = sign (and before the text) with no problem, but it's
  10979. probably safer to be consistent and use no spaces whatsoever.
  10980.  
  10981.  
  10982. Implementation
  10983.  
  10984.  
  10985. Both CDE.C and RET.C have two phases of operation: the first phase performs
  10986. the required drive/directory selection, and the second phase updates the
  10987. related environment variables. If the first phase fails, then the programs
  10988. exit immediately; there's no need to update environment variables if the
  10989. current directory wasn't changed.
  10990.  
  10991. To obtain the name of the target directory in phase one, RET simply accesses
  10992. the CHAIN1 environment variable. If the variable does not exist, then CDE has
  10993. never been run and an appropriate message is displayed. If CHAIN1 exists, it
  10994. specifies the target path. CDE.C gets its target path name from the command
  10995. line. If the name happens to be the name of an active environment variable,
  10996. then the value of the variable with that name is used to obtain the target
  10997. path.
  10998. The directory selection process itself is identical for both commands and
  10999. takes two steps: the selection of the logical drive and the selection of the
  11000. desired directory. The drive is selected first; if that fails, we quit and no
  11001. harm has been done. Once the new drive has been selected, then the new path is
  11002. selected. If that fails, we have to go back and reinstate the original drive.
  11003. If it succeeds, we're done with phase one.
  11004. Phase two for RET.C is relatively straightforward. If CHAINS is equal to 1,
  11005. then the CHAIN1 environment variable is set to the original current directory
  11006. name (before phase one) in order to support the toggling feature. For other
  11007. values of CHAINS, the directory stack is "popped" by looping to reassign each
  11008. CHAINn variable to the value of its next higher counterpart.
  11009. CDE's phase two begins by making sure the CHAINS environment variable, used to
  11010. specify the stack size, is present and initialized. If it exists, its value is
  11011. assigned to the program variable chaincnt. If CHAINS does not exist, then it
  11012. is initialized to the default value (specified by the symbolic string constant
  11013. DEFAULT_CHAINS).
  11014. Finally the directory stack is "pushed" by copying each CHAINn variable (for n
  11015. = 1 to CHAINS-1) to its next higher counterpart. CHAIN1 is a special case; it
  11016. is assigned to the name of the current directory before phase one was
  11017. completed.
  11018.  
  11019.  
  11020. Configuration
  11021.  
  11022.  
  11023. The following symbolic constants may be changed to suit your own preferences:
  11024. CHAINS_VAR The master directory chain size control variable
  11025. CHAIN_BASE The "base" name of directory stack variables
  11026. DEFAULT_CHAINS The default value for CHAINS_VAR (in quotes)
  11027. HOME_NAME The name of the env. variable for home directory
  11028. The CDE.EXE and RET.EXE commands should be placed in a directory that is
  11029. somewhere in your system search path. (I use c:\bin for all my personal
  11030. utilities.)
  11031.  
  11032.  
  11033. System-Dependent Functions
  11034.  
  11035.  
  11036. The two areas of high compiler-dependency in this application, direct console
  11037. I/O and DOS logical drive selection, have been isolated in a separate utility
  11038. library named UTIL.C (Listing 4). The only support function required by the
  11039. functions in UTIL.C is the bdos function typically supplied with most popular
  11040. compiler libraries.
  11041. If you need to write the bdos function yourself, the prototype is shown at the
  11042. top of the UTIL.C source file. It takes an interrupt (int 21h) function
  11043. number, a DX register value, and an AL register value as parameters (although
  11044. the AL parameter is not needed for this application). The bdos function can
  11045. easily be written in terms of any of the more general operating system
  11046. interface functions (int86(), intdos(), etc) you may have available.
  11047. To keep the commands' .EXE file sizes as short as possible, all messages are
  11048. displayed on the console using direct console I/O calls (through bdos
  11049. facilities) so as not to require the file I/O support to be dragged into the
  11050. linkages. The UTIL.C functions cputs () and putch () are similar to their
  11051. namesakes in the Microsoft library and are provided here for the benefit of
  11052. users of compiler packages that do not include these functions.
  11053. The setdrive() function I provide is cleaner than Microsoft's _dos_setdrive().
  11054. The library functions chdir() and getcwd() are used by the commands and should
  11055. be available in your compiler's standard library.
  11056. When compiled with optimization, both CDE.EXE and RET.EXE weigh in at just
  11057. over 6K, so their load-and-run time is negligible.
  11058.  
  11059.  
  11060. Caveats
  11061.  
  11062.  
  11063. The following line in your CONFIG.SYS file will insure plenty of environment
  11064. space for the CHAIN variables:
  11065. shell = c:\command.com /p /e:1500
  11066. Due to an as-of-yet inexplicable MS-DOS anomaly, specifying too small a value
  11067. for the environment (xxxx in /e:xxxx) may cause the system to hang up after
  11068. CDE or RET completes execution. The message I've gotten says something about
  11069. COMMAND.COM being "invalid". While this has never been destuctive, it has
  11070. required a re-boot of the system. The only way I've found (so far) to avoid
  11071. this problem is to allocate plenty of extra environment space. If anyone has a
  11072. more "bulletproof" solution, please let us know here at CUJ.
  11073. I recommend highly that one modification be made to the Master Environment
  11074. package as listed in the 7/89 CUJ: the environment variable name should be
  11075. converted to upper case both in the m_getenv() and m_delenv() functions. As
  11076. written, only the m_putenv() function converts the name to upper case, and
  11077. this causes failure when either m_getenv() or m_delenv are called with
  11078. lower-case variable names. To make this change, alter the lines reading:
  11079. n = name;
  11080. to:
  11081. n = strupr(name);
  11082. There is one such line near the beginning of both the m_getenv() and
  11083. m_putenv() functions.
  11084.  
  11085.  
  11086. Linking
  11087.  
  11088.  
  11089. The commands to compile and link CDE.C and RET. C using Microsoft C are shown
  11090. at the top of the source file listings. I arbitrarily named the master
  11091. environment package ENVLIB.OBJ, so including envlib on the qcl command line
  11092. links in the object module.
  11093.  
  11094.  
  11095. Summary
  11096.  
  11097.  
  11098. The CDE and RET commands provide a clean, quick and convenient mechanism for
  11099. alleviating some of MS-DOS's command processor limitations. Although there are
  11100. plenty of full-blown command processor replacements, shells and
  11101. special-purpose TSRs out there (even for free) that offer alternative ways to
  11102. "get around" your DOS system, few (if any) of these can offer 100%
  11103. compatibility with all other packages and TSRs, zero bytes of system RAM
  11104. overhead (unless you count the few extra bytes of environment space required),
  11105. and virtually instantaneous gratification. And you even get the source code!
  11106.  
  11107. Listing 1
  11108. /*
  11109. * UTIL.H: Includes and definitions for the CDE/RET
  11110. * Directory Navigation utilities.
  11111. */
  11112.  
  11113.  
  11114.  
  11115. #define MAX_DIRNAME_SIZE 100 /* longest conceivable directory name size */
  11116. #define MAX_EVARNAME_SIZE 20 /* max length of env. var. names created */
  11117. #define DEFAULT_CHAINS "1" /* initial default dir. stack size */
  11118. #define CHAINS_VAR "CHAINS" /* name of env. var. controlling stack size */
  11119. #define CHAIN_BASE "CHAIN" /* base name of env. vars holding dir names */
  11120. #define HOME_NAME "HOME" /* Name of 'home dir' environment variable */
  11121.  
  11122. /*
  11123. * Prototypes for utility functions in CDERET.C:
  11124. */
  11125.  
  11126. void error(char *msg);
  11127. int cputs(char *txt);
  11128. int putch(char c);
  11129. int setdrive(int drive_no);
  11130. int getdrive();
  11131. void change_dir(char *newpath);
  11132.  
  11133. /*
  11134. * Prototypes for Master Environment Control routines
  11135. * (functions from CUJ 7/89)
  11136. */
  11137.  
  11138. char *m_getenv(char *name);
  11139. int m_putenv(char *name, char *text);
  11140. int m_delenv(char *name);
  11141.  
  11142.  
  11143. Listing 2
  11144. /*
  11145. * CDE.C: Extended "cd" command for MS-DOS.
  11146. * Written by Leor Zolman, 9/20/89
  11147. *
  11148. * Features:
  11149. * 1) Allows changing to another drive and directory in one step
  11150. * 2) Supports directory aliasing through environment variables
  11151. * 3) With no arguments, optionally switches to 'home' directory
  11152. * (if the HOME environment variable is currently defined)
  11153. * 3) Manages a "previous directory" stack through environment
  11154. * variables. The number of entries in the stack is dynamically
  11155. * configurable through a special controlling environment variable.
  11156. * 4) For special case of stack size = 1, toggling back and forth
  11157. * between two directories is supported
  11158. *
  11159. * Usage:
  11160. * cde [d:] [path] (changes to given drive/directory)
  11161. * cde <env-var-name> (indirect dir change on environment variable)
  11162. * cde (changes to HOME directory, if defined, or
  11163. * returns current working directory otherwise)
  11164. *
  11165. * Compile/Link:
  11166. * cl /Ox cde.c util.c envlib (where ENVLIB.OBJ is Master Env. Pkg.)
  11167. *
  11168. * Uses the Master Environment library from CUJ 7/89.
  11169. *
  11170. */
  11171.  
  11172. #include <stdio.h>
  11173.  
  11174. #include <dos.h>
  11175. #include <string.h>
  11176. #include <stdlib.h>
  11177. #include "util.h"
  11178.  
  11179. main(int argc, char **argv)
  11180. {
  11181. char *pathp;
  11182. char cwdbuf[MAX_DIRNAME_SIZE]; /* buffer for current dir name */
  11183.  
  11184. int chaincnt; /* size of dir stack */
  11185. char chaincnt_txt[10], *chaincntp;
  11186. char chnevar1[MAX_EVARNAME_SIZE], /* env var names built here */
  11187. chnevar2[MAX_EVARNAME_SIZE];
  11188. char chndname_save[MAX_DIRNAME_SIZE], *chndname;
  11189. char itoabuf[10]; /* used by itoa() function */
  11190. int i;
  11191.  
  11192. /* Get current dir. name and current drive: */
  11193. getcwd(cwdbuf, MAX_DIRNAME_SIZE);
  11194.  
  11195. if (argc == 1) /* if no args given, */
  11196. if (pathp = m_getenv(HOME_NAME)) /* if HOME directory defined, */
  11197. {
  11198.  
  11199. change_dir(pathp); /* then try to change to it. */
  11200. strcpy(chnevar1, CHAIN_BASE); /* set top-stack env var */
  11201. strcat(chnevar1, "1");
  11202. if (m_putenv(chnevar1, cwdbuf)) /* to old dir */
  11203. error("Error setting environment variable");
  11204. return 0;
  11205. }
  11206. else
  11207. { /* just print current working dir */
  11208. cputs(cwdbuf);
  11209. putch('\n');
  11210. return 0;
  11211.  
  11212. }
  11213.  
  11214. if (argc != 2)
  11215. error("Usage: cde [d:][newpath] or <environment-var-name>\n");
  11216.  
  11217. pathp = argv[1]; /* skip whitespace in pathname */
  11218.  
  11219. if (chndname = m_getenv(pathp)) /* if env-var-name given, */
  11220. pathp = chndname; /* use its value as new path */
  11221.  
  11222. change_dir(pathp);
  11223.  
  11224. /* Read or initialize master chain length variable: */
  11225. if ((chaincntp = m_getenv(CHAINS_VAR)) == NULL)
  11226. if (m_putenv(CHAINS_VAR,
  11227. strcpy(chaincntp = chaincnt_txt, DEFAULT_CHAINS)))
  11228. error("Error creating environment variable");
  11229.  
  11230. /* Update the environment directory chain: */
  11231. chaincnt = atoi(chaincntp);
  11232. for (i = chaincnt; i > 0; i--)
  11233.  
  11234. { /* construct name of previous dirname variable: */
  11235. if (i != 1)
  11236. {
  11237. strcpy(chnevar2, CHAIN_BASE);
  11238. strcat(chnevar2, itoa(i-1, itoabuf, 10));
  11239. }
  11240.  
  11241. if (chndname = ((i != 1) ? m_getenv(chnevar2) : cwdbuf))
  11242. { /* copy value of prev. to current */
  11243. strcpy(chndname_save, chndname); /* m_putenv() bashes it */
  11244. strcpy(chnevar1, CHAIN_BASE);
  11245. strcat(chnevar1, itoa(i, itoabuf, 10));
  11246. if (m_putenv(chnevar1, chndname_save))
  11247. error("Error setting environment variable");
  11248. }
  11249. }
  11250. return 0;
  11251. }
  11252.  
  11253.  
  11254. Listing 3
  11255. /*
  11256. * RET.C: Return to previous working directory
  11257. * Written by Leor Zolman, 9/89
  11258. *
  11259. * (companion to CDE.C)
  11260. * Uses the Master Environment package from CUJ 7/89
  11261. *
  11262. * Usage:
  11263. * ret (returns to previous directory)
  11264. *
  11265. * Compile/Link:
  11266. * cl /Ox ret.c util.c envlib (ENVLIB.OBJ is Master Environment pkg)
  11267. */
  11268.  
  11269. #include <stdio.h>
  11270. #include <string.h>
  11271. #include <stdlib.h>
  11272. #include <dos.h>
  11273. #include "util.h"
  11274.  
  11275. main(int argc, char **argv)
  11276. {
  11277.  
  11278. char *pathp;
  11279. char cwdbuf[MAX_DIRNAME_SIZE];
  11280.  
  11281. int chaincnt;
  11282. char chnevar1[MAX_EVARNAME_SIZE], /* env var names built here */
  11283. chnevar2[MAX_EVARNAME_SIZE];
  11284. char chndname_save[MAX_DIRNAME_SIZE], *chndname;
  11285. char itoabuf[10]; /* used by itoa() function */
  11286. int i;
  11287.  
  11288. /* Get current dir. name and current drive: */
  11289. getcwd(cwdbuf, MAX_DIRNAME_SIZE);
  11290.  
  11291. if (argc != 1)
  11292. error("Usage: ret (returns to last dir cde'd from)");
  11293.  
  11294.  
  11295. if ((pathp = m_getenv(CHAINS_VAR)) == NULL)
  11296. error("cde hasn't been run yet");
  11297. else
  11298. chaincnt = atoi(pathp);
  11299.  
  11300. /* See if CDE has created any entries: */
  11301. strcpy(chnevar1, CHAIN_BASE);
  11302. strcat(chnevar1, "1");
  11303. if (!(pathp = m_getenv(chnevar1))) /* if so, pathp points to last dir */
  11304. error("No previous directory"); /* else no previous dir */
  11305.  
  11306. change_dir(pathp); /* change to previous directory: */
  11307.  
  11308. /* Update the environment directory chain: */
  11309. if (chaincnt == 1) /* special case: record old dir */
  11310.  
  11311. {
  11312. if (m_putenv(chnevar1, cwdbuf))
  11313. error("Error setting environment variable");
  11314. return 0;
  11315. }
  11316.  
  11317. for (i = 1; ; i++)
  11318.  
  11319. { /* get name of current dirname variable */
  11320.  
  11321. strcpy(chnevar1, CHAIN_BASE);
  11322. strcat(chnevar1, itoa(i, itoabuf, 10));
  11323.  
  11324. strcpy(chnevar2, CHAIN_BASE);
  11325. strcat(chnevar2, itoa(i + 1, itoabuf, 10));
  11326.  
  11327. if (!(chndname = m_getenv(chnevar2)))
  11328.  
  11329. break; /* found end of saved chain */
  11330.  
  11331. /* copy value of next higher to current */
  11332. strcpy(chndname_save, chndname); /* m_putenv() bashes it */
  11333. strcpy(chnevar1, CHAIN_BASE);
  11334. strcat(chnevar1, itoa(i, itoabuf, 10));
  11335. if (m_putenv(chnevar1, chndname_save))
  11336.  
  11337. error("Error setting environment variable");
  11338. }
  11339. return 0;
  11340. }
  11341.  
  11342.  
  11343. Listing 4
  11344. /*
  11345. * UTIL.C: Utility functions for CDE/RET package
  11346. *
  11347. * These function rely on the "bdos" library function
  11348. * from your compiler's library. Prototype:
  11349. *
  11350. * int bdos(int dosfn, unsigned dosdx, unsigned dosal);
  11351. */
  11352.  
  11353.  
  11354. #include <stdio.h>
  11355. #include <dos.h>
  11356. #include <ctype.h>
  11357. #include "util.h"
  11358.  
  11359. /*
  11360. * Print error msg and abort:
  11361. */
  11362.  
  11363. void error(char *msg)
  11364. {
  11365.  
  11366. cputs("cde: ");
  11367. cputs(msg);
  11368. putch('\n');
  11369. exit(-1);
  11370. }
  11371.  
  11372. /*
  11373. * Change to specified drive/path, terminate program on error:
  11374. */
  11375.  
  11376. void change_dir(char *new_path)
  11377. {
  11378. int old_drive;
  11379.  
  11380. old_drive = getdrive();
  11381.  
  11382. while (*new_path && isspace(*new_path)) /* skip whitespace */
  11383. new_path++;
  11384.  
  11385. if (new_path[1] == ':') /* if drive designator */
  11386. { /* given, then set drive */
  11387. if (setdrive(tolower(*new_path) - 'a'))
  11388. error("Can't select given drive\n");
  11389. new_path += 2;
  11390. }
  11391.  
  11392. if (*new_path && chdir(new_path)) /* If path given, set new path. */
  11393. {
  11394. setdrive(old_drive); /* If error, restore drive */
  11395. error("Can't change to given path");
  11396. }
  11397.  
  11398. }
  11399.  
  11400. /*
  11401. * DOS functions, written in terms of the "bdos" function:
  11402. */
  11403.  
  11404. int cputs(char *txt) /* display msg, console I/O only */
  11405. {
  11406. char c;
  11407.  
  11408. while [c = *txt++)
  11409. {
  11410. if (c == '\n')
  11411. putch('\r');
  11412. putch(c);
  11413.  
  11414. }
  11415. return 0;
  11416. }
  11417.  
  11418. int putch(char c) /* display a char on console */
  11419. {
  11420. return bdos(2, c, 0);
  11421. }
  11422.  
  11423. int setdrive(int drive_no) /* set logical drive. Return */
  11424. { /* non-zero on error. */
  11425. int after;
  11426.  
  11427. bdos(0x0E, drive_no, 0);
  11428. after = bdos(0x19, 0, 0);
  11429. if ((after & 0xff) == drive_no) /* low 8 bits are new drive no. */
  11430. return 0; /* set correctly */
  11431. else
  11432. return -1; /* error. */
  11433. }
  11434.  
  11435. int getdrive() /* return current logical drive */
  11436. {
  11437. return bdos(0x19, 0, 0);
  11438. }
  11439.  
  11440.  
  11441.  
  11442.  
  11443.  
  11444.  
  11445.  
  11446.  
  11447.  
  11448.  
  11449.  
  11450.  
  11451.  
  11452.  
  11453.  
  11454.  
  11455.  
  11456.  
  11457.  
  11458.  
  11459.  
  11460.  
  11461.  
  11462.  
  11463.  
  11464.  
  11465.  
  11466.  
  11467.  
  11468.  
  11469.  
  11470.  
  11471.  
  11472.  
  11473.  
  11474.  
  11475.  
  11476.  
  11477. Dealing With Memory Allocation Problems
  11478. Dear Mr. Ward:
  11479. I am not much of a letter writer, but after reading the July 89 issue of The C
  11480. Users Journal I felt I could save some of your readers a lot of time tracking
  11481. down a problem with the Microsoft C, version 5.10 memory allocation routines.
  11482. Enclosed is a listing and the output from the program.
  11483. This may help Steven Isaacson who is having memory allocation problems using
  11484. Vitamin C. I found this problem after a week of tracking down a memory leak
  11485. problem in a very large application. My final solution was to write my own
  11486. malloc()/free() rountines that call DOS directly. This will let the DOS
  11487. allocator do what is is supposed to do. No time penalty was noticed in our
  11488. application.
  11489. Note if you do write your own malloc()/free() routines, call them something
  11490. else! MSC uses these routines internally and makes assumptions about what data
  11491. is located outside the allocated area. I always use a malloc()/free() shell to
  11492. test for things like memory leaks and the free of a non-allocated block. It
  11493. also will give you an easy way to install a global 'out of memory' error
  11494. handler.
  11495. The code supplied by Leonard Zerman on finding the amount of free space in a
  11496. system is simplistic and very limited. A better routine would build a linked
  11497. list of elements and then the variable vptrarray could be made a single
  11498. pointer to the head of the list. The entire routine becomes dynamic, much more
  11499. robust, and there is no danger of overflowing a statically allocated array.
  11500. See the supplied code for an example.
  11501. The linked list implementation has the side effect that it will work on a
  11502. virtual memory system. Why you would want to do this is beyond me, but it
  11503. could be considered a very time consuming way to find out what swapmax is set
  11504. to on a UNIX system.
  11505. If you have any questions, please contact me. My phone number is (408)
  11506. 988-3818. My fax number is (408) 748-1424.
  11507. Sincerely yours,
  11508. Jim Schimandle
  11509. Primary Syncretics
  11510. 473 Sapena Court, Unit #6
  11511. Santa Clara, CA 95054
  11512. Editor's Note:
  11513. If you couldn't find "Listing 1" in last month's "We Have Mail", you needn't
  11514. fear the onset of any perceptual disorder -- there was no Listing 1. Usually
  11515. publishers blame this kind of problem on someone else -- the printer, the
  11516. typesetter, the proofreader, the paste-up artist. Unfortunately this publisher
  11517. doesn't have any convenient scapegoats; I pasted up the letters section
  11518. (something I often do), and failed to include the listing.
  11519. Anyway, here is the original letter and the promised listing. This time it
  11520. will be right -- my staff is doing it.
  11521. --rlw
  11522.  
  11523. Listing 1
  11524. /*----------------------------------------------------------------------
  11525. ++
  11526. membug.c
  11527. Demonstrate MSC malloc() large size problem
  11528.  
  11529. Description
  11530.  
  11531. membug.c demonstrates a problem that occurs when Microsoft C,
  11532. version 5.10 is used to allocate and free large blocks of
  11533. memory.
  11534.  
  11535. If this program is compiled and run, you will find
  11536. that the first list will have significantly more memory
  11537. allocated to it. The second list will only have 1 to 2
  11538. elements allocated to it, depending on your memory layout.
  11539.  
  11540. The basic problem is that MSC never deallocates a DOS
  11541. allocated memory block, even if the memory call is about to
  11542. fail. Thus, the first list causes the MSC runtime to allocate
  11543. memory in 48K blocks. When the first list is freed, the
  11544. 48K blocks remain. Then, when the second list is allocated,
  11545. there are only 2 blocks that DOS can carve the 60K blocks
  11546. from: the default memory segment and the last DOS memory block.
  11547. The default memory segment is 64K, so we should always get
  11548. an allocation from it. The last memory block can be expanded
  11549. by DOS to fit the 60K request if your memory layout will allow
  11550. it.
  11551.  
  11552. Note that if you reverse the order of memory requests, both
  11553. will return the same number of memory blocks because the 48K
  11554. requests will fit in the 60K blocks.
  11555.  
  11556. Compilation
  11557.  
  11558. Compilation is under Microsoft C, version 5.1 using the command:
  11559.  
  11560. c1 /W3 AL membug.c
  11561.  
  11562.  
  11563. Execution
  11564.  
  11565. Execution of the program should use the command line:
  11566.  
  11567. membug > membug.out
  11568.  
  11569. +-
  11570.  
  11571. $Log$
  11572.  
  11573. --
  11574. */
  11575.  
  11576. #include <stdio.h>
  11577. #include <stdlib.h>
  11578. #include <string.h>
  11579. #include <dos.h>
  11580.  
  11581. /* Local definitions */
  11582. /* ----------------- */
  11583. #define FIRST_ALLOC_SIZE 48000
  11584. #define SECOND_ALLOC_SIZE 60000
  11585.  
  11586. /* Memory allocation list structure */
  11587. /* -------------------------------- */
  11588. typedef struct mb /* Memory list node */
  11589. { /* ---------------------------- */
  11590. struct mb * mb_next ; /* Pointer to next block */
  11591. char mb_data ; /* Start of data area */
  11592. /* Actual data area size is */
  11593. /* determined by runtime */
  11594. /* malloc() argument */
  11595. } MEM_BLOCK ;
  11596.  
  11597. /* Pointer conversion macros */
  11598. /* ------------------------- */
  11599. #define FARPTR_SEG(a) ((int) (((unsigned long) (a)) >> 16))
  11600. #define FARPTR_OFF(a) ((int) ((long) (a)))
  11601. #define MAKE-FARPTR(seg,off) ((void far *) ((((long) (seg)) << 16) +
  11602. (of)))
  11603.  
  11604. /* Function prototypes */
  11605. /* ------------------- */
  11606. void main() ;
  11607. void DOS_Mem_Display(char *) ;
  11608.  
  11609. /*--------------------------------------------------------------------*/
  11610.  
  11611. +
  11612. main
  11613. Entry point for MSC dynamic memory test
  11614.  
  11615. Usage
  11616.  
  11617. void
  11618. main()
  11619.  
  11620. Parameters
  11621.  
  11622.  
  11623. None
  11624.  
  11625. Description
  11626.  
  11627. main() is the entry point for the Microsoft C dynamic memory
  11628. test. The function allocates a list of FIRST_ALLOC_SIZE
  11629. elements, frees the first list, allocates a second
  11630. list of SECOND_ALLOC_SIZE, and frees the second list. The
  11631. statistics printed out are the total bytes allocated by each
  11632. allocation and a dump of the DOS memory list after each
  11633. allocation/free.
  11634.  
  11635. Notes
  11636.  
  11637. None
  11638.  
  11639. -
  11640. */
  11641.  
  11642. void
  11643. main()
  11644. {
  11645. MEM_BLOCK * list ;
  11646. MEM_BLOCK * p ;
  11647. long first_size ;
  11648. long second_size ;
  11649.  
  11650. /* Allocate list using first allocation size */
  11651. /* ----------------------------------------- */
  11652. list = NULL ;
  11653. first_size = 0;
  11654. while ((p = (MEM_BLOCK *) malloc(FIRST_ALLOC_SIZE)) != NULL)
  11655. {
  11656. p->mb_next = list ;
  11657. list = p ;
  11658. firstsize += FIRST_ALLOC_SIZE ;
  11659. }
  11660.  
  11661. /* Print first allocation results */
  11662. /* ------------------------------ */
  11663. printf("***** First allocation - %ld *****\n\n", first_size) ;
  11664. DOS_Mem_Display("After first allocation/n") ;
  11665.  
  11666. /* Free first list */
  11667. /* --------------- */
  11668. while (list != NULL)
  11669. {
  11670. p = list ;
  11671. list = list->mb_next ;
  11672. }
  11673.  
  11674. DOS_Mem_Display("After first free\n") ;
  11675.  
  11676. /* Allocate list using second allocation size */
  11677. /* ------------------------------------------- */
  11678. list = NULL ;
  11679. second_size = 0 ;
  11680. while ((p = (MEM_BLOCK *) malloc(SECOND_ALLOC_SIZE)) != NULL)
  11681. {
  11682.  
  11683. p->mb_next = list ;
  11684. list = p ;
  11685. second_size += SECOND_ALLOC_SIZE ;
  11686. }
  11687.  
  11688. /* Print second allocation results */
  11689. /* ------------------------------- */
  11690. printf("***** Second allocation - %ld *****\n\n", second_size ;
  11691. DOS_Mem_Display("After second allocation\n") ;
  11692.  
  11693. /* Free second list */
  11694. /* ---------------- */
  11695. while (list != NULL)
  11696. {
  11697. p = list ;
  11698. list = list->mb_next ;
  11699. free (p) ;
  11700. }
  11701.  
  11702. DOS_Mem_Display("After second free\n") ;
  11703. }
  11704.  
  11705. /*--------------------------------------------------------------*/
  11706.  
  11707. DOS_Menu_Display
  11708.  
  11709. psp_seg = *(p+1) + ((*(p+2)) << 8) ;
  11710. blk_paras = *(p+3) + ((*(p+4)) << 8) ;
  11711. size = ((long) blk_paras) << 4 ;
  11712. if (psp_seg == 0)
  11713. {
  11714. prg = (unsigned char far *) "(free)" ;
  11715. total += size ;
  11716. }
  11717. else
  11718. {
  11719. ip = (unsigned int far *) MAKE_FARPTR(psp_seg, 0x2c) ;
  11720. prg = MAKE_FARPTR(*ip, 0) ;
  11721. while (*prg != '\0')
  11722. {
  11723. prg += strlen((char *) prg) + 1 ;
  11724. }
  11725. prg += 3 ;
  11726. }
  11727. sprintf(str, "%5d %91d %p", idx++, size, p) ;
  11728.  
  11729. printf("%s\t%s\n", str, prg) ;
  11730.  
  11731. if (*p == 'z'}
  11732. {
  11733. break ;
  11734. }
  11735.  
  11736. p = MAKE_FARPTR(FARPTR_SEG(p) + blk_paras + 1, 0) ;
  11737. }
  11738.  
  11739. sprintf(str, "Total Free: %ld", total) ;
  11740. printf("%s\n\n", str) ;
  11741. }
  11742.  
  11743.  
  11744. /*--------------------------------------------------------------------*/
  11745.  
  11746.  
  11747.  
  11748.  
  11749.  
  11750.  
  11751.  
  11752.  
  11753.  
  11754.  
  11755.  
  11756.  
  11757.  
  11758.  
  11759.  
  11760.  
  11761.  
  11762.  
  11763.  
  11764.  
  11765.  
  11766.  
  11767.  
  11768.  
  11769.  
  11770.  
  11771.  
  11772.  
  11773.  
  11774.  
  11775.  
  11776.  
  11777.  
  11778.  
  11779.  
  11780.  
  11781.  
  11782.  
  11783.  
  11784.  
  11785.  
  11786.  
  11787.  
  11788.  
  11789.  
  11790.  
  11791.  
  11792.  
  11793.  
  11794.  
  11795.  
  11796.  
  11797.  
  11798.  
  11799.  
  11800.  
  11801.  
  11802.  
  11803.  
  11804.  
  11805.  
  11806. Standard C
  11807.  
  11808.  
  11809. Quiet Changes, Part I
  11810.  
  11811.  
  11812.  
  11813.  
  11814. P.J. Plauger
  11815.  
  11816.  
  11817. P.J. Plauger has been a prolific programmer, textbook author, and software
  11818. entrepreneur. He is secretary of the ANSI C standards committee, X3J11, and
  11819. convenor of the ISO C standard committee.
  11820.  
  11821.  
  11822. A language standards committee can commit a variety of sins. It can eliminate
  11823. existing features, so that existing programs that use them generate
  11824. diagnostics with new translators. It can add lots of new features, so that
  11825. existing programs trip over them and generate diagnostics. It can even
  11826. redefine existing features, so that existing programs apparently misuse them
  11827. and generate diagnostics.
  11828. All of these are nasty things to do. A committee that indulges in such sins
  11829. had better be prepared to justify its actions. Discarded features must be
  11830. arguably dangerous, or at least not worth the clutter they cause by remaining
  11831. in the language. Added features must fill a real need and not add to the
  11832. clutter. Changed features require the most justification of all, since they
  11833. cause the greatest disturbance.
  11834. So long as changes cause diagnostics, however, you can live with them. Even if
  11835. you have to convert half a million lines of existing C code, you know how to
  11836. proceed. Stuff your code through the new translator and see where it gripes.
  11837. For very common gripes, you can often contrive a global edit that will
  11838. mechanically fix up the code. For the rest, you at least have your attention
  11839. forcibly directed to the areas where you must manually intervene.
  11840. The worst sin of all for a language standards committee is to make a change
  11841. that does not cause a diagnostic. You have a working program with your
  11842. existing C translator. You upgrade to a standard C compiler and your program
  11843. quietly recompiles. The only problem is, it behaves differently. That is a
  11844. project manager's worst nightmare.
  11845. Even if you generally like the new behavior, you have a serious problem on
  11846. your hands. That half a million lines of existing C code may change its
  11847. behavior in only a handful of places.You cannot rashly assume that the new
  11848. behavior is acceptable every place. (Probably it is not.) You need to locate
  11849. every place and check the implications of the change.
  11850. Committee X3J11 dubbed such alterations "quiet changes." We blanched every
  11851. time we faced the prospect of introducing one. We did our best to avoid them.
  11852. Nevertheless, we occasionally found compelling reasons to adopt quiet changes
  11853. along with various other subtle but noisy changes. So we made sure that we
  11854. documented every quiet change we made in the Rationale that accompanies the
  11855. Standard.
  11856. I discussed the most ambitious of these quiet changes last year. (See
  11857. "Standard C Promotes Types According to Value Preserving Rules," CUJ August
  11858. '88.) The rules for mixing signed and unsigned integer operands in an
  11859. expression were, in the past, both subtle and varied. The Committee discussed
  11860. the different approaches at length before choosing a particular set of
  11861. "promotion" rules. I did my best to present all the arguments and to justify
  11862. the choice we eventually made.
  11863. This column and the next endeavor to summarize all of the quiet changes made
  11864. in Standard C. They may not affect you because there have been numerous
  11865. dialects of C in past practice. (That's a principal reason for making a
  11866. language standard, to eliminate dialects.)
  11867. We labeled something a quiet change if any significant dialect of C quietly
  11868. changed meaning. The change may not affect your favorite dialect.
  11869. Nevertheless, you should be aware of any possibility of a quiet change in C
  11870. code. Who knows, you may already have a lurking problem in code moved from a
  11871. different implementation of C.
  11872. In the explanations that follow, I have copied the description of each quiet
  11873. change almost verbatim from the Rationale for Standard C. They appear in the
  11874. same order as in the Rationale, which reflects the order of topics presented
  11875. in the Standard.
  11876.  
  11877.  
  11878. The Quiet Changes
  11879.  
  11880.  
  11881. "Programs with character sequences such as ??! in string constants, character
  11882. constants, or header names will now produce different results."
  11883. For example,
  11884. printf ("You said what??!\n");
  11885. quietly becomes
  11886. printf ("You said what\n");
  11887. This is the result of introducing trigraphs. The committee felt a compelling
  11888. need to provide a way to represent certain characters unavailable in EBCDIC or
  11889. the invariant subset of ISO 646. (The characters are [\]^{/}~#.) The alternate
  11890. forms had to be representable using just the common subset of characters. They
  11891. also had to be usable within character constants, string literals, and header
  11892. names. Since existing programs can conceivably contain an arbitrary sequence
  11893. of characters in these places, we had no way to satisfy these basic
  11894. requirements without introducing the possibility of a quiet change.
  11895. We settled on trigraphs, or three-character sequences, as a compromise.
  11896. Digraphs might be easier to type, but were more likely to change the meaning
  11897. of older programs. (C uses all of the characters in the subset, so even code
  11898. outside quotes and headers is endangered.) Each trigraph begins with two
  11899. question marks, to minimize the chance of a quiet change. It ends with a
  11900. character from the subset that is designed more or less to suggest the
  11901. replacement character.
  11902. Nobody pretends that ??< is a highly readable alternative to {. But then
  11903. nobody prevents you from filtering your C code before you send it to a
  11904. printer. (You might, for example, overstrike a left parenthesis and a minus
  11905. sign to print a left brace instead of printing the actual trigraph.) Trigraphs
  11906. serve the limited purpose of providing a minimal interchange standard for
  11907. shipping C between countries. (Even the Danes, who are adamant that trigraphs
  11908. are insufficient, have offered no alternative to their use within quotes and
  11909. header names.)
  11910. "A program that depends upon internal identifiers matching only in the first
  11911. (say) eight characters may change to one with distinct objects for each
  11912. variant spelling of the identifier."
  11913. For example,
  11914. int get_stuff_DEF;
  11915.  
  11916. f() {
  11917. extern int get_stuff_REF;
  11918.  
  11919. return (get_stuff_REF); }
  11920. A clever programmer may expect that all the names beginning with get_stuff
  11921. refer to the same data object. That is no longer true.
  11922. There was widespread support for longer names in C. The eight-character
  11923. significance limit inherited from Ritchie's original implementation is
  11924. certainly inadequate. Worse, implementations differed on the treatment of
  11925. "insignificant" characters in a name. (Is an implementation obliged to ignore
  11926. the extra characters when comparing names? Or is it merely permitted to ignore
  11927. them?) Further confusing the issue was the distinct, and more severe, limit on
  11928. external names imposed by old-fashioned linkers.
  11929. The committee decided on a three-tiered limitation on names. First, any name
  11930. can be as long as a logical line. An implementation can choose to inspect all
  11931. characters when comparing names. Second, an implementation must inspect at
  11932. least the first 31 characters. It can choose to look at no more than 31
  11933. characters. Finally, an implementation may require that external names differ
  11934. in the first six characters, and ignore case distinctions.
  11935. These rules were adopted despite a few notorious cases cited of existing
  11936. programs that would quietly change. It seems that some implementations ignore
  11937. characters after the first eight. Some programmers have made a practice of
  11938. intentionally punning by writing distinct names that are intended to compare
  11939. equal. I don't recall the rationale for this practice and I don't care. The
  11940. practice is sufficiently barbaric that it garners little sympathy, even if it
  11941. can be the victim of a quiet change.
  11942. "A program relying on file scope rules may be valid under block scope rules
  11943. but behave differently -- for instance, if d_struct were defined as type float
  11944. rather than struct data in the following example:"
  11945. typedef struct data d_struct {
  11946. /* ... */ };
  11947.  
  11948. first() {
  11949. extern d_struct func();
  11950. /* ... */
  11951. }
  11952.  
  11953.  
  11954. second() {
  11955. d_struct n = func():
  11956. }
  11957. (This example from the Rationale is not wonderful. I even had to fix a small
  11958. bug in reproducing it here.)
  11959. At issue here is the clash between C as a block scoped language and C as a
  11960. "flat" language with separately compiled modules. The former requires that
  11961. names be forgotten at the end of the scope in which they are defined. The
  11962. latter requires that external names be remembered and matched up across
  11963. separate compilations.
  11964. Past implementations differ widely on the treatment of extern declarations
  11965. within function bodies. Do such declarations percolate out, a block at a time,
  11966. to file level so they can be matched up with any other file-level declarations
  11967. for the same name? Or does each such declaration form a worm-hole out to the
  11968. linker, with the worm-hole forgotten at the end of the block? Or does
  11969. something even more bizarre occur?
  11970. The example above can give different results with different interpretations.
  11971. In the first case, the declaration of func percolates out from the first
  11972. function. It is then visible within the second function, so the assignment
  11973. makes sense.
  11974. In the second case, the declaration of func goes out of scope at the end of
  11975. the first function. The second function must assume that func is implicitly
  11976. declared as an external function returning int. In this case, you get a
  11977. diagnostic. But change the type definition to float, as the Rationale
  11978. suggests, and you get a quiet (but erroneous) conversion across the
  11979. assignment.
  11980. Like the previous issue on identifier lengths, here is a case where a quiet
  11981. change is essentially unavoidable. Existing dialects differ too much for the
  11982. standard to contain a common subset of behavior. What the committee chose, in
  11983. fact, was the second behavior. C is a block structured language with holes
  11984. blown in it.
  11985. A translator can diagnose conflicting external declarations within a
  11986. translation unit. It can also elect not to do so, since this is a case of
  11987. "undefined behavior." A linker can diagnose conflicts between separate
  11988. compilations. It can also elect not to do so. In practice, most compilers and
  11989. few linkers will choose to diagnose such conflicts.
  11990. "Unsuffixed integer constants may have different types. In K&R, unsuffixed
  11991. decimal constants greater than INT_MAX, and unsuffixed octal or hexadecimal
  11992. constants greater than UINT_MAX, are of type long."
  11993. For example, on an implementation where type int occupies 16 bits,
  11994. f(32768); /* argument now 16-
  11995. bits */
  11996. i = OxFFFFF / -10; /* divide
  11997. now unsigned */
  11998. This is part of the fallout of choosing value-preserving rules for promoting
  11999. types in expressions (discussed later). The committee felt obliged to tidy up
  12000. the typing rules for integer constants, to maintain a consistent philosophy
  12001. toward preserving the expected value of a sub-expression.
  12002. Ritchie's original rules required that 32768 have type long on an
  12003. implementation where type int occupies 16 bits. That led to occasional
  12004. surprises, particularly when writing arguments on function calls. (There were
  12005. no function prototypes in those days to fix up or diagnose improper argument
  12006. types.) With value-preserving promotion rules, however, you get the expected
  12007. result more often by making 32768 type unsigned int. And such a choice is more
  12008. consistent with the basic philosophy of choosing the "cheapest" type that
  12009. preserves the value of an expression.
  12010. Similarly, octal and hexadecimal integer constants are expected to be
  12011. unsigned. It is silly for one to lose its unsignedness just because its value
  12012. is too large to be represented as type int. Consistency requires that 0x10000
  12013. (on an implementation where type int occupies 16 bits) have type unsigned long
  12014. instead of long.
  12015. In both cases, you can contrive programs that quietly change meaning with the
  12016. change of typing rules for integer constants. The committee felt, however,
  12017. that such programs were already at risk in being moved among existing
  12018. dialects, which supported a variety of promotion rules.
  12019. "A constant of the form '\078' is valid, but now has different meaning. It now
  12020. denotes a character constant whose value is the (implementation-defined)
  12021. combination of the values of the two characters '\07' and '8'. In some
  12022. implementations the old meaning is the character whose code is 078 == 64."
  12023. This is a consequence of now disallowing the digits 8 and 9 in octal escape
  12024. sequences. Even the earliest C compilers have tolerated the practice, and more
  12025. than a few programs have taken advantage of this tolerance. Nevertheless, the
  12026. committee felt it was sufficiently barbarous that it had to be dropped. (The
  12027. committee did not revoke the even more barbarous license to write 111l in
  12028. place of 111L.)
  12029. "A constant of the form '\a' or '\x' now may have different meaning. The old
  12030. meaning, if any, was implementation defined."
  12031. For example,
  12032. char letter = 'a';
  12033.  
  12034. if (letter == '\a') /* no longer
  12035. same as 'a' */
  12036. The backslash is no longer ignored in front of an arbitrary letter. Worse,
  12037. Standard C now gives special meaning to \a.
  12038. The committee felt obliged to add to the list of character escape sequences.
  12039. The sequence \a stands for the "alert" character. In ASCII, it is the BEL code
  12040. that rings the bell on old Teletype terminals and makes some sort of
  12041. electronic beep on modern ones. The sequence \x signals the start of a
  12042. hexadecimal escape sequence of arbitrary length.
  12043. Neither of these escape sequences was officially defined in the past. There
  12044. was the general promise that a backslash before a character with no magic
  12045. meaning simply stood for that character. (I had, in fact, written a number of
  12046. strings that used \x as a place holder to be filled in. That was my tough
  12047. luck.) Nevertheless, the addition could cause a quiet change.
  12048. "A string of the form "\078" is valid, but now has different meaning."
  12049. See above for the same issue with character constants. The only difference is
  12050. that the string literal gets longer. Character constants pack all the
  12051. character codes into a single int value, in an implementation-defined manner.
  12052. "A string of the form "\a" or "\x" now has different meaning."
  12053. See above for the same issue with character constants.
  12054. "It is neither required nor forbidden that identical string literals be
  12055. represented by a single copy of the string in memory; a program depending upon
  12056. either scheme may behave differently."
  12057. For example,
  12058. char *s = "abc";
  12059. .....
  12060. if (s != &"abc"[0])
  12061. printf("s has changed\n");
  12062. The printed message is correct only if both instances of "abc" become the same
  12063. data object. This is not guaranteed in Standard C.
  12064. Here is another case where existing dialects of C were in conflict. Some
  12065. dialects guarantee that identical string literals are represented by a single
  12066. copy within a translation unit. Others guarantee that each string literal
  12067. occupies distinct storage.
  12068. The committee chose to leave the choice up to the implementation. It is
  12069. "unspecified," so the implementation need not document the choice or even be
  12070. consistent in how it chooses. (Another example of unspecified behavior is the
  12071. order in which a program evaluates multiple arguments on a function call.)
  12072. Naturally, any program that depends on some particular behavior is likely to
  12073. be disappointed by some conforming implementation.
  12074. "Expressions of the form x=-3 change meaning with the loss of the old-style
  12075. assignment operators."
  12076. For example,
  12077. i =-3; /* now stores -3 */
  12078. It has been many years since UNIX C reversed the assigning operators. Where
  12079. now you write -= you once wrote =- as in the example above. Programmers who
  12080. are stingy or haphazard with spacing around operators got burned often enough
  12081. that Ritchie switched C to match the Algol 68 convention. Nevertheless, a
  12082. number of commercial C compilers retained the old forms for backward
  12083. compatibility with early C code.
  12084. Disallowing the old forms can, of course, lead to all sorts of nasty puns.
  12085. Those who didn't bite the bullet back in the seventies must do so now.
  12086.  
  12087.  
  12088. Intermission
  12089.  
  12090.  
  12091. That's about half of the quiet changes documented in the Rationale for the C
  12092. standard. Tune in next month for the rest of the story.
  12093.  
  12094.  
  12095.  
  12096.  
  12097.  
  12098.  
  12099.  
  12100.  
  12101.  
  12102.  
  12103.  
  12104.  
  12105.  
  12106.  
  12107.  
  12108.  
  12109.  
  12110.  
  12111.  
  12112.  
  12113.  
  12114.  
  12115.  
  12116.  
  12117.  
  12118.  
  12119.  
  12120.  
  12121.  
  12122.  
  12123.  
  12124.  
  12125.  
  12126.  
  12127.  
  12128.  
  12129.  
  12130.  
  12131.  
  12132.  
  12133.  
  12134.  
  12135.  
  12136.  
  12137.  
  12138.  
  12139.  
  12140.  
  12141.  
  12142.  
  12143.  
  12144.  
  12145.  
  12146.  
  12147.  
  12148.  
  12149.  
  12150.  
  12151.  
  12152.  
  12153.  
  12154.  
  12155.  
  12156.  
  12157.  
  12158.  
  12159.  
  12160. Doctor C's Pointers (R)
  12161.  
  12162.  
  12163. Header Design And Management
  12164.  
  12165.  
  12166.  
  12167.  
  12168. Rex Jaeschke
  12169.  
  12170.  
  12171. Rex Jaeschke is an independent computer consultant, author and seminar leader.
  12172. He participates in both ANSI and ISO C Standards meetings and is the editor of
  12173. The Journal of C Language Translation, a quarterly publication aimed at
  12174. implementers of C language translation tools. Readers are encouraged to submit
  12175. column topics and suggestions to Rex at 2051 Swans Neck Way, Reston, VA, 22091
  12176. or via UUCP at uunet!aussie!rex.
  12177.  
  12178.  
  12179. All too often, programs just "happen." There is little if any serious design
  12180. done, and programmers "design on the fly", using an approach I call stepwise
  12181. refinement. That is, you code a bit and test it then iteratively refine it
  12182. till it's somewhere close to what you think you want. And after you have
  12183. hard-coded the same macro definitions and function declarations in ten
  12184. different places you think perhaps it would be a good idea to create a header
  12185. instead. However, this either doesn't get done or it's done at the local level
  12186. to solve just the particular problem in the code you are currently working on.
  12187. For the most part, I find people program defensively.
  12188. Designing and managing headers is an integral part of a C project design. It
  12189. must be done before any code is written to ensure that the design is
  12190. consistent, can be managed easily, and that a high degree of quality assurance
  12191. can result. The lack of properly designed headers is a likely recipe for added
  12192. development, debugging, and maintenance time, as well as significantly reduced
  12193. reliability.
  12194. There are many aspects to designing headers. In this article I will look at
  12195. those I've recognized. However, before I begin, a definition of the term
  12196. header is in order. I think you all know what a header is but for the purposes
  12197. of this discussion, I will consider a header to be a collection of
  12198. declarations that can be shared across multiple source files via the #include
  12199. preprocessing directive. And while a header is typically represented as a
  12200. source code file on disk, it need not exist as such. For example, a header
  12201. might actually be built into the compiler (at least the standard ones like
  12202. math.h could be) or it could be compiled into some binary form that the
  12203. preprocessor can more easily or efficiently handle. The specific
  12204. representation details are left to the implementer's choice and will not be
  12205. further discussed here. As such, I prefer to use the term header rather than
  12206. header file or include file since the last two names imply a file
  12207. representation. Whatever term you use, be consistent.
  12208.  
  12209.  
  12210. Header Categories
  12211.  
  12212.  
  12213. There are four categories into which headers can be classified: standard,
  12214. system, library, and application.
  12215. A standard header is one of the 15 defined by ANSI C, such as stdio.h, math.h,
  12216. and string.h. ANSI requires you to include standard headers using the notation
  12217. #include <header.h>. Do so even if #include "header.h" appears to work for
  12218. them. A standard header is stored in some special place such that it can be
  12219. accessed from all places in which a source file can be compiled.
  12220. A system header is one supplied by the compiler vendor that can be used to
  12221. interface to and/or exploit the host hardware and/or operating system.
  12222. Examples on MS-DOS systems include bios.h and dos.h; on VAX/VMS, headers
  12223. rms.h, rab.h, and fab.h are used to access the RMS file system; and on UNIX,
  12224. the special set sys\*.h is provided. An implementer can provide as many system
  12225. headers as he needs. VAX C, for example, comes with about 200. Since system
  12226. headers are useful to all applications, they are typically stored in the same
  12227. place as standard headers.
  12228. A library header is one provided with a third-party library such as a windows,
  12229. graphics, or statistical package. Again, a product may include many headers
  12230. and you may use a number of different libraries in the same application.
  12231. Library headers are also universally shareable and will likely reside with
  12232. standard and system headers.
  12233. An application header is one you design for a particular application and as
  12234. such, it should be located in a place separate from headers in the other three
  12235. categories. It is possible, however, that over the course of designing an
  12236. application, you build a header that is useful beyond the life of the current
  12237. system. This header then, should really be treated as a miscellaneous library
  12238. header. If each programmer on the project develops his own private
  12239. miscellaneous headers naming conflicts can easily arise, so you must ensure
  12240. that private headers are not used.
  12241. During testing stages of a project, it can be very tempting to provide a quick
  12242. (and often dirty) fix to a given problem by simply changing a header and
  12243. recompiling the offending source module. However, this can cause other nasty
  12244. side-effects later on when the system as a whole is rebuilt. Also, you must
  12245. never, never, ever even think of changing a standard, system, or library
  12246. header; these are sacred. For example, you might discover you need macros
  12247. called TRUE and FALSE in several modules and since stdio.h is included in all
  12248. of them, why not simply add definitions for these macros to that header?
  12249. Afterall, it can't hurt any existing uses of these headers, can it? Apart from
  12250. reflecting bad style when you next (re)install the compiler, these changes are
  12251. lost. One solution to this is to make all headers, including application
  12252. headers that have been moved to production, read-only. That way, if you should
  12253. ever try to change or overwrite them you are reminded of the seriousness of
  12254. such an action.
  12255.  
  12256.  
  12257. Header Names
  12258.  
  12259.  
  12260. ANSI C requires the standard header names to be written in lower case. Do so
  12261. even if your file system is case insensitive (as is the case with MS-DOS and
  12262. VAX/VMS.) In fact, ANSI does not require that filenames of the form header.h
  12263. be supported by your file system. The compiler must accept #include <stdio.h>,
  12264. but is allowed to map the period or any other part of that header name to
  12265. other characters.
  12266. The convention of naming headers with a .h suffix is exactly that, a
  12267. convention and need not be followed by user-written headers. Certainly, it's a
  12268. useful default convention if you have no good reason to do otherwise.
  12269. If you wish to port code, keep in mind that the length of significance, case
  12270. distinction, and format of filename (assuming a header is a file), are all
  12271. implementation-defined.
  12272. It is generally considered bad style to specify device and or directory
  12273. information in a header name. Considering that almost all compilers provide
  12274. compile-time options and/or environment variables to specify an include search
  12275. path, I see no reason to unduly reduce your flexibility options.
  12276.  
  12277.  
  12278. Header Contents
  12279.  
  12280.  
  12281. Just what should go in a header and how big should headers be? It is
  12282. relatively easy to answer the "what." If something cannot be shared, it does
  12283. not belong in a header. For the record, candidates for inclusion in a header
  12284. are: macros, typedefs, templates for structures, unions, and enumerations, and
  12285. function prototypes, extern data declarations, and preprocessing directives.
  12286. Placing anything else in a header needs careful scrutiny. In particular,
  12287. including executable code that is not inside a macro definition is very bad
  12288. style.
  12289. My rule of thumb is to put all related stuff together in one header. However,
  12290. if that makes for a very large header and the contents can easily be broken
  12291. into logical subsets, then I prefer each subset be in its own header. It's
  12292. useful to give such headers names with the same prefix so you can easily
  12293. determine they are related. The only difference here is whether the
  12294. preprocessor has to process one big header instead of just those parts it
  12295. needs. Don't get too hung up on worrying how much work the preprocessor has to
  12296. do unnecessarily since that's what CPU cycles are for. In fact, in the extreme
  12297. case where you put each declaration in its own header, the preprocessor won't
  12298. need to do any extra work, except for opening and closing all those headers.
  12299. It's quite likely that, while most things will fit neatly into related groups
  12300. each in a header, some miscellaneous bits will be left over. About the only
  12301. way to handle these reasonably is a miscellaneous header. ANSI C has one of
  12302. these, called stddef.h. Whatever organization you chose, everything that can
  12303. be shared should be shared. That is, you should make sure that all macros,
  12304. function prototypes, etc., are part of some header and not hard-coded in
  12305. source files directly.
  12306. Each header should be self-contained. If one header refers to something in
  12307. another header, the first should directly include the second. Forcing the
  12308. programmer to know and remember the order in which related headers need be
  12309. included is burdensome and unnecessary.
  12310.  
  12311.  
  12312. Protecting Header Contents
  12313.  
  12314.  
  12315. It is very likely that in some source modules you will include the same header
  12316. multiple times, once directly and one or more times indirectly via other
  12317. headers. Since everything in a header is supposed to be shareable, there
  12318. should be no problem in processing the same header multiple times except the
  12319. extra work of preprocessing. Right? Well, that's not quite true. Specifically,
  12320. if the same typedef or structure, union, or enumeration template definition is
  12321. seen more than once, the compiler produces an error so they must be somehow
  12322. protected. The best way to achieve this is to place a conditional compilation
  12323. protective wrapper around the whole header as follows:
  12324. /* header local.h */
  12325. #ifndef LOCAL_H
  12326. #define LOCAL_H
  12327. ...
  12328. #endif
  12329.  
  12330. I prefer to use a macro spelled in upper case the same as the header, along
  12331. with a suffix of _H. This naming convention is easy to understand and is very
  12332. unlikely to be used for other macros elsewhere in the set of headers. Using
  12333. something like LOCAL could easily be used as a different macro elsewhere,
  12334. leading to confusion.
  12335. Since the standard headers can also be included multiple times and some of
  12336. them contain typedefs and structure templates, these too must be protected.
  12337. Check those provided with your compiler to see if they indeed are protected.
  12338. The only difference between your wrapper and that used by the standard headers
  12339. is that you must not begin your private macro name with an underscore while
  12340. they must, since that's the implementer's namespace.
  12341. It is preferable to have each thing defined in one, and only one, header.
  12342. However, for various reasons it may be desirable to duplicate something in
  12343. multiple headers. The problem here is to make sure that all of those headers
  12344. containing duplicates can be included at the same time. For example, consider
  12345. the case of having a typedef for count in two headers as in Listing 1.
  12346. You should also check your standard headers for this kind of protection since
  12347. size_t, the type of the sizeof operator, is required to be typedefed in five
  12348. of them. Note that ANSI C places strict rules on whether a standard header can
  12349. include another standard header. For example, most identifiers defined in a
  12350. standard header are only "reserved" if their parent header is included. For
  12351. example, if you don't include one of the six standard headers that define
  12352. NULL, you are perfectly safe in defining your own identifier NULL even though
  12353. it would be bad style. So, if assert includes stdio.h, all the names in
  12354. stdio.h would become defined as well, even though they are not defined in
  12355. assert.h. And while assert.h could contain #undefs to remove these, there is
  12356. no way for it to remove any typedefs or template definitions.
  12357. Many mainstream compilers claiming ANSI conformance or claiming to be tracking
  12358. the ANSI standard break this rule. As such, they are not ANSI-conforming.
  12359. Check your standard headers for this.
  12360.  
  12361.  
  12362. Conditional Inclusion
  12363.  
  12364.  
  12365. There are a number of ways to conditionally include headers as necessary.
  12366. Perhaps the best is to conditionally compile a subset of #include directives
  12367. inside a header, based on the existence or value of a macro defined using a
  12368. compiler option. That is, the compilation path is specified outside all source
  12369. modules. This way, you can trigger any possible conditional compilation path
  12370. from as few as one macro.
  12371. You also have the ANSI invention of #include macro where macro must expand to
  12372. a header name of the form <...> or "...". You also can use the stringize and
  12373. token pasting preprocessor operators # and ## respectively, to construct a
  12374. macro that is to expand to a header name.
  12375. I have also found that it is a good idea to remove as many preprocessing
  12376. directives as possible from source modules into headers. In particular, I find
  12377. conditional compilation directives in source code to be most distracting,
  12378. especially when there are more that two compilation paths. The aim is to
  12379. isolate such dependencies into headers so you can forget about them and get on
  12380. with the business of implementing or maintaining the application. An example
  12381. of this strategy follows:
  12382. #if TARGET == 1
  12383. fp = fopen("DBAO:[direct]master.date",
  12384. "r");
  12385. #else
  12386. fp = fopen("A:\direct\master.date",
  12387. "r");
  12388. #endif
  12389. This can be implemented in a much clearer way by abstracting the filename into
  12390. a header as in Listing 2.
  12391.  
  12392.  
  12393. Planning For Debugging And Maintenance
  12394.  
  12395.  
  12396. People who don't design programs are unlikely to plan for debugging and
  12397. maintenance. They probably don't even write a shopping list for that matter.
  12398. Unfortunately, there are lots of these people programming, many of them in C.
  12399. It is very naive and probably irresponsible to believe that with a non-trivial
  12400. program, debugging will be a mere formality and that you will always be around
  12401. to maintain the code.
  12402. Over the years I have found it a useful idea to include a header called
  12403. something like debug.h into every source file I write when working on a
  12404. non-trivial project. If the header is empty, that's fine. However, it makes it
  12405. very easy to add or change that header's contents and recompile all or part of
  12406. the system for testing. Since you have one header included everywhere, it is
  12407. trivially easy to make powerful changes and to experiment. And the cost of
  12408. having this flexibility is practically nothing, if you cater for it at the
  12409. beginning.
  12410.  
  12411.  
  12412. Concatenating Headers
  12413.  
  12414.  
  12415. There are always people who try to stretch a language's capabilities to the
  12416. extreme. For example, they place part of a source file in one header and the
  12417. rest in another and include them both to form a valid source module. Cute, but
  12418. very bad style.
  12419. Let's look at just what can and cannot be split across multiple source
  12420. modules, and therefore across multiple headers. A source module must contain
  12421. complete tokens. That is, a source token cannot be split across two files.
  12422. Specifically, the notation of backslash/new-line continuation cannot be used
  12423. in the last line of a source file. Likewise, a comment cannot span two files.
  12424. With string literal concatenation now supported by ANSI, you could have a
  12425. string in one file concatenated with a string in another, but that would
  12426. require the strings to be outside a macro definition and I have already said
  12427. that's very bad style. You could also split a structure template definition
  12428. across multiple files, but I see no benefit.
  12429. One thing not immediately obvious in ANSI C is that each matching set of
  12430. #if/endif and corresponding #elif and #else directives must be contained
  12431. within the same source file. That is, the #if and matching #endif directives
  12432. must be in the same source file.
  12433.  
  12434.  
  12435. Conclusion
  12436.  
  12437.  
  12438. I have addressed many issues here most of which have arisen from my own
  12439. experiences. I am sure there are others that could be added. For the most
  12440. part, I find header design to be simply a matter of common sense once you know
  12441. and understand the tools the language and preprocessor provide. But then
  12442. again, I find that to be pretty much the solution to a vast number of
  12443. problems. It's sad that common sense is not all that common.
  12444.  
  12445. Listing 1
  12446. /* h1.h */
  12447.  
  12448. #ifndef H1_H
  12449. #define H1_H
  12450. ...
  12451.  
  12452. #ifndef COUNT_T
  12453. #define COUNT_T
  12454. typedef unsigned int count;
  12455. #endif
  12456.  
  12457. ...
  12458. #endif
  12459. /* h2.h */
  12460.  
  12461.  
  12462. #ifndef H2_H
  12463. #define H2_H
  12464. ...
  12465.  
  12466. #ifndef COUNT_T
  12467. #define COUNT_T
  12468. typedef unsigned int count;
  12469. #endif
  12470.  
  12471. ...
  12472. #endif
  12473.  
  12474. #include "h1.h" /* count defined */
  12475. #include "h2.h" /* count not redefined */
  12476.  
  12477.  
  12478. Listing 2
  12479. /* files.h */
  12480.  
  12481. #if TARGET == 1
  12482. #define MASTER_FILE "DBAO:[direct]master.date" #else
  12483. #define MASTER_FILE "A:\direct\master.date"
  12484. #endif
  12485.  
  12486. /* source.c */
  12487.  
  12488. #include "files.h"
  12489.  
  12490. ...
  12491. fp = fopen(MASTER_FILE, "r");
  12492.  
  12493.  
  12494.  
  12495.  
  12496.  
  12497.  
  12498.  
  12499.  
  12500.  
  12501.  
  12502.  
  12503.  
  12504.  
  12505.  
  12506.  
  12507.  
  12508.  
  12509.  
  12510.  
  12511.  
  12512.  
  12513.  
  12514.  
  12515.  
  12516.  
  12517.  
  12518.  
  12519.  
  12520.  
  12521.  
  12522.  
  12523.  
  12524. On The Networks
  12525.  
  12526.  
  12527. Games And Tongues
  12528.  
  12529.  
  12530.  
  12531.  
  12532. Sydney S. Weinstein
  12533.  
  12534.  
  12535. Sydney S. Weinstein, CDP, CCP is a consultant, columnist, author and President
  12536. of Datacomp Systems, Inc., a consulting and contract programming firm
  12537. specializing in databases, data presentation and windowing, transaction
  12538. processing, networking, testing and test suites and device management for UNIX
  12539. and MSDOS. He can be contacted care of Datacomp Systems, Inc., 3837 Byron
  12540. Road, Huntingdon Valley, PA 19006-2320 or via electronic mail on the
  12541. Internet/Usenet mailbox syd@DSI.COM (dsinc!syd for those that cannot do
  12542. Internet addressing).
  12543.  
  12544.  
  12545.  
  12546.  
  12547. RPN Fans - Here's One For You
  12548.  
  12549.  
  12550. Before I took over David Fiedler's column, he mentioned in his last
  12551. installment the ultimate on-screen calculator for UNIX systems. Here now is a
  12552. simpler one, usable on any system that has a curses package or emulation
  12553. library. It emulates the HP-16C and can popup on both UNIX and MS-DOS. Support
  12554. for floating point, hexadecimal, decimal, octal and binary modes is provided.
  12555. The calculator, written by Emmet Gray of the US Army, has ten registers and
  12556. supports computer-oriented functions. It was posted to comp.sources.misc and
  12557. is available from the archive sites that support that group, including uunet.
  12558.  
  12559.  
  12560. New Games
  12561.  
  12562.  
  12563. New versions of several games were distributed recently in comp.sources.games.
  12564. These include version 4 of Conquer, a middle earth multi-player game for UNIX
  12565. systems. Source to the game itself, as well as the patches, is available from
  12566. the archive sites for comp.sources.games, including uunet. Conquer v4 patches
  12567. are volume 8, issues 1 - 4.
  12568. Nethack has also had a major update in comp.sources.games volume 8, issues
  12569. 6-12. New screens and enhancements were added to this display-oriented
  12570. dungeons and dragons game.
  12571. Galactic Bloodshed, an empire-like war game has also been upgraded this month
  12572. in comp.sources.games, volume 8, issues 26 - 30. This upgrade gives several
  12573. new versions to keep those UNIX systems busy.
  12574. A new game has also appeared, a two-handed card game similar to Bridge and
  12575. Spades (especially two-handed Spades). It's a trick-taking game with a trump
  12576. suit determined by bidding. Cards are drawn from the deck, each player taking
  12577. a turn drawing one card from the top of the deck. If you desire to keep that
  12578. card, it becomes part of your hand and the next card is discarded without
  12579. being seen, otherwise you discard it and take the next card. This yields two
  12580. thirteen-card hands. Bidding is based on the number of tricks you think you
  12581. can take, with the last winner naming the trump. Lastly, the hand is played
  12582. out.
  12583. Scoring is simple; if the bid is made, you score ten times the bid plus the
  12584. number of overtricks. If you go down and don't make the bid, you score
  12585. negative ten times the bid. The winner is the first player to 250 points.
  12586. The author, Scott Turner from UCLA, has asked for help in improving the
  12587. bidding process. He has provided a program with a very interesting set of
  12588. bidding options coded as rule based, neural networks, and then a cheating
  12589. bidder that reads both hands. However, he is not happy with the outcome and is
  12590. asking for help. The program gives ample statistics for tuning a bidding
  12591. algorithm and those of you up to a challenge just might want to take him up on
  12592. his offer for help.
  12593.  
  12594.  
  12595. Back To Work
  12596.  
  12597.  
  12598. Several serious works also appeared recently on the networks. For those
  12599. diehard fans of vi type editors comp.sources.misc recently distributed
  12600. "stevie" (ST Editor for VI Enthusiasts), a public domain clone of UNIX's vi
  12601. editor. This version was developed for the Atari ST, but has since been ported
  12602. to UNIX, OS/2, DOS and Minix-ST. Unsupported ports also included in the
  12603. release include Minix-PC, Amiga, and some Data General systems. Thus, stevie
  12604. appears to be extremely portable. Makefiles are included for all the systems.
  12605. Stevie's main drawback, for some environments, is that it keeps the file being
  12606. edited in memory, limiting the size of the file to be edited for systems with
  12607. smaller addressing spaces or without virtual memory. It was originally written
  12608. by Tim Thompson, but this latest version was posted by Tony Andrews at
  12609. onecom!wldrdg!tony. He also will mail diskettes to those who send him a
  12610. formatted disk along with a self-addressed, stamped disk mailer for returning
  12611. the disk. He can write Atari ST (SS or DS) or MS-DOS (360K or 1.2M) formats.
  12612. His address is Tony Andrews, 5902E Gunbarrel Avenue, Boulder, CO 80301.
  12613. Now that Berkeley has released much of its BSD 4.3-tahoe release to the
  12614. public, sections of it are being ported to UNIX System V and Xenix. Comsat,
  12615. the BSD mail notification daemon, was recently posted to comp.sources.misc.
  12616. Comsat sends messages to users when mail is delivered for them. It uses a
  12617. daemon approach, and thus does not need to wait for the current command to
  12618. complete or the user to type a carriage return to the shell. Also included in
  12619. this port are changes to smail v2.5 necessary for it to notify comsat when
  12620. mail is delivered. Users control whether or not they get notification using
  12621. the biff command, which is also included. Since UNIX System V usually doesn't
  12622. support the Berkeley socket interface, this port uses named pipes, so the
  12623. notification is limited to the local machine. Those with the socket interface
  12624. can use the BSD version of the program. Thanks to David MacKenzie for his
  12625. porting effort.
  12626.  
  12627.  
  12628. Foreign Tongues?
  12629.  
  12630.  
  12631. In volume 8, issues 65-87 comp.sources.misc has distributed a major effort
  12632. that will strike people as either a godsend or totally useless. If you need to
  12633. print foreign languages with their extended character set support, the "cz
  12634. text to PostScript system" is for you. It is a table-driven system that can be
  12635. used to convert any "context-free octet-based character set into PostScript."
  12636. This means that every character in the character set is represented by one or
  12637. more eight-bit bytes and that only the bytes of that character determine what
  12638. it prints, not other bytes in the file. This excludes locking shift sequences.
  12639. Even if you don't need the foreign language support, the posting had an
  12640. addendum called libhoward that includes several C functions to convert numeric
  12641. literals to internal representations and perform string manipulation all with
  12642. error recovery. It's all documented and worth looking at, even just to see how
  12643. he did it, courtesy of Howard Gayle of Ericsson Telecom AB in Sweden
  12644. (howard@dahlbeck.ericsson.se).
  12645.  
  12646.  
  12647. Yea! Its Back, Maybe?
  12648.  
  12649.  
  12650. After a long absence from USENET with no postings, comp.sources.unix
  12651. distributed the first program of Volume 20. It is a contribution from Barry
  12652. Books at IBM releasing into the public domain an include file tester. This
  12653. tester checks include files for POSIX 1003.1 and ANSI compliance. It reports
  12654. missing items, additional items allowed by the standard, and additional items
  12655. not allowed by the standard. References to the standards documents are also
  12656. included in the report. This could prove to be a really useful tool for
  12657. portability.
  12658. Unfortunately, after this promising posting, comp.sources.unix has been quiet
  12659. again. Hopefully, Rich Salz, the moderator, will find time to resume the
  12660. postings shortly.
  12661.  
  12662.  
  12663. Upcoming Releases
  12664.  
  12665.  
  12666.  
  12667. Perl, Larry Wall's Practical Extraction and Report Language, is going though
  12668. its beta period on a new version via alt.sources. Version 3 has lots of new
  12669. features, and next time I will give an in-depth review of this new release
  12670. from one of the net's most respected authors of "Off the Wall Software".
  12671. Less, a more replacement (a display pager) is also in beta test with its
  12672. newest release. alt.sources is wonderful for hints of what is to come. Many
  12673. authors are using it for beta test distributions.
  12674. Another major package is also in its latest beta round; the Extended Portable
  12675. Bitmap Toolkit appeared recently in alt.sources. This set of tools is used to
  12676. convert images from one bitmap format to another. It supports many formats and
  12677. again, next time, a more detailed report.
  12678. If you have a pending release you would like covered in this column, drop me a
  12679. line. My electronic address is syd@DSI.COM and I look forward to hearing from
  12680. you.
  12681.  
  12682.  
  12683.  
  12684.  
  12685.  
  12686.  
  12687.  
  12688.  
  12689.  
  12690.  
  12691.  
  12692.  
  12693.  
  12694.  
  12695.  
  12696.  
  12697.  
  12698.  
  12699.  
  12700.  
  12701.  
  12702.  
  12703.  
  12704.  
  12705.  
  12706.  
  12707.  
  12708.  
  12709.  
  12710.  
  12711.  
  12712.  
  12713.  
  12714.  
  12715.  
  12716.  
  12717.  
  12718.  
  12719.  
  12720.  
  12721.  
  12722.  
  12723.  
  12724.  
  12725.  
  12726.  
  12727.  
  12728.  
  12729.  
  12730.  
  12731.  
  12732.  
  12733.  
  12734.  
  12735.  
  12736.  
  12737.  
  12738.  
  12739.  
  12740. Questions & Answers
  12741.  
  12742.  
  12743. malloc, Porting, And Stack Overflow
  12744.  
  12745.  
  12746.  
  12747.  
  12748. Ken Pugh
  12749.  
  12750.  
  12751. Kenneth Pugh, a principal in Pugh-Killeen Associates, teaches C language
  12752. courses for corporations. He is the author of C Language for Programmers and
  12753. All On C, and is a member on the ANSI C committee. He also does custom C
  12754. programming for communications, graphics, and image databases. His address is
  12755. 4201 University Dr., Suite 102, Durham, NC 27707.
  12756.  
  12757.  
  12758. You can fax me your questions at (919) 493-4390. While you hear the answering
  12759. message, hit the * button on your telephone. Or you can send me e-mail
  12760. kpugh@dukeac.ac.duke.edu (Internet) or dukeac!kpugh (UUCP).
  12761. Q
  12762. I was having problems using malloc on a UNIX machine. After allocating some
  12763. memory with malloc(), I wrote past the end of the allocated memory. The next
  12764. time I called malloc(), it hung up. I ran the same program on an IBM-PC and it
  12765. worked fine. What gives?
  12766. Jim Campbell
  12767. Durham, NC
  12768. A
  12769. Writing beyond (or before) the memory space that is allocated with malloc and
  12770. related functions can cause some serious problems. These functions allocate a
  12771. block of memory from the heap (memory space not used for code, data, and
  12772. stack). They return the address of the memory block. The memory remains
  12773. allocated until you call free(), passing it the address of the block. This
  12774. deallocates the block and returns it to the heap. When the program exits, it
  12775. will free up any allocations you have for which you have not called free().
  12776. These functions look like:
  12777. #include <stdlib.h>
  12778. void *malloc(size_requested)
  12779. size_t size_requested; /* Number of bytes */
  12780.  
  12781. void free(pointer)
  12782. void *pointer; /* Address of memory to free */
  12783. You request an amount of memory in bytes. The function returns to you an
  12784. address which points to the first byte of the allocated memory. You can use
  12785. this memory for any purpose. However, you should not write in the memory
  12786. preceding or following the allocated block.
  12787. The operating system and/or the compiler usually use a few bytes of memory
  12788. adjacent to the allocated block. These bytes, sometimes called the "block
  12789. header", may come before or after the block. The header keeps such information
  12790. as the size of the block allocated, and usually some pointers, including one
  12791. to the next block (i.e., a linked list). If the information in this block
  12792. header is destroyed, the system cannot allocate a new block or deallocate an
  12793. old block. Basically, the block looks something like the diagram in Figure 1.
  12794. Let's assume that the information is kept after the block, as it appears in
  12795. the case of your UNIX machine. You probably did something like:
  12796. char *pc;
  12797. pc = malloc(100);
  12798. ...
  12799. *(pc + 100) = 0;
  12800. ...
  12801. pc1 = malloc(200);
  12802. and overwrote the first byte in the block header. When you attempted the next
  12803. allocation, malloc() hung up as you destroyed the block header for the
  12804. previous block.
  12805. On a PC, the block header typically appears before the allocated memory. In
  12806. that case, your program ran okay, as you were simply writing into unallocated
  12807. memory, which contains no information.
  12808. Depending on the order in which you perform allocations and illegal accesses,
  12809. you could still have problems. For example, let's assume that you performed
  12810. both allocations first, and then an illegal access:
  12811. char *pc;
  12812. char *pc1;
  12813. pc = malloc(100);
  12814. pc1 = malloc(200);
  12815. *(pc + 100) = 0;
  12816. Assuming that you do not attempt to allocate blocks later on in the program,
  12817. this will execute as if no error occurred until the program attempts to exit.
  12818. When the operating system tries to free the allocated memory, it will become
  12819. confused due to the erroneous block header information. You will get a dreaded
  12820. "Memory allocation error -- system halted" message.
  12821. With some compilers, malloc() does not call the operating system routine if
  12822. the request can be satisfied from its own unallocated buffer. In this case,
  12823. you may not see this allocation error, since the exit operations will simply
  12824. free all the buffer at once and not the individual pieces.
  12825. Q
  12826. I am using an array of pointers; each pointer points to a structure; and each
  12827. structure contains several strings of various lengths.
  12828. My array of pointers is declared something like this:
  12829. struct {
  12830. char firstname[MAX_FIRSTNAME+1];
  12831. char lastname[MAX_LASTNAME+];
  12832. char homephone[MAX_HOMEPHONE+1];
  12833. char workphone[MAX_WORKPHONE+1];
  12834. char areacode[MAX_AREACODE+1];
  12835. char street [MAX_STREET+1];
  12836. char city[MAX_CITY+1];
  12837.  
  12838. char state[MAX_STATE+1];
  12839. char comments[MAX_COMMENTS+1];
  12840. } *record[MAX_RECORDS];
  12841. It follows that I could display each element of the structure that represents
  12842. the current record as follows:
  12843. show_record ()
  12844. {
  12845. printf("%s\n",record[current-record]->firstname);
  12846. printf("%s\n",record[current-record]->lastname);
  12847. printf("%s\n",record[current-record]->homephone);
  12848. printf("%s\n",record[current-record]->workphone);
  12849. printf("%s\n",record[current-record]->areacode);
  12850. printf("%s\n",record[current-record]->street);
  12851. printf("%s\n",record[current-record]->city);
  12852. printf("%s\n",record[current-record]->state);
  12853. printf("%s\n",record[current-record]->comments);
  12854. }
  12855. However, it seems that much of the code is unnecessarily duplicated. It would
  12856. be more efficient if I could create a loop and access a different element of
  12857. the structure each time through the loop. My show_record() function would then
  12858. look something like this:
  12859. show_record()
  12860. {
  12861. int i:
  12862.  
  12863. for(i = 0; i < NUM_OF_FIELDS; i++)
  12864. {
  12865. printf("%s\n",record[current_record]->??? );
  12866. }
  12867. }
  12868. Where ??? is the part I can't figure out. I could think of ways to do it in
  12869. assembly language by providing additional data types and accessing them in the
  12870. loop. Since the elements of a structure are usually word aligned, it's hard to
  12871. even be sure how many bytes are between each element of the structure.
  12872. Again, any information you could provide would be greatly appreciated.
  12873. Jonathan Wood
  12874. Irvine, CA
  12875. A
  12876. Accessing individual members of a structure in a loop is a commonly needed
  12877. operation. There are several ways that you can do this. Let me change your
  12878. structure template slightly and add a tag-type. I normally avoid declaring
  12879. variables when declaring a structure template, eliminating the need to declare
  12880. those variables when you use the template in another program. A clean
  12881. structure template is a handy thing to have around because it makes declaring
  12882. variables of the same structure a breeze.
  12883. struct s_record
  12884. {
  12885. char firstname[MAX_FIRSTNAME + 1];
  12886. ...
  12887. };
  12888. struct s_record *record[MAX_RECORDS];
  12889. You could use a static variable, which will have constant addresses and set up
  12890. an array of pointers to those addresses. show_record() might then look like:
  12891. static struct s_record print_record;
  12892.  
  12893. #define NUMBER_FIELDS 9
  12894. char *record_field_address[NUMBER_FIELDS] =
  12895. {
  12896. &print_record.firstname,
  12897. &print_record.lastname,
  12898. ...
  12899.  
  12900. /* Remainder of the fields */
  12901. };
  12902.  
  12903. show_record()
  12904. {
  12905. int i;
  12906. /* Copy in the record to be printed */
  12907. print_record = *record[current_record];
  12908.  
  12909. for (i=0; i < NUMBER_FIELDS; i++)
  12910. {
  12911.  
  12912. printf("%s\n", record_field_address[i]);
  12913. }
  12914. }
  12915. One feature in the new ANSI standard, the offsetof() macro, can help you out
  12916. here. Its syntax is:
  12917. #include <stddef.h>
  12918. offsetof( type, member-name)
  12919. The type is a structure type and the member-name is a member in the structure.
  12920. Instead of keeping the address of individual members in an array, you simply
  12921. keep the offsets from the start of a structure. For example,
  12922. #define NUMBER_FIELDS 9
  12923. size_t record_offsets[NUMBER_FIELDS] =
  12924. {
  12925. offsetof(struct s_record, firstname),
  12926. offsetof(struct s_record, lastname),
  12927. ...
  12928. /* Remainder of the fields */
  12929. };
  12930. Now show_record could look something like:
  12931. show_record()
  12932. {
  12933. int i;
  12934. char *pc;
  12935. pc = (char *) &record[current_record];
  12936. for (i=0; i < NUMBER_FIELDS; i++)
  12937. {
  12938. printf("%s\n", pc + record_offsets[i]);
  12939. }
  12940. }
  12941. Note that the conversion of the address to a char pointer is necessary. If you
  12942. simply printed out &record[current_record] + record_offsets[i], you would get
  12943. the address of something which is record_offsets[i] * sizeof(struct s_record)
  12944. after the beginning of record.
  12945. I would suggest that you change the calling sequence of show_record so that it
  12946. expects a record (or an address of a record). This way, you can print out
  12947. records that are not part of the array (such as a record that might be used
  12948. for input purposes).
  12949. show_record(record)
  12950. /* Prints out a record */
  12951. struct s_record record;
  12952. {
  12953. int i;
  12954. char *pc;
  12955. pc = (char *) record;
  12956. for (i=0; i < NUMBER_FIELDS; i++)
  12957. {
  12958. printf("%s\n", pc + record_offsets[i]);
  12959. }
  12960. }
  12961. or
  12962. show_record(precord)
  12963. /* Prints out a record, whose address is passed */
  12964. struct s_record *record;
  12965. {
  12966. int i;
  12967. char *pc; pc = (char *) precord;
  12968. for (i=0; i < NUMBER_FIELDS; i++)
  12969. {
  12970. printf("%s\n", pc + record_offsets[i]);
  12971. }
  12972. }
  12973. You might want to be even more organized and create another structure that
  12974. contains not only the offsets, but also the names of the members, so that you
  12975. can use the same names everywhere you print the record.
  12976. struct s_field
  12977. {
  12978. char name[MAX_FIELD_NAME + 1];
  12979. size_t offset;
  12980. }
  12981.  
  12982.  
  12983. #define NUMBER_FIELDS 9
  12984. struct s_field fields[NUMBER_FIELDS] =
  12985. {"First name", offsetof(struct s_record,
  12986. firstname)},
  12987. {"Last name", offsetof(struct s_record, lastname)},
  12988. ...
  12989. /* Remainder of the fields */
  12990. };
  12991. With this you might have a function like:
  12992. show_record_with_field_names(precord)
  12993. /* Prints out a record, whose address is passed */
  12994. struct s_record *record;
  12995. {
  12996. int i;
  12997. char *pc;
  12998. pc = (char *) precord;
  12999. for (i=0; i NUMBER_FIELDS; i++)
  13000. {
  13001. printf("%-20.20s: %s\n",
  13002. fields[i].name,
  13003. pc + fields[i].record_offsets);
  13004. }
  13005. }
  13006. You should note that elements of a structure are not necessarily word aligned.
  13007. On a PC, they can be byte aligned or word aligned. I prefer packed (i.e., byte
  13008. alignment) structures, in order to save space, but there is a slight element
  13009. of speed in using non-packed structures. Note that the sizeof() operator and
  13010. the offsetof() macro take into account any padding bytes (unused bytes due to
  13011. alignment). In fact, it is the potential presence of padding bytes that made
  13012. the ANSI committee eliminate the equality comparison of structures. For
  13013. example:
  13014. func()
  13015. {
  13016. static struct s_record record_1;
  13017. struct s_record record_2;
  13018.  
  13019. if (record_1 == record_2)
  13020. ...
  13021. }
  13022. The padding bytes in record_1 will be set to 0, since it is a static variable.
  13023. The padding bytes in record_2 will be garbage, since record_2 is an automatic
  13024. variable. You could use the fields array shown above to create a structure
  13025. comparison function, if you required it.
  13026. Q
  13027. How do you make a binary data file that is portable between the MAC and the
  13028. IBM PC?
  13029. Richard Walton
  13030. Wellesley, MA
  13031. A
  13032. Porting data files between any two systems presents a problem in that the
  13033. representation of the numbers varies from computer to computer. A common way
  13034. of avoiding this problem is to output the data to an text file using fprintf()
  13035. and to read the data on the other machine using fscanf(). For example, on one
  13036. machine you would have:
  13037. struct_s record
  13038. {
  13039. int one_number;
  13040. double another_number;
  13041. };
  13042. write_record_to_file(data_file, record)
  13043. FILE *data_file;
  13044. struct s_record record;
  13045. {
  13046. int ret;
  13047. ret = fprintf(data_file, "%d %lf\n",
  13048. record.one_number, record.another_number);
  13049. return ret;
  13050. }
  13051. On the other machine, you would use:
  13052. read_record_from_file(data_file, precord)
  13053. FILE *data_file;
  13054. struct s_record *precord;
  13055. {
  13056. int ret;
  13057.  
  13058. ret = fscanf(data_file, "%d %lf", &(precord->
  13059. one_number), &(precord->another_number) );
  13060. return ret;
  13061. }
  13062. If you do not wish to have the overhead of the conversions done by fprintf()
  13063. and fscanf(), then you will need to write some specific code. For example,
  13064. suppose on an IBM you have written out the records as:
  13065. write_record_to_file(data_file, record)
  13066. FILE *data_file;
  13067. struct s_record record;
  13068. {
  13069. int ret;
  13070. ret = fwrite(&record, sizeof(struct s_record), 1,
  13071. data_file);
  13072. return ret;
  13073. }
  13074. On the other machine, you will have to rearrange the bit patterns manually:
  13075. #define SIZE_BUFFER 8 /* Size of record on other
  13076. machine */
  13077. read_record_to_file(data_file, precord)
  13078. FILE *data_file;
  13079. struct s_record *precord;
  13080. {
  13081. int ret;
  13082. char buffer[SIZE_BUFFER];
  13083. ret = fread(&buffer, SIZE_BUFFER, 1, data_file);
  13084.  
  13085. /* Now you need to convert each value
  13086. individually */
  13087. convert_ibm_int_to_mac_int(&buffer[0]°,
  13088. & (precord->one_number);
  13089. convert_ibm_double_to_mac_double((&buffer[2],
  13090. & (precord->another_number);
  13091. return ret;
  13092. }
  13093. Now each of the individual members must be dealt with separately. The double
  13094. conversion is a bit of a bear. As they say in the teaching business, it is
  13095. reserved as an exercise for the student. The integer conversion might look
  13096. like:
  13097. convert_ibm_int_to_mac_int(pibm_number,pmac_number)
  13098. char *pibm_number;
  13099. char *pmac_number;
  13100. {
  13101. /* Reverse the byte order */
  13102. *(pmac_number) = *(pibm_number + 1);
  13103. *(pmac_number + 1) = *(pibm_number);
  13104. }
  13105. Note that I have simply shown a return value for each of these file functions.
  13106. You probably want to be more clever and test the functions so that the return
  13107. value is consistent among all the functions. For example, the first function
  13108. might look like:
  13109. #define BAD_IO 1
  13110. #define GOOD_IO 0
  13111.  
  13112. write record_to_file (data_file, record)
  13113. FILE *data file;
  13114. struct s_record record;
  13115. {
  13116. int ret;
  13117. int io_ret;
  13118. ret = fprintf(data_file, "%d %lf\n",
  13119. record.one_number,record.another_number);
  13120. if (ret < 1)
  13121. io_ret = BAD_IO;
  13122. else
  13123. io_ret = GOOD_IO;
  13124. return io_ret;
  13125.  
  13126. }
  13127. Q
  13128. I am in the process of implementing hotkey-controlled real-time data
  13129. acquisition for some laboratory experiments. This is being achieved by taking
  13130. control of the keyboard interrupt number 0x09. My compiler is Microsoft C
  13131. v5.1.
  13132. The experimental apparatus has three distinct modes of operation: A, B, and C,
  13133. which are to begin upon the striking of their respective keys from the
  13134. keyboard. Assume that task A, defined by its function, fA(), is currently
  13135. executing and that the user now strikes the key to commence task B, similarly
  13136. defined by its function, fB(), so that fA() stops and fB() starts. My question
  13137. is this: Can you continually interrupt function i and start function j and
  13138. expect to escape a stack overflow? How does one handle suspending a function
  13139. at an arbitrary time with no a priori intention of returning to it (which
  13140. would free the stack space used by the function). I would imagine that you
  13141. could do this a few times, but what about suspending A and starting B (or C)
  13142. an arbitrary number of times? Perhaps setjmp() and longjmp() are the solution.
  13143. Another serious problem that concerns me is that my method does not seem to
  13144. admit a way to signal end-of-interrupt to the keyboard handler (or to whatever
  13145. is listening). Because the directives to begin execution of function A, B, or
  13146. C are embedded in the new 0x09 interrupt handler, the handler could
  13147. potentially never finish executing during the experiment. Is there a better
  13148. implementation which can achieve what I need and still use hotkeys?
  13149. Mark S. Petrovic
  13150. Stillwater, OK
  13151. A
  13152. You are right in your concern over stack overflow. If you keep calling an
  13153. interrupt function without clearing up the stack (i.e., with an IRET
  13154. instruction), you will eventually run out of stack space. An interrupt
  13155. function that might cause overflow could look like the following, where
  13156. keyboard_input() is a function that gets the actual keystroke.
  13157. control_function()
  13158. /* This will only be called if a keyboard
  13159. interrupt */
  13160. {
  13161. int c;
  13162.  
  13163. /* Get the key that was hit */
  13164. c = keyboard_input();
  13165.  
  13166. switch(c)
  13167. {
  13168. case 'A':
  13169. function_a();
  13170. break;
  13171. case 'B':
  13172. function_b();
  13173. break;
  13174. case 'C':
  13175. function_c();
  13176. break;
  13177. default:
  13178. function_default();
  13179. break;
  13180. }
  13181. /***** This function never returns *****/
  13182. }
  13183.  
  13184. function_A()
  13185. {
  13186. /* Code to perform function A */
  13187. }
  13188. function_B()
  13189. {
  13190. /* Code to perform function B */
  13191. }
  13192. function_C()
  13193. {
  13194. /* Code to perform function C */
  13195. }
  13196. function_default()
  13197. {
  13198. /* Code to perform default function */
  13199. }
  13200. Everytime you invoke the interrupt, another set of flags and return addresses
  13201. are pushed onto the stack. set_jmp/longjmp provide an appropriate mechanism
  13202. for implementing the sort of structure you desire. These two functions allow
  13203. you to set a place marker in your code (setjmp) and then jump directly back to
  13204. it from another routine (longjmp). Without setjmp/longjmp to report an error
  13205. that occurred several levels deep in a program, you could return an error
  13206. value at every level as you exit the nested calls. With setjmp/longjmp you can
  13207. instead simply jump back to a central error handler and give it the error
  13208. value. The function calls are:
  13209. #include <setjump.h>
  13210. int setjmp(environment)
  13211. jmp_buf environment; /* Will hold the place
  13212. information */
  13213. and
  13214. void longjmp( environment, return_value)
  13215. jmp_buf environment;
  13216.  
  13217. /* Place information from setjmp */
  13218. int return_value; /* To be returned to setjmp */
  13219. setjmp() returns 0 the first time it is invoked. The calling function can test
  13220. this and ignore any error condition. When longjmp() is called, the next C
  13221. instruction to be executed is the equivalent of a return from setjmp(). This
  13222. returns execution to the place marked by the call to setjmp(). One of the
  13223. parameters to longjmp() is a non-zero value which was setjmp()'s return value.
  13224. longjmp() cleans up the stack from any nested function calls.
  13225. The parameter passed to setjmp() is of type jmp_buf. This variable holds
  13226. information regarding the current position of the stack. You can call setjmp()
  13227. in many different places and pass it different variables of type jmp_buf. The
  13228. value passed to longjmp() determines to which of the setjmp() calls it will
  13229. return. The code below gives an indication of how your problem might be
  13230. programmed. You would connect this up to the keyboard interrupt.
  13231. #include <setjmp.h>
  13232. #define TRUE 1
  13233. #define FALSE 0
  13234. control_function()
  13235. /* This will only be called if a keyboard
  13236. interrupt */
  13237. {
  13238. int c; /* Character input */
  13239. int ret; /* Return value from setjmp() */
  13240. jmp_buf environment; /* For the setjmp */
  13241. static int init = FALSE; /* First time through flag */
  13242.  
  13243. if (init)
  13244. {
  13245. /* Stop previous execution */
  13246. longjmp(environment, 1);
  13247. }
  13248. ret = setjmp(environment);
  13249. if (ret == 0)
  13250. {
  13251. /* This is the return from the initial setup */
  13252. init = TRUE;
  13253. }
  13254. else
  13255. {
  13256.  
  13257. /* This is the return from the longjmp */
  13258. ;
  13259. }
  13260. /* Get the key that was hit */
  13261. c = keyboard_input();
  13262.  
  13263. switch (c)
  13264. {
  13265. case 'A':
  13266. function_a();
  13267. break;
  13268. case 'B':
  13269. function_b();
  13270. break;
  13271. case 'C':
  13272. function_c();
  13273. break;
  13274. default:
  13275. function default();
  13276. break;
  13277. }
  13278. /******* THIS FUNCTION NEVER RETURNS */
  13279. }
  13280. Alternatively, you could avoid using an interrupt by coding each function to
  13281. periodically check for something on the keyboard stack. This approach does
  13282. kludge up your lower level functions. However, if the lower level functions
  13283. have sections of code that should not be interrupted, then this less elegant
  13284. method may be preferable. Two Microsoft (and some other compiler) functions
  13285. (not ANSI standard) support this alternate approach. The kbhit() function
  13286. returns non-zero if there is a key in the buffer. The getch() function returns
  13287. the character in the buffer, without waiting for a carriage return.
  13288. #include <setjmp.h>
  13289. #define TRUE 1
  13290. #define FALSE 0
  13291.  
  13292. main()
  13293.  
  13294. {
  13295. int c;
  13296.  
  13297. /* Get the key the first key*/
  13298. while (1)
  13299. {
  13300. c = getch();
  13301.  
  13302. switch (c)
  13303. {
  13304. case 'A':
  13305. function_a();
  13306. break;
  13307. case 'B':
  13308. function_b();
  13309. break;
  13310. case 'C':
  13311. function_c();
  13312. break;
  13313. default:
  13314. function_default();
  13315. break;
  13316. }
  13317.  
  13318. } /* End while loop */
  13319.  
  13320. function_A()
  13321. {
  13322.  
  13323. /* Code to perform function A */
  13324. /* Inside each loop: */
  13325. if (kbhit()) return;
  13326.  
  13327. }
  13328. function_B()
  13329. {
  13330. /* Code to perform function B */
  13331. /* Inside each loop: */
  13332. if (kbhit()) return;
  13333. }
  13334.  
  13335. function_C()
  13336. {
  13337. /* Code to perform function C */
  13338. /* Inside each loop: */
  13339. if (kbhit()) return;
  13340. }
  13341. Figure 1
  13342.  
  13343.  
  13344.  
  13345.  
  13346.  
  13347.  
  13348.  
  13349.  
  13350.  
  13351.  
  13352.  
  13353.  
  13354.  
  13355.  
  13356.  
  13357.  
  13358.  
  13359.  
  13360.  
  13361.  
  13362.  
  13363.  
  13364.  
  13365.  
  13366.  
  13367.  
  13368.  
  13369.  
  13370.  
  13371.  
  13372.  
  13373.  
  13374.  
  13375.  
  13376.  
  13377.  
  13378.  
  13379.  
  13380.  
  13381.  
  13382.  
  13383.  
  13384.  
  13385.  
  13386.  
  13387.  
  13388.  
  13389.  
  13390.  
  13391.  
  13392.  
  13393.  
  13394.  
  13395.  
  13396.  
  13397.  
  13398.  
  13399.  
  13400.  
  13401.  
  13402.  
  13403.  
  13404.  
  13405.  
  13406.  
  13407.  
  13408.  
  13409.  
  13410.  
  13411.  
  13412.  
  13413.  
  13414.  
  13415.  
  13416.  
  13417. New Releases
  13418.  
  13419.  
  13420. A New Year's Wish List
  13421.  
  13422.  
  13423.  
  13424.  
  13425. Kenji Hino
  13426.  
  13427.  
  13428. Kenji Hino is a member of The C Users' Group technical staff. He holds a
  13429. B.S.C.S. from McPherson College and an undergraduate degree in metalurgy from
  13430. a Japanese university. He is currently working toward an M.S.C.S. at the
  13431. University of Kansas.
  13432.  
  13433.  
  13434.  
  13435.  
  13436. New Releases
  13437.  
  13438.  
  13439.  
  13440.  
  13441. CUG299 -- MEL and BP
  13442.  
  13443.  
  13444. This volume contains two programs, MEL -- Universal Metalanguage Data
  13445. Processor submitted by George Crews (TN), and BP -- Back Propagation for
  13446. neural networks by Ronald Michaels (TN).
  13447. MEL provides an I/O interface between a program and the user. It can take
  13448. input data written in "pseudo-English" and translate it into program
  13449. variables. It can also translate a program's variables into pseudo-English.
  13450. (See the article on page 33 in this issue.) MEL was originally designed for
  13451. use with engineering analysis programs. It was written in ANSI C and was
  13452. developed using Microsoft C v5.1. The disk includes MEL source code, a test
  13453. example program, sample input and output files, documentation, and the article
  13454. and listings from this issue. Since MEL provides only a processor engine, you
  13455. need to define your own input and output data format rule (called a
  13456. dictionary) for your application program in mel.h.
  13457. BP is a simple implementation of the back propagation algorithm as an example
  13458. of a neural network. The implementation is based upon the article in Nature,
  13459. "Learning representations by back propagating errors" by Rummelhart, Hinton
  13460. and Williams. BP employs an adaptive algorithm that converges as result of
  13461. learning. BP was developed on an AT clone with a math coprocessor using
  13462. Zortech C v1.07. The disk also includes the Hercules graphics version of BP.
  13463.  
  13464.  
  13465. CUG300 -- MAT_LIB
  13466.  
  13467.  
  13468. Our first volume in the 300's is a shareware package, MAT_LIB -- Matrix
  13469. Library submitted by John J. Hughes III (TN).
  13470. MAT_LIB includes approximately 50 C language functions and macros which input
  13471. and output tabular data maintained in ASCII text files. While the tabular data
  13472. is in RAM, it is stored in dynamically-allocated token or floating-point
  13473. arrays on the heap.
  13474. Functions are provided to examine an ASCII text file to determine the number
  13475. of rows, columns, and token size of the tabular data in the file. Other C
  13476. macros dimension either a floating-point or string token array large enough to
  13477. hold the ASCII data. Once in memory, floating-point array matrix operations
  13478. can be performed on the data. Token array data can be converted to and from
  13479. float or integer values. Floating-point arrays which have been modified by
  13480. calculation can be merged into token arrays for output or they can be output
  13481. to a text file directly. The output text files can in turn be used as the
  13482. input for later application programs found in MAT_LIB text file formats.
  13483. The disk includes a users manual, test programs, example programs, and small
  13484. and medium model libraries for Turbo C. The library source can be obtained for
  13485. $20 from the author (John Hughes III, 928 Brantley Dr., Knoxville, TN 37923).
  13486.  
  13487.  
  13488. CUG301 -- BGI Applications
  13489.  
  13490.  
  13491. This volume contains graphics applications that use Borland Graphics
  13492. Interfaces (BGI) submitted by three authors, Mark A. Johnson (CO), Henry M.
  13493. Pollock (MA), and John Muczynski (MI). All programs were compiled with Turbo C
  13494. and use BGI files. The disk includes C source code, executable code and BGI
  13495. files.
  13496. Mark A. Johnson has created DCUWCU -- a simple application environment that
  13497. provides a mouse-driven cursor, stacked pop-up menus, and forms that contain
  13498. editable fields and a variety of selectable buttons. The sample program DRAW
  13499. allows you to draw lines, circles, and text on the screen using a mouse. A
  13500. stacked pop-up menu can be invoked anywhere on the screen (Figure 1). DRAW
  13501. uses public domain Microsoft mouse routines written by Andrew Markley (CUJ
  13502. Sept/Oct 1988). An article describing DCUWCU appeared in the Jan '89 issue of
  13503. CUJ (p. 67).
  13504. Henry M. Pollock has submitted a demonstration program combining trig
  13505. functions and graphics functions in Turbo C. By selecting an option from the
  13506. menu, the program displays circleoids, asteroids, spirals, cycloids (Figure
  13507. 2), etc.
  13508. My review of the JJB library in the October 1989 issue prompted John Muczynski
  13509. to create a graphics pull-down menu system with deeply nested menus. The
  13510. separate include code allows you to change key assignments and create macros.
  13511. The new configuration may be saved and restored. He also has submitted an
  13512. example program, "Conway's game of life," using the pull-down menu.
  13513.  
  13514.  
  13515. Updates
  13516.  
  13517.  
  13518.  
  13519.  
  13520. CUG295 -- blkio Library
  13521.  
  13522.  
  13523. The blkio library released in the November issue has been updated. Version 1.1
  13524. includes minor bug fixes and modifications.
  13525.  
  13526.  
  13527.  
  13528. Retrospective
  13529.  
  13530.  
  13531. CUG started collecting and maintaining public domain source code (originally
  13532. just BDS C source code) nine years ago. The library started with just ten
  13533. standard CP/M 8-inch disks. Currently, the total number of volumes (one volume
  13534. includes one to three 360K MS-DOS disks) has surpassed 300.
  13535. The past nine years have brought remarkable changes in C compiler technology
  13536. and in the microcomputer marketplace. Figure 3 shows the change in formats
  13537. requested by our members. Over the past three years, CP/M has become virtually
  13538. extinct and MS-DOS has come to dominate. More interesting, however, is the
  13539. diversity of operating systems used in recent years. Macintosh, UNIX/Xenix,
  13540. Atari and Amiga have appeared more than ever -- indicating that more and more
  13541. programmers who use non-MS-DOS operating systems are interested in C and are
  13542. seeking portable C source code. I think this trend is strong evidence that C
  13543. is a portable language.
  13544. Table 1 shows the 20 most popular disks in the last three years.
  13545. The most-ordered CUG disk is MicroEmacs v3.9 (CUG#197 and CUG#198). MicroEmacs
  13546. faithfully implements most of the features of Richard Stallman's Emacs editor.
  13547. Daniel Lawrence claims copyright privileges for this version which has also
  13548. been updated and enhanced many times by our staff and members. The secret of
  13549. MicroEmacs' popularity seems to be its portablity (it runs on more than ten
  13550. different operating systems), rich set of features, and its configurability --
  13551. a built-in macro language lets MicroEmacs be tailored to virtually any task.
  13552. The next two most popular disks are UNIX tools used in compiler development.
  13553. CUG#172, #173 and #290 are LEX, a lexical analyzer that extracts tokens from
  13554. an input character stream. CUG#285 is a YACC compatible parser and code
  13555. generator.
  13556. As you'll notice from the Top 20 list, our library contains a wide variety of
  13557. application programs and development tools, including cross-assemblers,
  13558. windows, graphics, an AI application, communications, and a math package,
  13559. among others.
  13560. One of the more recent trends in the library is the emergence of shareware.
  13561. Even though you must pay some minimal fee for the source code of a shareware
  13562. program, the quality of some volumes is very competitive with more expensive
  13563. commercial products. Another trend is the submission of more serious and
  13564. specialized applications. For example, the 3-D medical imaging software on
  13565. CUG#293-294.
  13566.  
  13567.  
  13568. Wish List
  13569.  
  13570.  
  13571. Even with all this diversity, there are many frequently requested packages.
  13572.  
  13573.  
  13574. A Simple Text Editor
  13575.  
  13576.  
  13577. Many people have asked for a simple text editor that can be embedded in their
  13578. application. The editor needn't be fancy and powerful like MicroEmacs, but
  13579. should offer these features:
  13580. Be callable (as a function) from the application program
  13581. Function in both full-screen and windowed applications
  13582. Can retrieve and save a file
  13583. Can browse a file (page up/down)
  13584. Be modeless
  13585. Support block manupilations (block copy, move, or delete)
  13586. Can be compiled with small model under MS-DOS
  13587. Can read up to 30K ASCII text
  13588. Search or replacement is optional
  13589. Go to the specified line number is optional
  13590.  
  13591.  
  13592. An ANSI C Compiler
  13593.  
  13594.  
  13595. This is a real challenge. We hope to address this need by distributing the GNU
  13596. C compiler (and C+ + compiler) from The Free Software Foundation.
  13597.  
  13598.  
  13599. .PDX Or .DBF File Function Libraries
  13600.  
  13601.  
  13602. A .PDX file is an image file produced by ZSoft's PC Paintbrush. It is a common
  13603. graphics file format for the PC and is also used by most scanners, fax
  13604. programs, and desktop publishing programs. A .DBF file is a data file used by
  13605. Ashton-Tate's dBase programs. We need function libraries that manipulate these
  13606. standard format files.
  13607.  
  13608.  
  13609. Spread Sheet
  13610.  
  13611.  
  13612. As with the editor, we need a simple spread sheet that can be embedded in
  13613. larger applications.
  13614.  
  13615.  
  13616. Pascal To C Translator
  13617.  
  13618.  
  13619. This would be useful for Pascal programmers trying to port their programs.
  13620. Michael Yokoyama (HI) has forwarded such a program to us, but we have been
  13621. unable to contact the author, Per Bergsten of Sweden, to get permission to
  13622. release the program. Please let us know if you can contact Per Bergsten or
  13623. know of an independent version of this code.
  13624.  
  13625.  
  13626.  
  13627. C To Pascal
  13628.  
  13629.  
  13630. Useful for Pascal programmers who want to port an application program written
  13631. in C.
  13632.  
  13633.  
  13634. Cross C Compiler
  13635.  
  13636.  
  13637. Thanks to Will Colley, we have a variety of cross assemblers. However, our
  13638. only cross C compiler is CUG204, 68000 C compiler by Matthew Brandt, which
  13639. runs under MS-DOS and generates 68000 object code. We need more variety in
  13640. this area (like a cross C compiler that runs under MAC and generates 8086
  13641. code).
  13642.  
  13643.  
  13644. Download Fonts In A Laser Printer
  13645.  
  13646.  
  13647. All sorts of applications could make better use of laser printer capabilities
  13648. if they could download special fonts. We'd like a library of functions that
  13649. can read Bitstream, Ventura Bitmap and other popular font files and download
  13650. them to an HP compatible.
  13651.  
  13652.  
  13653. Sideways Text
  13654.  
  13655.  
  13656. Not a configuration utility that uses a printer's landscape mode, but a
  13657. utility that exploits a printer's graphic mode to print 90° rotated text. Why
  13658. not?
  13659.  
  13660.  
  13661. Database Management
  13662.  
  13663.  
  13664. We would like a simple and useful relational database manager -- in C.
  13665. If you've seen C source code such as those listed here or can implement them,
  13666. please let us know. In addition, we are interested in obtaining C++ and C
  13667. source code for Macintosh. Moreover, I believe you have your own wish list.
  13668. Please let me know about it for a future column.
  13669. P.S. Henri de Feraudy of France, the author of Small Prolog in CUG#297, is
  13670. sending us a PC version of Little Small Talk. It will be a new release in a
  13671. future issue.
  13672. Figure 1
  13673. Figure 2
  13674. Figure 3
  13675. Table 1
  13676. Year 1987
  13677. 1. 173 LEX Part 1 (lexical analyzer)
  13678. 2. 172 LEX Part 2
  13679. 3. 198 MicroEmacs v3.9 Source (text
  13680. editor)
  13681. 4. 197 MicroEmacs v3.9 Executable &
  13682. Documetation
  13683. 5. 175 (Replaced with CUG285)
  13684. 6. 174 (Replaced with CUG 285)
  13685. 7. 201 MS-DOS System Support (ANSI
  13686. driver, TRS, ..etc.)
  13687. 8. 204 68000 C Compiler (cross compier
  13688. for MSDOS)
  13689. 9. 236 Highly Portable Utilities (Unix-like
  13690. tools)
  13691. 10. 200 Small C Interpreter
  13692. 11. 220 Window BOSS (window library)
  13693. 12. 227 Portable Graphics
  13694. 13. 164 Windows
  13695. 14. 218 Dictionary Part I
  13696. 15. 217 Spell & Dictionary Part II (spell
  13697. checker)
  13698. 16. 155 B-TREES, FFT, etc. (balanced
  13699. binary tree, fast fourier transform)
  13700. 17. 228 Miscellaney IX (window, ISAM
  13701.  
  13702. routines, .. etc.
  13703. 18. 165 Programs from Reliable Data
  13704. Structures (from Plum Hall)
  13705. 19. 216 Zmodem & Saveram
  13706. (communication)
  13707. 20. 226 ART-CEE (rule-based inference
  13708. engine)
  13709.  
  13710. Year 1988
  13711. 1. 197 MicroEmacs v3.9 Exec. & Doc.
  13712. (Text Editor)
  13713. 2. 198 MicroEmacs v3.9 Source
  13714. 3. 259 Console I/O & Withers Tools
  13715. (window functions)
  13716. 4. 255 EGA Graphics Library
  13717. 5. 172 LEX Part 1 (Lexical analyzer)
  13718. 6. 173 LEX Part 2
  13719. 7. 260 Zmodem, CU, tty Library
  13720. (communication)
  13721. 8. 236 Highly Portable Utilities UNIX-like
  13722. tools)
  13723. 9. 151 Ed Ream's Screen Editor for IBM
  13724. PC
  13725. 10. 263 C_wndw Toolkit (windows)
  13726. 11. 248 Micro Spell (spell checker)
  13727. 12. 241 Inference Engine & Rule Based
  13728. Compiler
  13729. 13. 242 Still More Cross Assemblers
  13730. 14. 155 B-TREES, FFT, etc. (balanced
  13731. binary tree, fast fourier transform)
  13732. 15. 227 Portable Graphics
  13733. 16. 247 Miracl (multi-precision integer and
  13734. rational arithmetic C library)
  13735. 17. 246 Cycles, Mandelbrot
  13736. 18. 232 Little Smalltalk - Unbundled Part 2
  13737. 19. 231 Little Smalltalk - Unbundled Part 1
  13738. 20. 265 cpio Installation Kit (archive
  13739. utility)
  13740.  
  13741. Year 1989 (Until October)
  13742. 1. 197 MicroEmacs v3.9 Exec. & Doc.
  13743. 2. 198 MicroEmacs v3.9 Source
  13744. 3. 285 Bison for MS-DOS (YACC like
  13745. parser)
  13746. 4. 290 FLEX (fast lexical analyzer)
  13747. 5. 263 C_wndw Toolkit
  13748. 6. 283 FAFNIR (general-purpose,
  13749. table-driven forms engine)
  13750. 7. 277 HP Plotter Library (graphics)
  13751. 8. 173 LEX Part 2
  13752. 9. 172 LEX Part 1
  13753. 10. 284 Portable 8080 Emulator
  13754. 11. 260 Zmodem, CU, tty Library
  13755. 12. 236 Highly Portable Utilities
  13756. 13. 276 Z80 and 6804 Cross Assembler
  13757. 14. 155 B-TREES, FFT, etc.
  13758. 15. 241 Inference Engine & Rule Based
  13759. Compiler
  13760. 16. 242 Still More Cross Assemblers
  13761.  
  13762. 17. 273 Turbo C Utilities
  13763. 18. 261 68K Cross Assembler for MSDOS
  13764. 19. 220 Window BOSS (window library)
  13765. 20. 292 ASxxxx C Cross Assemblers
  13766.  
  13767.  
  13768.  
  13769.  
  13770.  
  13771.  
  13772.  
  13773.  
  13774.  
  13775.  
  13776.  
  13777.  
  13778.  
  13779.  
  13780.  
  13781.  
  13782.  
  13783.  
  13784.  
  13785.  
  13786.  
  13787.  
  13788.  
  13789.  
  13790.  
  13791.  
  13792.  
  13793.  
  13794.  
  13795.  
  13796.  
  13797.  
  13798.  
  13799.  
  13800.  
  13801.  
  13802.  
  13803.  
  13804.  
  13805.  
  13806.  
  13807.  
  13808.  
  13809.  
  13810.  
  13811.  
  13812.  
  13813.  
  13814.  
  13815.  
  13816.  
  13817.  
  13818.  
  13819.  
  13820.  
  13821.  
  13822.  
  13823.  
  13824.  
  13825. C Programmer's Toolbox/PC
  13826.  
  13827.  
  13828. Kenji Hino
  13829.  
  13830.  
  13831. Kenji Hino is a member of The C Users' Group technical staff. He holds a
  13832. B.S.C.S. from McPherson College and an undergraduate degree in metalurgy from
  13833. a Japanese university. He is currently working toward an M.S.C.S at the
  13834. University of Kansas.
  13835.  
  13836.  
  13837. Unlike UNIX, MS-DOS has no standard utility programs to support C programmers
  13838. in program development or maintenance. In the past, C programmers have
  13839. developed their own tools from scratch or ported tools from other operating
  13840. systems to MS-DOS. UNIX tools have been ported most, simply because they are
  13841. the "right" tools to improve programmer productivity. This report looks at a
  13842. collection of UNIX-like tools, C Programmer's Toolbox/PC revision 2.0 by MMC
  13843. AD Systems.
  13844.  
  13845.  
  13846. Component
  13847.  
  13848.  
  13849. The Toolbox/PC consists of Volumes I and II, which are available separately or
  13850. together. I recommend getting both. Each volume includes two IBM 360K disks
  13851. and costs $99.95; both volumes together go for $175. The manual (in a binder)
  13852. describes both volumes, regardless of whether you purchase Volume I, II, or
  13853. both. The C Programmer's Toolbox is available from MMC AD Systems, Box 360845
  13854. Milpitas, CA 95035, phone (408) 263-0781. Although the Toolbox/PC runs on
  13855. PC/MS-DOS, MMC AD Systems also distributes versions of the Toolbox for the
  13856. Macintosh MPW and the Sun UNIX system.
  13857. The installation of the Toolbox on either a floppy disk or hard disk system is
  13858. straightforward; just copy all files from the distribution to your disk. If
  13859. you install the Toolbox on hard disk systems, be sure that the path is set
  13860. correctly.
  13861.  
  13862.  
  13863. The Tools
  13864.  
  13865.  
  13866. The Toolbox includes 21 tools (see Table 1). All the tools are command-line
  13867. driven. The corresponding UNIX tools are also listed in the same table. The
  13868. tools help analyze the structure, format and execution of programs, manipulate
  13869. and/or modify program input/output data, or verify program input/output data
  13870. (see Figure 1).
  13871. Covering all 21 tools in a report this size is impractical and undesirable.
  13872. Thus, I will focus on the analytical tools, CFlow, PMon and CritPath. These
  13873. tools are mainly used to understand a program's structure and to analyze the
  13874. performance of your application program for the enhancement.
  13875.  
  13876.  
  13877. CFlow
  13878.  
  13879.  
  13880. Whether developing or maintaining a program, as the program becomes larger,
  13881. you tend to lose sight of the overall program structure. Discerning the
  13882. inter-relationships between modules becomes harder as the program grows. Even
  13883. worse, you may have to study code written by somebody else.
  13884. CFlow is a tool for studying code. It scans one or more C source files to
  13885. generate reports that describe the hierarchy of both defined and invoked
  13886. functions (external or library functions).
  13887. Figure 2 shows a program flow tree, one of the reports produced by CFlow. (The
  13888. analyzed source code is shown in Listing 1 and is adapted from a program in
  13889. the CUG PD Library. The original author is Richard Threlkeld.) The line
  13890. indentation indicates the level of function invocations. If the same function
  13891. is referenced more than once, the line number of the last reference is
  13892. attached to the beginning of the line. An asterisk (*) indicates whether the
  13893. function is an external or run-time library function. Within the parentheses
  13894. following a function name is the source filename and a starting line number of
  13895. the function definition.
  13896. In order to obtain the desired result, you must specify the dash/slash options
  13897. appropriately. For example, function names at each level of a CFlow tree are
  13898. displayed in alphabetical order by default. If you want function names
  13899. displayed as they are encountered, use the -e option. In addition, when using
  13900. multiple input files, the -f option is useful to display the location of each
  13901. function.
  13902. In this version 2.0, many improvements were made over the previous version.
  13903. CFlow now reports a function pointer (such as (*a) ()) or function address
  13904. (such as f(); a = f;). It also has a virtual memory system that handles
  13905. programs of unlimited size (true for some of the other tools, too).
  13906. The biggest improvement is that CFlow now automatically preprocesses your
  13907. source code. That is, it recognizes #if directives to read and process the
  13908. appropriate portions of your code. This, however, creates one problem. If a
  13909. function is a macro, it is expanded and replaced with some system-level
  13910. function, surprising you with some unfamiliar function name in the report,
  13911. such as _filbuf() instead of getc(). This can be solved by turning off the
  13912. preprocessor with the p switch, thereby sacrificing all the preprocessor
  13913. benefits.
  13914. Along with the CFlow Tree, CFlow generates a Master Define Function List (a
  13915. list of caller and callee), an Undefined Function List (a list of external or
  13916. library functions) and Function Called by List (a list of callee and caller)
  13917. when you specify the proper dash/slash options.
  13918. Using CFlow, the programmer can easily and quickly understand how a program is
  13919. structured and which module is invoked by which module. To understand
  13920. visually, you can draw the structure diagram as in Figure 3, based on the
  13921. Program Flow Tree. In Figure 3 for example, if a portion of the code in
  13922. crc_update() is modified, you know from the reports which other functions will
  13923. be affected (in this case, crc() and crc_finish()).
  13924.  
  13925.  
  13926. PMon And CritPath
  13927.  
  13928.  
  13929. The execution profiler PMon is a tool which analyzes a program. It determines
  13930. how much execution time is spent on each symbol (functions or BIOS/DOS calls)
  13931. or program area.
  13932. During program execution, PMon resides in memory with the target program,
  13933. intercepts the program at regular intervals and examines the CS: IP register
  13934. of the target program to determine which section of code is currently being
  13935. executed. PMon tracks this information for each intercept and, using the
  13936. information from the .MAP file (symbol entries), generates a set of reports.
  13937. I tested PMon using the CRCK (Cyclic Redundancy ChecK) program CRC15.EXE. The
  13938. program listing of CRC15 is in Listing 1; it must be compiled and linked to
  13939. generate a .MAP file. The .MAP file is then processed by MapVar and placed
  13940. into PMon with the target executable program. Figure 4 shows two reports
  13941. resulting from monitoring CRC15.
  13942. The first report is the program execution summary, which gives the complete
  13943. synopsis of the program's execution. Descriptions for certain summary headings
  13944. are:
  13945. Total execution clicks. The total number of clock ticks recorded in the
  13946. program initiation, execution, and termination.
  13947. Total monitored hits. The actual number of clock ticks recorded during program
  13948. execution.
  13949. Total symbol entries. The total number of symbols (function names) used in the
  13950. program.
  13951. Number of symbols hit. The number of symbols detected in the execution.
  13952. Total symbols hits. The total number of times PMon found the program executing
  13953. as opposed to BIOS, DOS, or other resident programs.
  13954. Time in program. The total time spent in the program vs. BIOS/DOS functions
  13955. and other activities (Time below/above).
  13956. Time in BIOS/DOS. The total time spent in BIOS/DOS functions.
  13957. According to the program execution summary, CRC15 processed 1 file within 6
  13958. seconds. Although CRC15 contains 115 symbol entries, PMon found only four
  13959. symbols during program execution, even though it checked CRC15 a total of 92
  13960. times. CRC15 made 113 DOS system calls using 12 different DOS calls. Of the 92
  13961. times checked, PMon found the program executing for 4.76 seconds (79.3%) and
  13962. BIOS/DOS for 1.24 seconds (20.7 %).
  13963. The second report, the Symbol execution Summary, shows where a monitored
  13964. program is executing within itself, excluding DOS calls.
  13965. Abs Adr -- the starting address (segment:offset) of asymbol.
  13966. Hits -- the total number of times PMon found the execution of a symbol.
  13967.  
  13968. Loc% -- the percentage of activity of a symbol when compared with the total
  13969. execution excluding DOS calls.
  13970. Tot% -- the percentage of activity of a symbol when compared with the total
  13971. execution including DOS calls.
  13972. Entry Name -- Symbol name.
  13973. In this example, PMon detected that function crc_update(), whose starting
  13974. address is 0:011e, executed 50 times and took 63.5% of total execution time
  13975. excluding DOS calls and 54.3% of total execution including DOS calls.
  13976. In addition, PMon generates a BIOS Interrupt Summary, a DOS Function Call
  13977. Execution Summary Report and DOS Function Call Execution Detail Report showing
  13978. the statistics of BIOS/DOS operations performed in the program execution such
  13979. as Character input/output, File input/output, etc.
  13980. Although these reports provide a good amount of information about software
  13981. performance, further analysis can be done with CritPath command.
  13982. CritPath determines the critical path of a program by analyzing the reports
  13983. generated by CFlow and PMon commands. A program's critical path is the
  13984. sequence of functions called from main() that consumes more execution time
  13985. than any other sequence.
  13986. Figure 5 shows a Critical Path Report generated by CritPath. The report
  13987. provides the primary information necessary to improve a program's performance.
  13988. The report shows a list of the 20 functions that used the most execution time
  13989. (Top 20 Functions in Actual Time), a list of the 20 functions that by
  13990. themselves and through other functions that they called used the most
  13991. execution time (Top 20 Functions in Cumulative Time). Finally, the reports
  13992. provide a list of the functions that comprise the critical path of the
  13993. program. In this example, the critical path is the sequence of functions crc()
  13994. and crc_update(). CritPath also generates both a Function Summary Report that
  13995. evaluates the performance of all functions and system calls in the program and
  13996. a Weighted Hierarchical Program Flow Tree.
  13997. Using the statistics produced by PMon and CritPath, programmers can spot
  13998. places where performance could be improved. However, these tools only identify
  13999. weak spots in the program and don't come up with the method to improve the
  14000. performance. Such information might be obtained from books such as
  14001. Supercharging C With Assembley Language by Harry Chesley, Mitchell Waite, The
  14002. Waite Group.
  14003.  
  14004.  
  14005. Conclusion
  14006.  
  14007.  
  14008. Overall, compared to UNIX tools, the Toolbox tools have more options and
  14009. provide more detailed information, helping the programmer to take more control
  14010. over program output. On the other hand, he or she must read the manual very
  14011. carefully and specify the appropriate options that will generate the desired
  14012. result. Furthermore, the input source code for some tools should be not only
  14013. syntactically correct but done in good programming style, even if the program
  14014. compiled fine. Otherwise, the output information might come out confusing. For
  14015. example, the inappropriate choice of options and poor programming style (such
  14016. as Listing 1) cause CFlow to report an identifier, crc as a function address,
  14017. not as a variable (crc is used for a function name and variable name. This can
  14018. be detected by CXref.). CFlow also doesn't distinguish between function
  14019. invocation and function declaration inside a function.
  14020. For beginners, the Toolbox can be a good starting point for using tools to
  14021. improve productivity since the commands are very uniform and the manual is
  14022. well written. In the manual, each tool is uniformly explained using sample
  14023. results. In particular, observations and suggestions about the reports
  14024. generated are honest and good advice for users.
  14025. For advanced programmers, the combination of CFlow, PMon and CritPath can give
  14026. them clues for fine tuning or improving software performance either after the
  14027. program has been developed or when it is about to be updated. CFlow, CPrint,
  14028. CXref and CLint can be used to study existing programs and will greatly reduce
  14029. maintenance cost.
  14030. Figure 1
  14031. Figure 2
  14032.  *** Program Flow Tree ***
  14033.  -------------------------
  14034.  
  14035.  1: main(CRC15.c:4)
  14036.  2: crc(CRC15.C:29)
  14037.  3: crc_clear(CRC15.C:58)
  14038.  4: crc_finish(CRC15.C:80)
  14039.  5: crc_update(CRC15.C:63)
  14040.  6: 5 crc_update()
  14041.  7: exit(*)
  14042.  8: fclose(*)
  14043.  9: fopen(*)
  14044. 10: fprintf(*)
  14045. 11: printf(*)
  14046. 12: _filbuf(*)
  14047. 13: 7 exit(*)
  14048. 14: 11 printf(*)
  14049. Figure 3
  14050. Figure 4
  14051.  *** Program Execution Summary ***
  14052.  
  14053. Program executed: crc15.exe
  14054. Delay/Run period (clicks): 0/0
  14055. Start date/time: October 19, 1989 19:45:12
  14056. Stop date/time: October 19, 1989 19:45:18
  14057. Elapsed execution time: 0: 0: 0: 6 6 seconds
  14058.  
  14059. Total execution clicks: 95
  14060. Approximate clicks/second: 15.8 Approx sample period (ms): 63.2
  14061. Total monitored hits: 92
  14062.  
  14063. Total symbol entries: 115
  14064. Number of symbols hit: 4 % of total symbols hit: 3.5
  14065. Total symbol hits: 73 Avg hits/hit symbol: 18.3
  14066.  
  14067. Number of monitored interrupts: 2
  14068. Number of interrupts used: 2 % of total monitored: 100.0
  14069. Total BIOS interrupt calls: 141 Avg # interrupts/hit: 7.4
  14070. Total BIOS interrupt hits: 19 Avg # hits/interrupt: 0.1
  14071.  
  14072.  
  14073. Number of DOS calls used: 12
  14074. Total DOS program calls: 113
  14075.  
  14076. Time in program (secs): 4.76 % of total: 79.3
  14077. Time in BIOS/DOS (secs): 1.24 % of total: 20.7
  14078. Time below program (secs): 0.00 % of total: 0.0
  14079. Time above program (secs): 0.00 % of total: 0.0
  14080.  
  14081. Total KNOWN time used (secs): 6.00 % of total: 100.0
  14082. Total UNKNOWN time used (secs): 0.00 % of total: 0.0
  14083.  
  14084.  *** Symbol Execution Summary ***
  14085.  
  14086.  Abs Addr Hits Loc % Tot % Entry Name
  14087. --------- -------- ----- ----- ----------
  14088.  
  14089.  7a 12 16.4 13.0 _crc
  14090.  11e 50 68.5 54.3 _crc_update
  14091.  3e4 1 1.4 1.1 __chkstk
  14092.  1edc 10 13.7 10.9 __aNlshr
  14093.  
  14094. --- HINT --- HINT --- HINT --- HINT --- HINT --- HINT --- HINT --- HINT ---
  14095.  
  14096.  Concentrate on the following functions to improve your program's
  14097.  performance:
  14098.  
  14099.  _crc ( 13.0)
  14100.  _crc_update ( 54.3)
  14101.  __aNlshr ( 10.9)
  14102. Figure 5
  14103.  *** Critical Path Report ***
  14104.  ----------------------------
  14105.  
  14106.  Top 20 Functions in Actual Time
  14107.  -------------------------------
  14108.  
  14109. Rank Seconds % Total Function Name
  14110. ---- ------- ------- -------------
  14111.  
  14112.  1. 3.3 54.3% crc_update()
  14113.  2. 1.0 17.4% __SysCall_3fH()
  14114.  3. 0.8 13.0% crc()
  14115.  4. 0.7 10.9% _aNlshr()
  14116.  5. 0.1 1.1% _chkstk()
  14117.  6. 0.1 1.1% __SysCall_3dH()
  14118.  7. 0.1 1.1% __SysCall_40H()
  14119.  8. 0.1 1.1% __SysCall_43H()
  14120.  9. 0.0 0.0% crc_clear()
  14121.  10. 0.0 0.0% crc_finish()
  14122.  11. 0.0 0.0% exit()
  14123.  12. 0.0 0.0% fclose()
  14124.  13. 0.0 0.0% fopen()
  14125.  14. 0.0 0.0% fprintf()
  14126.  15. 0.0 0.0% main()
  14127.  16. 0.0 0.0% printf()
  14128.  17. 0.0 0.0% _filbuf()
  14129.  
  14130.  Top 20 Functions in Cumulative Time
  14131.  -----------------------------------
  14132.  
  14133.  
  14134. Rank Seconds % Total Function Name
  14135. ---- ------- ------- -------------
  14136.  
  14137.  1. 6.0 100.0% crc()
  14138.  2. 6.0 100.0% main()
  14139.  3. 2.7 44.6% crc_finish()
  14140.  4. 2.7 44.6% crc_update()
  14141.  5. 0.8 14.1% __SysCall_3fH()
  14142.  6. 0.5 8.7% _aNlshr()
  14143.  7. 0.0 0.0% crc_clear()
  14144.  8. 0.0 0.0% exit()
  14145.  9. 0.0 0.0% fclose()
  14146.  10. 0.0 0.0% fopen()
  14147.  11. 0.0 0.0% fprintf()
  14148.  12. 0.0 0.0% printf()
  14149.  13. 0.0 0.0% _chkstk()
  14150.  14. 0.0 0.0% _filbuf()
  14151.  15. 0.0 0.0% __SysCall_3dH()
  14152.  16. 0.0 0.0% __SysCall_40H()
  14153.  17. 0.0 0.0% __SysCall_43H()
  14154.  
  14155.  The Critical Path
  14156.  -----------------
  14157.  
  14158.  Act Rank Cum Rank
  14159.  (%) (%)
  14160. ---- ---- ---- ----
  14161.  0.0 15 100.0 2 main()
  14162. 13.0 3 100.0 1 crc()
  14163.  0.0 10 44.6 3 crc_finish()
  14164. 54.3 1 44.6 4 crc_update()
  14165.  
  14166.  Critical path hits = 62 Total hits = 92
  14167.  Critical path time = 4.0 secs Total time = 6.0 secs
  14168.  % of total = 67.4
  14169. Table 1
  14170. Toolbox Volumes I & II UNIX tools Description
  14171. ==================================================================
  14172. Cat cat, cp Concatenate Data
  14173. CharCnt wc Count Characters,Lines...
  14174. CFlow cflow Trace C Program Flow
  14175. CLint lint C Semantic Checker
  14176. CPrint cb, indent C Source Code Beautifier/Reformatter
  14177. CritPath Critical Path Analyzer
  14178. CXref xref C Cross Reference
  14179. Detab expand Remove Tabs
  14180. Entab unexpand Restore Tabs
  14181. ExecTime time Time Program Execution
  14182. FileComp comp Compare Files
  14183. FileDiff diff Difference Files
  14184. FileDump od Dump File
  14185. FileList List and Find Files
  14186. Fill Expand Text Template
  14187. MapVar Extract Load Map Variables
  14188. PMon prof, gprof Program Performance Monitor
  14189. STrip Extract Text
  14190. Tail tail Copy End of File
  14191. TabTran sed Translate Tabs
  14192.  
  14193. TransLit tr Transliterate Characters
  14194.  
  14195. Listing 1
  14196. #include <stdio.h>
  14197.  
  14198. main(argc,argv)
  14199. int argc;
  14200. char **argv;
  14201. {
  14202. int i;
  14203. void crc();
  14204.  
  14205. if (argc <= 1)
  14206. {
  14207. printf("USAGE:crc15 filename [filename...]\n");
  14208. exit(1);
  14209. }
  14210.  
  14211. for(i=1; i < argc; i++)
  14212. {
  14213. printf ("\n%-s ",argv[i]);
  14214. crc(argv[i]);
  14215. }
  14216. exit(0);
  14217. } /* main */
  14218.  
  14219. /* CRC
  14220. * Cycric Redundancy Check
  14221. *
  14222. */
  14223. void crc(argv)
  14224. char *argv;
  14225. {
  14226. FILE *fd;
  14227. int crc;
  14228. int c;
  14229. char crc_char;
  14230. int crc_clear(),crc_update(),crc_finish();
  14231.  
  14232. fd = fopen(argv,"rb");
  14233. if(!fd)
  14234. {
  14235. fprintf(stderr,"Can't open %s !\n",argv);
  14236. exit(1);
  14237. }
  14238. crc = crc_clear();
  14239.  
  14240. while((c = getc(fd)) != EOF)
  14241. {
  14242. crc_char = c;
  14243. crc = crc_update(crc,crc_char);
  14244. }
  14245.  
  14246. crc = crc_finish(crc);
  14247. printf("%04x",crc);
  14248. fclose(fd);
  14249. } /* crc */
  14250.  
  14251. int crc_clear()
  14252.  
  14253. {
  14254. return(0);
  14255. }
  14256.  
  14257. int crc_update(crc,crc_char)
  14258. int crc;
  14259. char crc_char;
  14260. {
  14261. long x;
  14262. int i;
  14263.  
  14264. x = ((long)crc << 8) + crc_char;
  14265. for(i = 0;i < 8;i++)
  14266. {
  14267. x = x << 1;
  14268. if(x & 0x01000000)
  14269. x = x ^ 0x01A09700;
  14270. }
  14271. return(((x & 0x00ffff00) >> 8));
  14272. }
  14273.  
  14274. int crc_finish(crc)
  14275. int crc;
  14276. {
  14277. return(crc_update(crc_update(crc,'\0'),'\0'));
  14278. }
  14279.  
  14280.  
  14281.  
  14282.  
  14283.  
  14284.  
  14285.  
  14286.  
  14287.  
  14288.  
  14289.  
  14290.  
  14291.  
  14292.  
  14293.  
  14294.  
  14295.  
  14296.  
  14297.  
  14298.  
  14299.  
  14300.  
  14301.  
  14302.  
  14303.  
  14304.  
  14305.  
  14306.  
  14307.  
  14308.  
  14309.  
  14310.  
  14311.  
  14312.  
  14313.  
  14314.  
  14315.  
  14316. Publisher's Forum
  14317. I've been reading documentation. It's no fun.
  14318. Here's some advice from an experienced "how to" writer, who's also an
  14319. experienced programmer, about how documentation should be structured to be
  14320. useful.
  14321. Include an extended procedural tutorial. This section is for the user who
  14322. doesn't have enough prior experience with similar products to guess what to do
  14323. next. Don't mix tips about advanced tricks into this section, or cautions
  14324. about product limitations and quirks. If you do, the user won't be able to
  14325. find those important tidbits later without re-reading the entire section. In
  14326. every "how-to" piece, focus is everything: give the procedural outline and
  14327. just the procedural outline.
  14328. Include a goal-oriented "Tips & Techniques" section. I don't care what fruity
  14329. name you give your product, there will be certain non-obvious tricks that make
  14330. it more productive. Organize these by goals -- e.g. Printing Fields From A
  14331. Join, Timestamping A File, Converting File Formats. This section should be
  14332. rife with cross-references and redundancy. Each goal's discussion should at
  14333. least cross-reference related material that appears elsewhere, and include all
  14334. the other "extraneous" information you were tempted to toss in as asides in
  14335. the procedural section. Short, well-targeted examples belong here. Even if
  14336. your product is "truly unique", the goals should be stated in terms of
  14337. commonly recognized paradigms so that my experience with similar projects can
  14338. speed my adaptation to your product.
  14339. Include a thorough technical specification. No, technical specifications don't
  14340. help the beginner, but they are invaluable to an experienced user.
  14341. Cross-reference the specs. Include hardware requirements, interface
  14342. specifications, data structure templates, file specifications, and
  14343. command-line syntax for subordinate modules (even for those modules that are
  14344. normally invoked by some "integrated environment driver" -- don't presume to
  14345. know better than the programmer what he needs to know to get the job done).
  14346. Explain the design goals and philosophy. Virtually every product started in a
  14347. specific environment with a specific, limited application in mind. Yes,
  14348. marketing will want to promote the product as everything for everyone, but
  14349. make room somewhere in the document for the truth. Sharing the design
  14350. philosophy helps the programmer understand where the product fits and reduces
  14351. the early frustration level. If I'm trying to use your tool in a development
  14352. project, and I know the design goals that produced the tool, I stand a better
  14353. chance of designing a project that can be built with the tool.
  14354. Invest in a superb index. So what if the answer to my question is in the
  14355. manual. How many times can I afford to read a 900-page primer to find the two
  14356. lines that are critical? The answer is a very small integer; I'm going to be
  14357. calling customer support. Get your ego and marketing's time-table out of the
  14358. way and hire a professional to prepare a SUPERB index. Every dollar spent on
  14359. an index will be returned ten-fold in reduced customer support costs.
  14360. Explain the installation process for standard environments, and then explain
  14361. what configuration options are available and how they interact. Give me this
  14362. information even if you do bundle a whizbang installation utility. I've
  14363. probably been at this long enough to have my own ideas about where to put my
  14364. working files.
  14365. In short, keep your reader in mind. Design your documentation to meet the
  14366. user's needs over his entire life as a user: a detailed step-by-step to orient
  14367. the beginner; well-packaged goal-organized information to support the
  14368. exploration and growth of the intermediate user; and comprehensive, frank, and
  14369. well-indexed reference material for the experienced and technically advanced
  14370. user.
  14371. I mean it.
  14372. Robert Ward
  14373. Editor/Publisher
  14374.  
  14375.  
  14376.  
  14377.  
  14378.  
  14379.  
  14380.  
  14381.  
  14382.  
  14383.  
  14384.  
  14385.  
  14386.  
  14387.  
  14388.  
  14389.  
  14390.  
  14391.  
  14392.  
  14393.  
  14394.  
  14395.  
  14396.  
  14397.  
  14398.  
  14399.  
  14400.  
  14401.  
  14402.  
  14403.  
  14404.  
  14405.  
  14406.  
  14407.  
  14408.  
  14409.  
  14410.  
  14411.  
  14412.  
  14413.  
  14414.  
  14415.  
  14416.  
  14417.  
  14418.  
  14419.  
  14420.  
  14421.  
  14422. New Products
  14423.  
  14424.  
  14425. Industry-Related News And Announcements
  14426.  
  14427.  
  14428.  
  14429.  
  14430. UNIX Alternative Announced For The Apple Macintosh
  14431.  
  14432.  
  14433. Technical Systems Consultants, Inc. has released a UNIX compatible, real-time
  14434. operating system for the Apple Macintosh family. The system, UniFLEX, supports
  14435. multi-tasking and multi-users and comes complete with all development tools, a
  14436. C Compiler, TCP/IP Networking support and X Window System v11.3 software. A
  14437. version has also been released for Force Computer's CPU-37-singleboard VMEbus
  14438. computer with integrated Ethernet hardware.
  14439. For the Apple Macintosh family, price for a single system development license
  14440. is $595. The price includes 90 days phone support. For the Force CPU-37, the
  14441. single system licensing price is $1000 for UniFLEX/RT or $1800 for UniFLEX/RN
  14442. with networking.
  14443. Contact Technical Systems Consultants, Inc., 111 Providence Road, Chapel Hill,
  14444. NC 27514 (919) 493-1451; FAX (919) 490-2903.
  14445.  
  14446.  
  14447. Stepstone Updates Objective C
  14448.  
  14449.  
  14450. The Stepstone Corporation has released its Objective-C Compiler v4.0 running
  14451. under MS-DOS and Microsoft's OS/2 The Compiler is a C-based hybrid
  14452. object-oriented language and is ANSI C compatible.
  14453. Objective C v4.0 requires a PC/AT or PS/2 class machine running MS-DOS and
  14454. Microsoft C v5.0. The compiler, packaged with a basic data structures library
  14455. (ICpak101) and built-in extended memory support is $249.
  14456. Stepstone has also released its object-oriented user interface toolkit,
  14457. ICpak101, for workstations running the X-Windows System v11.
  14458. Product information is available from the Stepstone Corporation at (203)
  14459. 426-1875, (800) 289-6253 or by mail to The Stepstone Corporation, 75 Glen
  14460. Road, Sand Hook, CT 06482.
  14461.  
  14462.  
  14463. Lattice's New 6.0 Release Features ANSI Compliance
  14464.  
  14465.  
  14466. Lattice, Inc. is shipping v6.0 of its C compiler for MS-DOS & OS/2. The
  14467. release features major enhancements to the compiler, a global optimizer, new
  14468. programming utilities, and a number of new library functions. Both the
  14469. compiler and libraries are now ANSI compatible.
  14470. Version 6.0 contains a new global optimizer, automatic register variable
  14471. support, in-line function support, optimized libraries, and upgrades to the
  14472. compiler. The Lattice C Compiler v6.0 allows program modules compiled under
  14473. different memory models to be linked into a single program.
  14474. The Lattice v6.0 now includes LASM, a full-featured macro assembler with
  14475. support for 386 systems. LASM is compatible with MASM, and its output is
  14476. compatible with CodePRobe so assembly language programs can also be debugged
  14477. at source level. Utilities now bundled with the compiler are an overlay
  14478. linker, a MAKE facility, BIND Utility, and several UNIX-like tools including
  14479. EXTRACT and BUILD, DIFF, GREP, SPLAT, TOUCH, and WC. Programmer's tools in the
  14480. compiler package include the CodePRobe source level debugger, an integrated
  14481. editor, object module disassembler, object module librarian, and an automatic
  14482. installation program.
  14483. In addition to the OS/2 API and special graphics libraries in the previous
  14484. version, Lattice adds its Curses screen management library, communications
  14485. library, the dBC III library of database functions, and a protected mode OS/2
  14486. library.
  14487. The new list price of $250 includes unlimited free technical support through
  14488. Lattice's telephone hotline, bulletin board, MIX network, or written
  14489. correspondence. Lattice provides an unconditional, 30-day money-back guarantee
  14490. with each product.
  14491. For further information, contact: Lattice, Inc., 2500 South Highland Avenue,
  14492. Lombard, IL 60148 (312) 916-1600; FAX: (312) 916-1190.
  14493.  
  14494.  
  14495. Greenleaf CommLib, V3.0 Released
  14496.  
  14497.  
  14498. Greenleaf Software has released a new version of its communications library,
  14499. CommLib. Greenleaf CommLib v3.0 includes Kermit, XModem, XModem 1K, and YModem
  14500. batch file transfer protocols. It fully supports automatic RTS-CTS hardware
  14501. flow control, Hayes modem control functions, and XON/XOFF software flow
  14502. control.
  14503. CommLib automatically filters up to three codes from the receive stream,
  14504. stores status along with data in a "WideTrack Receive" mode, and
  14505. programmatically ignores or reacts to modem status at the interupt service
  14506. level.
  14507. The Greenleaf CommLib supports the PC, XT, AT, PS/2, and compatible machines
  14508. using COM1 and COM2 ports, and COM3..COM8 on a PS/2. It also supports up to 35
  14509. ports when using multiport boards. It can serve several families of multi-port
  14510. boards, including Digiboard, Stargate, Arnet, Contec, Quatec, and Quadram.
  14511. CommLib v3.0 is $299. For additional information and a free Demo disk, contact
  14512. Greenleaf Software, Inc.; 16479 Dallas Parkway, Suite 570; Dallas, TX 75218;
  14513. (800) 523-9830; FAX (214) 248-7830.
  14514.  
  14515.  
  14516. XVT Now Runs On MS-DOS, OS/2 And UNIX
  14517.  
  14518.  
  14519. New character-based versions of GSS' XVT Extensible Virtual Toolkit are
  14520. available for MS-DOS, OS/2 and UNIX programmers.
  14521. XVT allows programmers to support character displays with applications that
  14522. feature windowing, pull-down menus, dialog boxes, scroll-bars and other
  14523. graphical user-interface features. The same application source code can
  14524. support the Windows, PM and Mac GUIs.
  14525. Versions for Windows, PM, Macintosh and UNIX list for $595. XVT carries no
  14526. run-time redistribution royalties. The company is located at 9590 SW Gemini
  14527. Drive, Beaverton, Oregon 97005 (503) 641-2200; FAX: (503) 643-8642.
  14528.  
  14529.  
  14530. Helios Enhances Proteus System
  14531.  
  14532.  
  14533. Helios Software has released a new version of its prototype/demo system,
  14534. Proteus. Proteus v4.5 enables software developers to build functional
  14535. prototypes, marketing demos, tutorials and other interactive presentations.
  14536.  
  14537. Version 4.5 offers an integrated environment to build character-based demos
  14538. and bitmapped demos in any of 23 graphics formats. Both full-screen and
  14539. overlay images can be displayed, using 26 different video effects. Designers
  14540. can create screens with the built-in Screen Painter or configure Proteus to
  14541. execute any external paint program. Captured screens can also be incorporated
  14542. into demos.
  14543. Proteus is $199 for a three-disk set, with examples in source code. There is a
  14544. 30-day money-back guarantee, no royalties for distribution and no sign-on
  14545. screen. Required hardware configurations depend on the graphics mode used,
  14546. ranging from monochrome text to super-VGA. The Helios order number is (800)
  14547. 634-9986, or contact them at P.O. Box 22869, Seattle, WA 98112 (206) 324-7208.
  14548.  
  14549.  
  14550. High C V1.6 Includes 486 Support
  14551.  
  14552.  
  14553. MetaWare Inc. has released its ANSI-conformant High C compiler v1.6 for
  14554. 386/DOS on the 80386 and the 80486 in protected mode. Protected mode on the
  14555. 386 and 486 is supported in conjunction with MS-DOS extenders. Specific
  14556. support for the 486 is provided under toggle control. MetaWare has also
  14557. released High C v1.6 for OS/2 and real-mode MS-DOS.
  14558. Version 1.6 features expanded libraries, new documentation, two editors, a
  14559. disk cache utilily, a B-tree library, and a graphics library for the 80386/486
  14560. in protected mode. Users also get MetaWare's new make facility, and DOS Helper
  14561. which is a set of UNIX-style utilities for the MS-DOS operating system.
  14562. This upgrade comes with the GFX/386 Graphics library, produced in conjuction
  14563. with C Source. GFX for the 80386 is a user-transparent port of the C Source
  14564. GFX graphics package. The graphics package provides specific floating-point
  14565. graphics function; MetaWare is providing additional libraries that support the
  14566. 80387 and Weitek Abacus. High C also includes the EC editor from C Source,
  14567. HyperDisk disk cache from HyperWare, and source code for the MicroEMACS
  14568. editor.
  14569. In addition, v1.6 will be bundled with two products from Sterling Castle:
  14570. BlackStar/386 "C" Function Library and BPTPlus in C. These products provide
  14571. data retrival capabilities and over 300 additional library functions. Sterling
  14572. Castle's BlackStar/386 C libraries and the GFX/386 Graphics library are
  14573. available only through MetaWare.
  14574. Please refer inquiries to MetaWare Incorporated, 2161 Delaware Avenue, Santa
  14575. Cruz, CA 95060-5706 (408) 429-6382; FAX (408) 429-9273.
  14576.  
  14577.  
  14578. Prototyping Tools Combined
  14579.  
  14580.  
  14581. Genesis Data Systems has consolidated its line of prototyping and presentation
  14582. products, promerly sold as RADs and RPS, into a single system named
  14583. "ProtoFinish."
  14584. ProtoFinish is a versatile system for creating prototypes, demos, tutorials
  14585. and other presentations. It includes a screen design module for building
  14586. ASCII-based screens, a memory-resident utility for capturing text or CGA
  14587. graphics screens, a music module for adding sound, a flexible 4th-generation
  14588. language for accurately simulating the look and feel of a program, and a
  14589. royalty-free run-time utility for distribution. Libraries of assembly language
  14590. routines, primarily for incorporating screens in C, PASCAL, BASIC, and Clipper
  14591. code, are included for the programmer.
  14592. Contact Genesis Data Systems, 8415 Washington Place NE, Albuquerque, NM 87113
  14593. (505) 821-9425; FAX (505) 821-9695.
  14594.  
  14595.  
  14596. LISP Objects
  14597.  
  14598.  
  14599. Sapiens Software has released a beta test version of its Common LISP Object
  14600. System (CLOS) implementation.
  14601. CLOS supports generic functions and methods (rather than message passing), and
  14602. multiple inheritance of object slots. Star Sapphire CLOS is embedded in the
  14603. Star Sapphire LISP v3.1 run-time, written in C, which eliminates CLOS loading
  14604. time.
  14605. Star Sapphire LISP runs on any PC compatible with 640Kb and a hard disk;
  14606. extended memory can be used if installed. The product is $99.95 from Sapiens
  14607. Software Corporation, P.O. Box 3365, Santa Cruz CA 95063 (408) 458-1990.
  14608.  
  14609.  
  14610. Faircom Offers 'Special Edition'
  14611.  
  14612.  
  14613. The Faircom Corporation has released a new application development toolbox,
  14614. which includes the d-tree development environment, file management system and
  14615. report generation system.
  14616. Faircom is introducing this product with a $695 "Special Edition" package and
  14617. a 30-day, no-risk trial offer.
  14618. For more information, contact Faircom at (800) 234-8180, 4006 West Broadway,
  14619. Columbia, MO 65203; FAX (315) 445-9698.
  14620.  
  14621.  
  14622. Oakland Updates Screen Tools
  14623.  
  14624.  
  14625. Oakland Group, Inc. has released v3.1 of the Look & Feel screen designer and
  14626. the C-scape interface management system. Look & Feel lets you prototype and
  14627. simulate screens, and automatically turn screens into C source code that will
  14628. run across MS-DOS, OS/2, UNIX, and VMS. The new version of C-scape allows for
  14629. total portability, has fewer levels of indirection, and creates smaller
  14630. executables.
  14631. MS-DOS and OS/2 versions of C-scape with Look & Feel cost $399, including
  14632. source code. Look & Feel costs $149; C-scape $299. UNIX versions begin at
  14633. $999. Look & Feel source code costs $900.
  14634. For more information, contact Oakland Group, Inc., 675 Massachusetts Avenue,
  14635. Cambridge, MA 02139 (800) 233-3733 or (617) 491-7311.
  14636.  
  14637.  
  14638. New Linker
  14639.  
  14640.  
  14641. Pocket Soft, Inc., has released .RTLink/Plus, an advanced overlay linker which
  14642. supports debugging of programs with multiple and nested overlays with
  14643. Microsoft's CodeView debugger.
  14644. .RTLink/Plus also provides a unique link-time Profiler, which gives a detailed
  14645. performance analysis in timing intervals which are user-adjustable to
  14646. thousandths of a second.
  14647. Pocket Soft is an authorized licensee of Microsoft CodeView information.
  14648. .RTLink/Plus has a list price of $495 and is available through most common
  14649. distribution/reseller channels and direct from Pocket Soft, Inc., 7676
  14650. Hillmont, Suite 195, Houston, TX 77040 (713) 460-5600.
  14651.  
  14652.  
  14653. Tool Writes Dialog Box Source Code
  14654.  
  14655.  
  14656. The Software Organization, Inc. has released DialogCoder, a programming tool
  14657. that eliminates as much as 95 percent of the coding normally associated with
  14658. windows dialog box programming.
  14659.  
  14660. DialogCoder automatically generates C source code from dialog templates to
  14661. manage all controls in the dialog; it uses graphical metaphors to express the
  14662. relationships between dialog controls and actions, which eliminates most of
  14663. the conventional dialog control programming. It also allows users to
  14664. interactively specify the state of each dialog control during initialization
  14665. and command processing.
  14666. DialogCoder requires a 286-or 386-based machine with Windows 2.X. A
  14667. Microsoft-compatible mouse is optional. DialogCoder is $349. To order, contact
  14668. the Software Organization, Inc. at (800) 696-2012.
  14669.  
  14670.  
  14671. Trio Releases C-Index/PC
  14672.  
  14673.  
  14674. Trio Systems has started shipping a new $195 C database library, C-Index/PC.
  14675. The new product, based on their C-Index/Plus package, allows C programmers to
  14676. incorporate database features into their applications running under Microsoft
  14677. Windows, OS/2, and MS-DOS. The C-Index/PC database library supports
  14678. single-user and multi-user LAN applications with full file management
  14679. facilities.
  14680. Complete source code is supplied with C-Index/PC and can be adapted for use
  14681. with any PC compiler and operating system running on an Intel microcomputer.
  14682. Product features include: precompiled libraries for Microsoft C and Turbo C, B
  14683. + Tree indexing, variable-length records, direct and sequential access and
  14684. multiple record formats per file. There are no application royalties. For more
  14685. information, call (818) 798-5567.
  14686.  
  14687.  
  14688. New Debug Tool Traces Memory References
  14689.  
  14690.  
  14691. TUITS Inc. has introduced Dr. MD., a run time memory tracking utility that
  14692. finds memory overwrite bugs before an application crashes. Dr. MD catches
  14693. memory overwrites when they happen. It also catches 'free()s' on invalid
  14694. pointers, and dangling pointers.
  14695. Dr. MD will not allow you to overwrite allocated or automatic variables. When
  14696. Dr. MD finds a problem it reports the source file and the line number where
  14697. the problem was found as well as where the space was allocated. No heap
  14698. walking is needed.
  14699. Dr. MD comes as source and you compile it with your compiler to fit your
  14700. environment. The vendor claims it should work with any ANSII standard
  14701. compiler, and has worked successfully in MS-DOS and UNIX System V
  14702. environments. Dr. MD supports all the string library functions as well as
  14703. memset, memcpy, and limited support of sprintf.
  14704. Dr. MD sells for $59.95, and includes source code, manual, and some hints on
  14705. memory management. For more information contact TUITS Inc., 411 N. Shields,
  14706. Fort Collins, CO 80521, or call at (303) 224-9070.
  14707.  
  14708.  
  14709. AtLast Offers Overlay Tools
  14710.  
  14711.  
  14712. AtLast Software has released two new products: Overlay Architect, which
  14713. automates the process of overlay construction, and Overlay Optimizer, which
  14714. analyzes the performance of the program's overlay structure, then determines
  14715. how to rebuild the overlays for the best performance in a given amount of
  14716. space. AtLast Software will also custom build an overlay structure for
  14717. developers who do not want to build their own.
  14718. Overlay Architect sells for $369; Overlay Optimizer for $269. They can be
  14719. purchased together for $569. Quantity discounts are available. Custom built
  14720. structures are priced individually.
  14721.  
  14722.  
  14723. MicroWay 486 Compilers For C, Pascal & FORTRAN
  14724.  
  14725.  
  14726. MicroWay has released its 80486-targeted series of compilers, NDP C-486, NDP
  14727. Fortran-486, and NDP Pascal-486.
  14728. Each of the NDP-486 compilers include a "scheduler/code generator" that aligns
  14729. code and data on paragraph boundaries, detects and minimizes prefetch buffer
  14730. starving, uses new code sequences that run faster on the 80486 than the 80386,
  14731. and incorporates a new strategy for driving the Weitek 4167 high speed
  14732. coprocessor. They also provide a library of 70 device-independent graphics,
  14733. keyboard, and sound routines.
  14734. C, NDP Fortran, and Pascal-486 generate globally optimized, 32-bit native code
  14735. that runs in protected mode under UNIX 386 System V v3.0, SCO XENIX 386 v2.3,
  14736. and Phar Lap extended DOS. The compilers support the 486's built-in FPU and
  14737. the Weitek 4167 numeric coprocessor.
  14738. NDP C-486 is a two-dialect compiler that passes 100 percent of the Plum Hall
  14739. validation suite for UNIX System V C and 95 percent of the tests for the new
  14740. ANSI C standard. It includes an inline assembly language interface that
  14741. simplifies the writing of embedded code by allowing the programmer to specify
  14742. register values and generate interrupts.
  14743. The MS-DOS, UNIX, and XENIX versions of NDP C-486, NDP Fortran-486, and NDP
  14744. Pascal-486 retail at $1195 each. The C + + preprocessor lists at $495. All of
  14745. the compilers include one year of free updates.
  14746. Users should contact MicroWay's Technical Support Staff at (508) 746-7341 for
  14747. more information.
  14748.  
  14749.  
  14750. DOS Extender Supports Turbo C
  14751.  
  14752.  
  14753. Eclipse Computer Solutions, Inc.'s OS/286 MS-DOS extender now supports
  14754. Borland's Turbo C v2.0 and will soon support Turbo Pascal as well.
  14755. The MS-DOS extender products of Eclipse Computer Solutions, Inc. (formerly
  14756. A.I. Architects) exploit the protected mode operation of the 80286 and 80386
  14757. processors and make it possible to create, with conventional development
  14758. tools, applications that are not restricted by normal MS-DOS memory limits.
  14759. Contact Eclipse Computer Solutions, Inc., One Intercontinental Way, Peabody,
  14760. MA 01960 (508) 535-7510; FAX: (508) 535-7512.
  14761.  
  14762.  
  14763. T & T Enhances Data Junction
  14764.  
  14765.  
  14766. Tools & Techniques has released Data Junction v3.01. The new version adds
  14767. formats, an improved user interface, an expanded EZ-Convert mode, 300 percent
  14768. plus speed improvements, a built-in case translation, and new conversion
  14769. filters.
  14770. MS-DOS licenses are $99 for Data Junction: Standard, $199 for Data Junction:
  14771. Professional, and $299 for Data Junction: Advanced. UNIX/Xenix and LAN
  14772. licenses start at $495. Data Junction is written in C, and distribution/OEM
  14773. licenses are also available.
  14774. For more information, contact Micheal Hoskins at Tools & Techniques Inc., 1620
  14775. West 12th Street, Austin, TX 78703 (800) 444-1945, or (512) 482-0824.
  14776.  
  14777.  
  14778. LALR Adds Scanner Generator To Version 3.2
  14779.  
  14780.  
  14781. LALR Research has released LALR v3.2 which features the following improvements
  14782. over v3.0.
  14783. A lexical scanner generator is included which provides a 10 percent increase
  14784. in syntax checking speed over the previous hand-written scanner. An option has
  14785. been added to generate 0-40 percent smaller parsers. Multiple parsers can
  14786. exist in an application program. Parsers can read input files of unlimited
  14787. size.
  14788.  
  14789. The input grammar format for the new version is fully compatible with previous
  14790. versions.
  14791. LALR v3.2 is $249 and comes with a 60-day, money-back guarantee. Upgrades from
  14792. LALR v3.0 are $150. Shipping is $6.
  14793. For more information, contact LALR Research at PO Box 4722, Chico CA 95927
  14794. (916) 345-0916.
  14795.  
  14796.  
  14797. Solbourne Updates OS/MP
  14798.  
  14799.  
  14800. Solbourne Computer, Inc., has shipped the latest version of its
  14801. multiprocessing operating system, OS/MP v4.0A, which is based on the SunOS
  14802. v4.0.1, licensed from Sun Microsystems Inc.
  14803. OS/MP v4.0A introduces a set of system administration tools, to handle user
  14804. account maintenance, group account maintenance, network group account
  14805. maintenance, network account maintenance, NFS client maintenance, NFS server
  14806. configuration and modem installation.
  14807. OS/MP v4.0 also includes two new X Window tools. Smail is a user-friendly
  14808. interface to the standard UNIX mail environment. Sproperty displays the
  14809. property of any visible X Window.
  14810. Contact Solbourne Computer, Inc. at 1900 Pike Road, Longmont, CO 80501 (303)
  14811. 722-3400; FAX: (303) 772-3646.
  14812.  
  14813.  
  14814.  
  14815.  
  14816.  
  14817.  
  14818.  
  14819.  
  14820.  
  14821.  
  14822.  
  14823.  
  14824.  
  14825.  
  14826.  
  14827.  
  14828.  
  14829.  
  14830.  
  14831.  
  14832.  
  14833.  
  14834.  
  14835.  
  14836.  
  14837.  
  14838.  
  14839.  
  14840.  
  14841.  
  14842.  
  14843.  
  14844.  
  14845.  
  14846.  
  14847.  
  14848.  
  14849.  
  14850.  
  14851.  
  14852.  
  14853.  
  14854.  
  14855.  
  14856.  
  14857.  
  14858.  
  14859.  
  14860.  
  14861.  
  14862.  
  14863. Belief Maintenance Using The Dempster-Shafer Theory Of Evidence
  14864.  
  14865.  
  14866. Dwayne Phillips
  14867.  
  14868.  
  14869. The author works as a computer and electronics engineer with the U.S.
  14870. Department of Defense and is a doctoral candidate in Electrical and Computer
  14871. Engineering at Louisiana State University. His interests include computer
  14872. vision, artificial intelligence, software engineering, and programming
  14873. languages. He first used the Dempster Shafer theory of evidence in 1984 and
  14874. uses it extensively in his PhD research into computer vision.
  14875.  
  14876.  
  14877. An expert system makes a decision given an amount of evidence. Usually it must
  14878. choose between several competing answers or hypotheses. The human expert keeps
  14879. these answers in his mind while he thinks over the problem. He gathers
  14880. evidence and shifts his thoughts from one answer to another. After gathering
  14881. evidence, he chooses the most favorable answer. We all do this in our daily
  14882. decisions, but we don't think about the process, and we certainly don't keep
  14883. track of specific numbers in our head.
  14884. An expert system needs a sub-system to pool evidence and reach decisions: a
  14885. belief maintenance system. The belief maintenance system keeps track of the
  14886. hypotheses and the degree of belief attributed to each hypothesis. When the
  14887. expert system finishes gathering evidence, the belief maintenance system
  14888. chooses the answer.
  14889. In some expert systems a belief maintenance system is not necessary, because
  14890. some expert systems make decisions based on a single, clear cut piece of
  14891. evidence. For instance, suppose an expert system has the task of rolling up
  14892. the windows in your car. The evidence is whether or not it is raining. The
  14893. system would check the atmosphere and ask, "Is it raining?" If the answer were
  14894. yes, it would roll up the windows.
  14895. In other expert systems a belief maintenance system is essential. Suppose the
  14896. expert system had to decide at 9.00 AM whether or not to roll up the windows
  14897. at 3.00 PM. Now the question is tougher. Evidence would include the daily
  14898. weather forecast, the wind speed and direction, the relative humidity, weather
  14899. records from past years, forecasts from the Farmer's Almanac, satellite
  14900. photographs, and other relevant sources. The expert system would pool all the
  14901. evidence and arrive at an answer.
  14902. Consider the nature of evidence. Some evidence is not reliable (the weatherman
  14903. is wrong sometimes and right sometimes). Some evidence is uncertain (an
  14904. intermittent atmospheric reading). Some is incomplete (the wind speed by
  14905. itself does not tell us much). Some evidence is contradictory (the
  14906. weatherman's forecast and the atmospheric conditions). Finally, some evidence
  14907. is incorrect (a broken atmospheric sensor or a wrong weather forecast).
  14908. The belief maintenance system must deal with these factors, taking the
  14909. evidence, assigning a measure of belief to each hypothesis, and changing this
  14910. belief as new evidence becomes available. The resulting decision must be the
  14911. same regardless of the order in which the system gathers the evidence.
  14912. The method of belief maintenance that most of us know is classical
  14913. probability. The basic properties of this system are [Beyer]:
  14914. A) P(Æ) = 0 (null set)
  14915. B) P(Q) = 1 (entire sample set)
  14916. C) P(A) = 1 - P(A')
  14917. D) P(AB) = P(A) + P(B), if A and B are mutually exclusive
  14918. E) P(AÇB) = P(A) * P(B), if A and B are mutually exclusive
  14919. Another belief maintenance system came from the MYCIN project (a pioneering
  14920. medical expert system developed in the early seventies by Edward Shortliffe at
  14921. Stanford.) MYCIN used a system of certainty factors to keep track of
  14922. hypotheses. Shortliffe later dropped the certainty factor system for the
  14923. Dempster-Shafer theory of evidence.
  14924. The Dempster-Shafer (D-S) theory of evidence was created by Glen Shafer
  14925. [Shafer, 1976] at Princeton. He built on earlier work performed by Arthur
  14926. Dempster. The theory is a broad treatment of probabilities, and includes
  14927. classical probability and Shortliffe's certainty factors as subsets.
  14928. In the D-S theory of evidence, the set of all hypotheses that describes a
  14929. situation is the frame of discernment. The letter Q denotes the frame of
  14930. discernment. The hypotheses in Q must be mutually exclusive and exhaustive,
  14931. meaning that they must cover all the possibilities and that the individual
  14932. hypotheses cannot overlap.
  14933. The D-S theory mirrors human resoning by narrowing its reasoning gradually as
  14934. more evidence becomes available. Two properties of the D-S theory permit this
  14935. process: the ability to assign belief to ignorance, and the ability to assign
  14936. belief to subsets of hypotheses.
  14937. An example provides the easiest way to understand these properties and how
  14938. they differ from classical probability. Suppose we want to decide which of
  14939. three persons in an office -- Adam, Bob, and Carol -- will come in early to
  14940. turn on the lights and make coffee.
  14941. In the D-S theory the set Q = {Adam or Bob or Carol}. The sets {Adam}, {Bob},
  14942. and {Carol} are the mutually exclusive and exhaustive hypotheses. They are
  14943. singletons. In the frame of discernment there are 2Q or 8 possible
  14944. interpretations. (Figure 1)
  14945. Figure 1 contains two special sets in {Æ} and {Adam, Bob, Carol}. The first is
  14946. the null set, which cannot hold any value. As later examples will show, the
  14947. null set normalizes beliefs. The second special set is {Adam or Bob or Carol},
  14948. represented by Q. Assigning belief to Q does not help distinguish anything.
  14949. Therefore, Q represents ignorance.
  14950. Representing ignorance is a key concept. Humans often give weight to the
  14951. hypothesis "I don't know", which is not possible in classical probability.
  14952. Assigning belief to "I don't know" allows us to delay a decision until more
  14953. evidence becomes available. This mirrors the human tendency to procrastinate.
  14954. Suppose that given a piece of evidence, we make the assertion shown in Figure
  14955. 2. The D-S theory calls an assertion a basic probability assignment. The M in
  14956. Figure 2 represents the measure of belief. The assertion of Figure 2 says that
  14957. we believe Adam is the best choice with a weight of 0.6. We'll give the other
  14958. 0.4 of belief to Q or "I don't know," thus allowing us to delay deciding on
  14959. Adam.
  14960. We cannot make this type of assertion in classical probability. The classical
  14961. system's property of complements given earlier forces us to give Adam' 0.4
  14962. (the complement of Adam) if we give 0.6 to Adam. In this case Adam' = {Bob or
  14963. Carol}. Notice the difference between Q ={Adam or Bob or Carol} and Adam'
  14964. ={Bob or Carol}. Adam' gives more belief to Bob and Carol than we want. Q
  14965. allows us to express a true "no comment" on the situation.
  14966. Assigning belief to subsets in the D-S theory allows us to assign belief to a
  14967. general concept instead of being too specific. Suppose in our example that the
  14968. local police advise us that we should not have women coming to work early by
  14969. themselves. We would make an assertion like that, shown in Figure 3. This
  14970. assertion gives a weight of 0.7 to the subset {Adam or Bob} and a weight of
  14971. 0.3 to ignorance or no comment.
  14972. Classical probability does not permit a subset assertion. Recall that property
  14973. D requires P(Adam or Bob) = P(Adam) + P(Bob). That property would force us to
  14974. assign specific beliefs to Adam and to Bob individually. We do not want to be
  14975. that specific. We want to procrastinate and think it over some more.
  14976. Also, property C would make us assert the 0.3 to the complement of {Adam or
  14977. Bob} which is {Carol}. We do not want to assert 0.3 to {Carol}. Assigning
  14978. belief directly to {Carol} would contradict the evidence the police gave us.
  14979. The D-S theory employs Dempster's rule of combination to combine two
  14980. assertions. The mathematical formulas may be found in the references. They
  14981. confuse the best of us, but they are simple when illustrated. Figure 4 shows
  14982. how the two assertions combine.
  14983. The table in Figure 4 is an intersection tableau. Which lists one assertion
  14984. across the top and one down the side. Inside the tableau are the intersections
  14985. of the sets in the rows and columns, with the products assigned to the
  14986. intersections. The measures of belief inside the table sum to the final values
  14987. given below the table.
  14988. Notice how combination narrows the decision process. The single set {Adam} now
  14989. has the highest belief. The subset {Adam or Bob} comes in second with no
  14990. comment last.
  14991. Now suppose that we require the first person in the office in the morning to
  14992. bring up the computer system. Carol is an expert at this so we make the
  14993. assertion shown in Figure 5. This attributes most of the belief to {Carol}.
  14994. This new requirement or piece of evidence contradicts the previous evidence
  14995. given by the police. That is the nature of evidence. Dempster's rule of
  14996. combination allows us to combine the contradictory evidence and draw a logical
  14997. conclusion.
  14998. Figure 6 shows the combination of the result of Figure 4 and the assertion of
  14999. Figure 5. Inside the intersection tableau is the null set. There is no
  15000. intersection between the set {Carol} and the set {Adam} and there is also no
  15001. intersection between the set {Carol} and the set {Adam, Bob}. The null set
  15002. cannot hold any value. Therefore, it normalizes the beliefs of the other
  15003. subsets. The sum of the beliefs of the other subsets is divided by one minus
  15004. the belief in the null set. The beliefs of all the subsets sum to one. The
  15005. bottom of Figure 6 shows this extra step.
  15006. As a result, Carol is now the choice for coming in early in the morning. If
  15007. she is unable to do so, then Adam is the logical replacement. If Adam is
  15008. unavailable, then Bob comes in early.
  15009.  
  15010.  
  15011. Implementation
  15012.  
  15013.  
  15014. The preceding examples show that no complex mathematics are involved in
  15015. combining two assertions. Dempster's rule of combination uses simple addition,
  15016. subtraction, multiplication, and division. The only tricky part is the
  15017. intersections of the sets in the tableau.
  15018. There are several ways to solve the intersection question. Since there are
  15019. three singletons and 23 total interpretations, we'll represent the hypotheses
  15020. with three bits as in Figure 7.
  15021. Listing 2 shows the C function that combines two assertions. The inputs are
  15022. two belief vectors, each holding an assertion. The belief vector is a
  15023. one-dimensional array of floats. In our examples, the LENGTH_OF_BELIEF_VECTOR
  15024. is eight because we have three singletons and 23=8. The belief vector has a
  15025. space, or slot, for each hypothesis, ordered as in Figure 7. The belief vector
  15026. is awkward to initialize since we would like Adam in slot one, not slot four,
  15027. and Carol in slot three, not slot one. Nevertheless, a uniform belief vector
  15028. allows a very simple subroutine to combine the assertions.
  15029. The first for loop initializes the sum_vector, the belief vector which holds
  15030. the sums of the values found inside the intersection tableau. sum_vector holds
  15031. the sums for later when normalization occurs.
  15032. The for a loop goes through the belief vectors, finds the intersections, and
  15033. calculates the products. The two if > 0.0 statements reduce processing time by
  15034. eliminating unnecessary multiplication by zero. The function uses the C
  15035. bitwise AND operator & to find the intersection of sets. Without the bitwise
  15036. AND, the function would be much longer and much more complex.
  15037. The last for loop performs the normalization. The values in sum_vector are
  15038. divided by one minus the value assigned to the null set. The answer is stored
  15039. in vector1.
  15040. The combine_using_dempsters_rule function is the meat of the program written
  15041. in Turbo C v1.5. I used this compiler because it had a few functions that made
  15042. the user interface more pleasant. Except for those functions, there is nothing
  15043. in the program that is machine, compiler, or operating system specific.
  15044. One important note about implementing Dempster's rule of combination. The
  15045. number of calculations depends on 2Q . In our example there were eight
  15046. hypotheses. Alternatively, 200 single hypotheses would produce 2200 subsets,
  15047. 2200 slots in the belief vector, and 2200 floating point calculations. This
  15048. gets out of hand rather quickly. Several of the references [Gordon, Shortliffe
  15049. 1985] [Shafer 1985] [Shafer 1987] deal exclusively with this topic. The
  15050. discussion and proposed solutions are beyond the scope of this article.
  15051.  
  15052.  
  15053. Conclusion
  15054.  
  15055.  
  15056.  
  15057. The Dempster-Shafer theory of evidence is one method that an expert system may
  15058. use to keep score on competing hypotheses while it gathers evidence and draws
  15059. a logical conclusion. It is more general and capable than the classical
  15060. probability with which most of us are familiar. It is easy to implement and
  15061. executes quickly as long as the number of hypotheses is manageable. I suggest
  15062. you try it on your next expert system or AI-related project.
  15063. References
  15064. Beyer, William H., CRC Standard Mathematical Tables, 26th edition, CRC Press,
  15065. 1983, pp. 503-559.
  15066. Gordon, Jean, Edward H. Shortliffe, "The Dempster-Shafer, Theory of Evidence,"
  15067. pp. 272-292 of Shortliffe, Edward H., Bruce G. Buchanan, eds., Rule Based
  15068. Expert Systems, Addison Wesley Publishing Company, 1984.
  15069. Gordon, Jean, Edward H. Shortliffe, "A Method for Managing Evidential
  15070. Reasoning in a Hierarchical Hypothesis Space," Artificial Intelligence, Vol.
  15071. 26, No. 3, July 1985, pp. 323-357.
  15072. Shortliffe, Edward H., Bruce G. Buchanan, eds., Rule Based Expert Systems,
  15073. Addison Wesley Publishing Company, 1984.
  15074. Shafer, Glen, A Mathematical Theory of Evidence, Princeton University Press,
  15075. 1976.
  15076. Shafer, Glen, "Hierarchical Evidence," The Second Conference on Artificial
  15077. Intelligence Applications, IEEE Press, December 1985, pp. 16-21.
  15078. Shafer, Glen, Roger Logan, "Implementing Dempster's Rule for Hierarchical
  15079. Evidence," Artificial Intelligence, Vol. 33, No. 3, November 1987, pp.
  15080. 271-298.
  15081. Figure 1 Frame of Discernment for the Case of Adam, Bob, and Carol
  15082.  {Adam, Bob, Carol}
  15083. {Adam, Bob,} {Adam, Carol} {Bob, Carol}
  15084.  {Adam} {Bob} {Carol} {0}
  15085. Figure 2 An Assertion Showing the Use of Ignorance
  15086. m{Adam} = 0.6 m{Q} = 0.4
  15087. Figure 3 An Assertion Showing Belief Assigned to a Subset
  15088. m{Adam, Bob} = 0.7 m{Q} = 0.3
  15089. Figure 4 Combining Two Assertions Using Dempster's Rule of Combination
  15090. Figure 5 A New Assertion
  15091. m{Carol} = 0.9 m{Q} = 0.1
  15092. Figure 6 Combining Result of Figure 4 with Figure 5
  15093. Figure 7 Using Three bits to Represent the Hypotheses
  15094. bits hypothesis
  15095.  
  15096. 000 {0}
  15097.  
  15098. 001 {Carol}
  15099.  
  15100. 010 {Bob}
  15101.  
  15102. 011 {Bob, Carol}
  15103.  
  15104. 100 {Adam}
  15105.  
  15106. 101 {Adam, Carol}
  15107.  
  15108. 110 {Adam, Bob}
  15109.  
  15110. 111 {Adam, Bob, Carol} or {Q}
  15111.  
  15112. Listing 1
  15113. /*******************************************************************
  15114. * file d:\tc\cujds.c
  15115. *
  15116. * Functions: This file contains
  15117. * main
  15118. * display_belief_vector
  15119. * clear_belief_vector
  15120. * enter_belief_vector
  15121. * combine_using_dempsters_rule
  15122. *
  15123. * Purpose:
  15124. * This program demonstrates how to implement Dempster's
  15125. * rule of combination.
  15126. *
  15127. * NOTE: This is written for Borland's Turbo C
  15128. * Version 1.5. This allows us to use some
  15129. * nice user interface functions. The actual
  15130.  
  15131. * combination code is compiler independent.
  15132. *
  15133. ******************************************************************/
  15134.  
  15135. extern unsigned int _stklen = 40000;
  15136.  
  15137.  
  15138. #include "d:\tc\include\stdio.h"
  15139. #include "d:\tc\include\io.h"
  15140. #include "d:\tc\include\fcntl.h"
  15141. #include "d:\tc\include\dos.h"
  15142. #include "d:\tc\include\math.h"
  15143. #include "d:\tc\include\graphics.h"
  15144. #include "d:\tc\include\conio.h"
  15145. #include "d:\tc\include\sys\stat.h"
  15146.  
  15147. #define LENGTH_OF_BELIEF_VECTOR 8
  15148.  
  15149.  
  15150.  
  15151. main()
  15152. {
  15153.  
  15154. char response[80];
  15155.  
  15156. int choice,
  15157. i,
  15158. j,
  15159. not_finished;
  15160.  
  15161. short place;
  15162. float a[LENGTH_OF_BELIEF_VECTOR],
  15163. belief,
  15164. v[LENGTH_OF_BELIEF_VECTOR];
  15165.  
  15166.  
  15167. textbackground(1);
  15168. textcolor(7);
  15169. clrscr();
  15170.  
  15171. not_finished = 1;
  15172. while(not_finished){
  15173.  
  15174. clrscr();
  15175. printf("\n> You may now either:");
  15176. printf("\n 1. Start the process");
  15177. printf("\n 2. Enter more assertions");
  15178. printf("\n 3. Exit program");
  15179. printf("\n _\b");
  15180. get_integer(&choice);
  15181.  
  15182. switch (choice){
  15183.  
  15184. case 1:
  15185. clear_belief_vector(v);
  15186. clear_belief_vector(a);
  15187. clrscr();
  15188. enter_belief_vector(v, 1);
  15189. clrscr();
  15190.  
  15191. enter_belief_vector(a, 1);
  15192. clrscr();
  15193. printf("\n> Initial Belief Vector\n");
  15194. display_belief_vector(v);
  15195. printf("\n> Second Belief Vector\n");
  15196. display_belief_vector(a);
  15197. combine_using_dempsters_rule(v, a);
  15198. printf("\n> Resultant Belief Vector\n");
  15199. display_belief_vector(v);
  15200. break;
  15201.  
  15202. case 2:
  15203. clrscr();
  15204. clear_belief_vector(a);
  15205. enter_belief_vector(a, 1);
  15206. clrscr();
  15207. printf("\n> Initial Belief Vector\n");
  15208. display_belief_vector(v);
  15209. printf("\n> Second Belief Vector\n");
  15210. display_belief_vector(a);
  15211. combine_using_dempsters_rule ( v, a);
  15212. printf("\n> Resultant Belief Vector\n");
  15213. display_belief_vector(v);
  15214. break;
  15215.  
  15216. case 3:
  15217. not_finished = 0;
  15218. break;
  15219.  
  15220. } /* ends switch choice */
  15221. } /* ends while not_finished */
  15222. } /* ends main */
  15223.  
  15224.  
  15225. clear_belief_vector (v)
  15226. float v[];
  15227. {
  15228. int i;
  15229.  
  15230. for(i=0; i<LENGTH_OF BELIEF_VECTOR; i++)
  15231. v[i] = 0.0;
  15232. } /* ends clear_belief_vector */
  15233.  
  15234.  
  15235.  
  15236. display_belief_vector(v)
  15237. float v[];
  15238. {
  15239. int i, j;
  15240. char response[80];
  15241.  
  15242. j=1;
  15243. for(i=0; i<LENGTH_OF_BELIEF_VECTOR; i++){
  15244. if((j%5) == 0){
  15245. printf("\n");
  15246. j++;
  15247. }
  15248. if(v[i] > 0.0001){
  15249. printf(" [%3d]=%6f",i, v[i]);
  15250.  
  15251. j++;
  15252. }
  15253. }
  15254. printf("\n Hit RETURN to continue");
  15255. read_string(response);
  15256. } /* ends display_belief_vector */
  15257.  
  15258.  
  15259.  
  15260. enter_belief_vector(v, line)
  15261. float v[];
  15262. int line;
  15263. {
  15264. int i,
  15265. not_finished,
  15266. y;
  15267. float value;
  15268. y = line;
  15269.  
  15270. printf("\n> ENTER BELIEF VECTOR");
  15271.  
  15272. printf("\n> Enter the place (RETURN) and value (RETURN)");
  15273. printf("\n> (Enter -1 for place when you're finished)");
  15274.  
  15275. not_finished = 1;
  15276. while(not_finished){
  15277. printf("\n [__]=___");
  15278. y = wherey();
  15279. gotoxy(5, y);
  15280. get_integer(&i);
  15281. gotoxy(10, y);
  15282. get_float(&value);
  15283.  
  15284. if(i != -1){
  15285. v[i] = value;
  15286. } /* ends if i 1+ -1 */
  15287. else
  15288. not_finished = 0;
  15289.  
  15290. } /* ends while not_finished */
  15291. } /* ends enter_belief_vector */
  15292.  
  15293. /***************************************************************
  15294. *
  15295. * This is the function that implements Demptser's rule
  15296. * of combination.
  15297. * vector1 holds the original beliefs and will hold the
  15298. * result of the combination.
  15299. *
  15300. ***************************************************************/
  15301.  
  15302. combine_using_dempsters_rule(vector1, vector2)
  15303. float vectorl[LENGTH_OF_BELIEF_VECTOR],
  15304. vector2 [LENGTH_OF_BELIEF_VECTOR];
  15305. {
  15306. float denominator,
  15307. sum_vector[LENGTH_OF_BELIEF_VECTOR];
  15308.  
  15309. int a,
  15310.  
  15311. i,
  15312. place;
  15313.  
  15314. /* set the sums to zero */
  15315. for(i=0; i<LENGTH_OF_BELIEF_VECTOR; i++)
  15316. sum_vector[i] = 0.0;
  15317.  
  15318. /* Now go through the intersection tableau. */
  15319. /* Look for the intersection of non-zero beliefs */
  15320. /* and save their products. */
  15321. for(a=1; a<LENGTH_OF_BELIEF_VECTOR; a++){
  15322. if(vector2[a] > 0.0){
  15323. for[i-0; i<LENGTH_OF_BELIEF_VECTOR; i++){
  15324. place = i & a;
  15325. if(vector1[i] > 0.0)
  15326. sum_vector[place] = (vector1[i] * vector2[a])
  15327. + sum_vector [place];
  15328. } /* ends loop over i */
  15329. } /* ends if vector2[a] > 0.0 */
  15330. } /* ends loop over a */
  15331.  
  15332. denominator = 1.0 - sum_vector[0];
  15333. for(i=1; i<LENGTH_OF_BELIEF_VECTOR; i++)
  15334. vector[i] = sum_vector[i]/denominator;
  15335.  
  15336. } /* ends combine_using_dempsters_rule */
  15337.  
  15338.  
  15339.  
  15340. /* The following functions are I-O */
  15341.  
  15342.  
  15343. read_string(string)
  15344. char *string;
  15345. {
  15346. int eof,
  15347. letter,
  15348. no_error;
  15349.  
  15350. eof = -1;
  15351. no_error = 0;
  15352.  
  15353. while((letter = getchar()) != '\n' &&
  15354. letter != eof)
  15355. *string++ = letter;
  15356.  
  15357. *string = '\0';
  15358.  
  15359. return((letter == eof) ? eof : no_error);
  15360.  
  15361. } /* ends read_string */
  15362.  
  15363.  
  15364. clear_buffer(string)
  15365. char string[];
  15366. {
  15367. int i;
  15368. for(i=0; i<80; i++)
  15369. string[i] = ' ';
  15370.  
  15371. }
  15372.  
  15373. long_clear_buffer(string)
  15374. char string[];
  15375. {
  15376. int i;
  15377. for(i=0; i<300; i++)
  15378. string[i] = ' ';
  15379. }
  15380.  
  15381.  
  15382.  
  15383. #define is_digit(x) ((x >= '0' && x <= '9') ? 1 : 0)
  15384.  
  15385. #define is_blank(x) ((x == ' '] ? 1 : 0)
  15386.  
  15387. #define to_decimal(x) (x - '0')
  15388.  
  15389. #define NO_ERROR 0
  15390. #define IO_ERROR -1
  15391. #define NULL2 '\0'
  15392.  
  15393. get_integer(n)
  15394. int *n;
  15395. {
  15396. char string[80];
  15397.  
  15398. read_string(string);
  15399. int_convert(string, n);
  15400. }
  15401.  
  15402.  
  15403.  
  15404.  
  15405. int_convert (ascii_val, result)
  15406. char *ascii_val;
  15407. int *result;
  15408. {
  15409. int sign = 1; /* -1 if negative */
  15410.  
  15411. *result = 0; /* value returned to the calling routine */
  15412.  
  15413. /* read passed blanks */
  15414.  
  15415. while (is_blank(*ascii_val))
  15416. ascii_val++; /* get next letter */
  15417.  
  15418. /* check for sign */
  15419.  
  15420. if (*ascii_val == '-' *ascii_val == '+')
  15421. sign = (*ascii_val++ == '-') ? -1 : 1; /* find sign */
  15422.  
  15423. /*
  15424. * convert the ASCII representation to the actual
  15425. * decimal value by subtracting '0' from each character.
  15426. *
  15427. * for example, the ASCII '9' is equivalent to 57 in decimal.
  15428. * by subtracting '0' (or 48 in decimal) we get the desired
  15429. * value.
  15430.  
  15431. *
  15432. * if we have already converted '9' to 9 and the next character
  15433. * is '3', we must first multiply 9 by 10 and then convert '3'
  15434. * to decimal and add it to the previous total yielding 93.
  15435. *
  15436. */
  15437.  
  15438. while (*ascii_val)
  15439. if (is_digit(*ascii_val))
  15440. *result = *result * 10 + to_decimal(*ascii_val++);
  15441.  
  15442. else
  15443. return (IO_ERROR);
  15444.  
  15445. *result = *result * sign;
  15446.  
  15447. return (NO_ERROR);
  15448. }
  15449.  
  15450.  
  15451.  
  15452. get_short(n)
  15453. short *n;
  15454. {
  15455. char string[80];
  15456.  
  15457. read_string(string);
  15458. int_convert(string, n);
  15459. }
  15460.  
  15461. short_convert (ascii_val, result)
  15462. char *ascii_val;
  15463. short *result;
  15464. {
  15465. int sign = 1; /* -1 if negative */
  15466.  
  15467. *result = 0; /* value returned to the calling routine */
  15468.  
  15469. /* read passed blanks */
  15470.  
  15471. while (is_blank(*ascii_val))
  15472. ascii_val++; /* get next letter */
  15473.  
  15474. /* check for sign */
  15475.  
  15476. if (*ascii_val == '-' *ascii_val == '+')
  15477. sign = (*ascii_val++ == '-') ? -1 : 1; /* find sign */
  15478.  
  15479. /*
  15480. * convert the ASCII representation to the actual
  15481. * decimal value by subtracting '0' from each character.
  15482. *
  15483. * for example, the ASCII '9' is equivalent to 57 in decimal.
  15484. * by subtracting '0' (or 48 in decimal) we get the desired
  15485. * value.
  15486. *
  15487. * if we have already converted '9' to 9 and the next character
  15488. * is '3', we must first multiply 9 by 10 and then convert '3'
  15489. * to decimal and add it to the previous total yielding 93.
  15490.  
  15491. *
  15492. */
  15493.  
  15494. while (*ascii_val){
  15495. if (is_digit(*ascii_val)){
  15496. *result = *result * 10 + to_decimal(*ascii_val++);
  15497. if( (sign == -1) && (*result > 0)) *result = *result * -1;
  15498. }
  15499. else
  15500. return (IO_ERROR);
  15501. } /* ends while ascii_val */
  15502.  
  15503. return (NO_ERROR);
  15504. }
  15505.  
  15506.  
  15507.  
  15508. get_long(n)
  15509. long *n;
  15510. {
  15511. char string(80];
  15512.  
  15513. read_string(string);
  15514. long_convert(string, n);
  15515. }
  15516.  
  15517.  
  15518.  
  15519. long_convert (ascii_val, result)
  15520. char *ascii_val;
  15521. long *result;
  15522. {
  15523. int sign = 1; /* -1 if negative */
  15524.  
  15525. *result = 0; /* value returned to the calling routine */
  15526.  
  15527. /* read passed blanks */
  15528.  
  15529. while (is_blank(*ascii_val))
  15530. ascii_val++; /* get next letter */
  15531.  
  15532. /* check for sign */
  15533.  
  15534. if (*ascii_val == '-' *ascii_val == '+')
  15535. sign = (*ascii_val++ == '-') ? -1 : 1; /* find sign */
  15536.  
  15537. /*
  15538. * convert the ASCII representation to the actual
  15539. * decimal value by subtracting '0' from each character.
  15540. *
  15541. * for example, the ASCII '9' is equivalent to 57 in decimal.
  15542. * by subtracting '0' (or 48 in decimal) we get the desired
  15543. * value.
  15544. *
  15545. * if we have already converted '9' to 9 and the next character
  15546. * is '3', we must first multiply 9 by 10 and then convert '3'
  15547. * to decimal and add it to the previous total yielding 93.
  15548. *
  15549. */
  15550.  
  15551.  
  15552. while (*ascii_val)
  15553. if (is_digit(*ascii_val))
  15554. *result = *result * 10 + to_decimal(*ascii_val++);
  15555.  
  15556. else
  15557. return (IO_ERROR);
  15558.  
  15559. *result = *result * sign;
  15560.  
  15561. return [NO_ERROR);
  15562. }
  15563.  
  15564.  
  15565.  
  15566.  
  15567. get_float(f)
  15568. float *(f);
  15569. {
  15570. char string[80];
  15571.  
  15572. read_string(string);
  15573. float_convert(string, f);
  15574. }
  15575.  
  15576. float_convert (ascii_val, result)
  15577. char *ascii_val;
  15578. float *result;
  15579. {
  15580. int count; /* # of digits to the right of the
  15581. decimal point. */
  15582. int sign = 1; /* -1 if negative */
  15583.  
  15584. double pow10(); /* Turbo C function */
  15585. float power(); /* function returning a value raised
  15586. to the power specified. */
  15587.  
  15588. *result = 0.0; /* value desired by the calling routine */
  15589.  
  15590. /* read passed blanks */
  15591.  
  15592. while (is_blank(*ascii_val))
  15593. ascii_val++; /* get the next letter */
  15594.  
  15595. /* check for a sign */
  15596.  
  15597. if (*ascii_val == '-' *ascii_val == '+')
  15598. sign = (*ascii_val++ == '-') ? -1 : 1; /* find sign */
  15599.  
  15600. /*
  15601. * first convert the numbers on the left of the decimal point.
  15602. *
  15603. * if the number is 33.141592 this loop will convert 33
  15604. *
  15605. * convert ASCII representation to the actual decimal
  15606. * value by subtracting '0' from each character.
  15607. *
  15608. * for example, the ASCII '9' is equivalent to 57 in decimal.
  15609. * by subtracting '0' (or 48 in decimal) we get the desired
  15610.  
  15611. * value.
  15612. *
  15613. * if we have already converted '9' to 9 and the next character
  15614. * is '3', we must first multiply 9 by 10 and then convert '3'
  15615. * to decimal and add it to the previous total yielding 93.
  15616. *
  15617. */
  15618.  
  15619. while (*ascii_val)
  15620. if [is_digit(*ascii_val))
  15621. *result = *result * 10 + to_decimal(*ascii_val++);
  15622. else if (*ascii_val == '.') /* start the fractional part */
  15623. break;
  15624.  
  15625. else
  15626. return (IO_ERROR);
  15627.  
  15628. /*
  15629. * find number to the right of the decimal point.
  15630. *
  15631.  
  15632.  
  15633. * if the number is 33.141592 this portion will return 141592.
  15634. *
  15635. * by converting a character and then dividing it by 10
  15636. * raised to the number of digits to the right of the
  15637. * decimal place the digits are placed in the correct locations.
  15638. *
  15639. * 4 / power = (10, 2) ==> 0.04
  15640. *
  15641. */
  15642.  
  15643. if (*ascii_val != NULL2)
  15644. {
  15645.  
  15646. ascii_val++; /* past decimal point */
  15647.  
  15648. for (count = 1; *ascii_val != NULL2; count++, ascii_val++)
  15649.  
  15650. /*************************************************
  15651. *
  15652. * The following change was made 16 June 1987.
  15653. * For some reason the power function below
  15654. * was not working. Borland's Turbo C pow10
  15655. * was substituted.
  15656. *
  15657. *************************************************/
  15658.  
  15659. if (is_digit(*ascii_val)){
  15660. *result = *result
  15661. + to_decimal(*ascii_val)/((float)(pow10(count)));
  15662.  
  15663. /***********
  15664. *result = *result
  15665. + to_decimal(*ascii_val)/power(10.0,count);
  15666. ************/
  15667. }
  15668.  
  15669. else
  15670.  
  15671. return (IO_ERROR);
  15672. }
  15673.  
  15674. *result = *result *sign; /* positive or negative value */
  15675.  
  15676. return (NO_ERROR);
  15677. }
  15678.  
  15679. float power(value, n)
  15680. float value;
  15681. int n;
  15682. {
  15683.  
  15684. int count;
  15685. float result;
  15686.  
  15687. if(n < 0)
  15688. return(-1.0);
  15689.  
  15690. result = 1;
  15691. for(count=1; count<=n; count++){
  15692. result = result * value;
  15693. }
  15694.  
  15695.  
  15696. Listing 2 C Code to Implement Dempster's Rule of Combination
  15697. /*
  15698. * This is the function that implements dempster's rule
  15699. * of combination.
  15700. * vector1 & vector2 are belief vectors. vector2 will
  15701. * hold the result of the combination.
  15702. */
  15703. #define LENGTH_OF_BELIEF_VECTOR 8
  15704.  
  15705. combine_using_dempsters_rule (vector1, vector2)
  15706. float vector1[LENGTH_OF_BELIEF_VECTOR],
  15707. vector2[LENGTH_OF_BELIEF_VECTOR];
  15708. {
  15709. float denominator, sum_vector[LENGTH_OF_BELIEF_VECTOR];
  15710.  
  15711. int a, i, place;
  15712.  
  15713. /* set the sums to zero */
  15714. for(i=0; i<LENGTH_OF_BELIEF_VECTOR; i++)
  15715. sum_vector[i] = 0.0;
  15716.  
  15717. /* Now go through the intersection tableau.
  15718. * Look for the intersection of non-zero beliefs
  15719. * and save their products. */
  15720.  
  15721. for(a=1; a<LENGTH_OF_BELIEF_VECTOR; a++){
  15722.  
  15723. if(vector2[a] > 0.0){
  15724.  
  15725. for(i=0; i<LENGTH_OF_BELIEF_VECTOR; i++){
  15726. place = i & a;
  15727. if(vector1[i] > 0.0)
  15728. sum_vector[place] = (vector1[i] * vector2[a])
  15729. + sum_vector[place];
  15730.  
  15731. } /* ends loop over i */
  15732.  
  15733. } /* ends if vector2[a] > 0.0 */
  15734.  
  15735. } /* ends loop over a */
  15736.  
  15737. denominator = 1.0 - sum_vector[0];
  15738. for(i=1; i<LENGTH_OF_BELIEF_VECTOR; i++)
  15739. vectorl[i] = sum_vector[i]/demoninator;
  15740.  
  15741. } /* ends combine_using_dempsters_rule */
  15742.  
  15743.  
  15744.  
  15745.  
  15746.  
  15747.  
  15748.  
  15749.  
  15750.  
  15751.  
  15752.  
  15753.  
  15754.  
  15755.  
  15756.  
  15757.  
  15758.  
  15759.  
  15760.  
  15761.  
  15762.  
  15763.  
  15764.  
  15765.  
  15766.  
  15767.  
  15768.  
  15769.  
  15770.  
  15771.  
  15772.  
  15773.  
  15774.  
  15775.  
  15776.  
  15777.  
  15778.  
  15779.  
  15780.  
  15781.  
  15782.  
  15783.  
  15784.  
  15785.  
  15786.  
  15787.  
  15788.  
  15789.  
  15790.  
  15791.  
  15792.  
  15793.  
  15794. An Introduction To Speech Recognition
  15795.  
  15796.  
  15797. B.J. Gleason
  15798.  
  15799.  
  15800. B.J. Gleason is an Assistant Professor at Upsala College in New Jersey, and
  15801. holds a master's degree in computer science. He is currently working on an
  15802. Ed.D. in computer education at Nova University. Contact Mr. Gleason care of
  15803. the Computer Science Department, Upsala College, East Orange, NJ 07019.
  15804.  
  15805.  
  15806. "Open the pod bay door, Hal."
  15807. "I'm sorry, Dave, I'm afraid I can't do that." --
  15808. 2001, A Space Odyssey
  15809. "Shields Open." --Batman
  15810. One of the friendliest user interfaces should be voice. At some point in the
  15811. future, we will be able to talk to our computer system and have it understand
  15812. us. In the two examples above, the computer understood the spoken word. In
  15813. Hal's case, he could also lip-read (An article for this is currently in
  15814. progress.). With the Batmobile, the speech recognition is more realistic, if
  15815. not the automatic pilot in the car.
  15816. In the above paragraph, I use the word "should" to describe voice as a
  15817. friendly interface. It should be, but at this time, it isn't. Not for lack of
  15818. trying, however. Speech recognition (SR) is not yet in the home although it
  15819. has been around for about 20 years. SR is still unreliable and not easy to
  15820. use. Yet.
  15821. Speech recognition is a wide field that must be broken down if it is to be
  15822. understood. There are two portions to a speech recognition (SR) system. The
  15823. first is the recognition portion. This is almost easy. The person says
  15824. something and the recognizer returns the words that were spoken. The second
  15825. part, the understanding of what was said, is much harder. The second portion
  15826. falls into the area of Natural Language Processing. In the Batman example, the
  15827. understand portion is easy. In the 2001 example, it is harder. For example,
  15828. Hal must not only know how to open the door, but why the door is to be opened.
  15829. Hal understands this, and realizes that he must not open the door for Dave.
  15830. In this article, I will describe the first portion, the recognition procedure.
  15831. It is beyond the present scope to describe Natural Language Processing. We
  15832. will take a look at the current techniques used in SR and I will provide you
  15833. with everything you need in order to experiment with SR on your own.
  15834.  
  15835.  
  15836. Intro To Speech Recognition
  15837.  
  15838.  
  15839. Most SR systems belong to one of two fundamental classes: speaker dependent or
  15840. speaker independent.
  15841. With speaker dependent systems, the user must first "train" the system to
  15842. recognize his or her voice. During training the system displays a word, the
  15843. user pronounces it, and the system saves the resulting voice pattern. Once a
  15844. sample of all the required words is stored, the user may begin using the
  15845. system. When the user speaks, the system will take the unknown voice pattern
  15846. and compare it to the patterns stored in memory. The word associated with the
  15847. closest matching pattern is returned as being the word the system believes the
  15848. user spoke. This is the most common technique.
  15849. Speaker dependent SR units are capable of 91% to 99% accuracy. While the
  15850. training is time consuming, this technique is economical. You can pick up one
  15851. of these units for less than 100 dollars. If you are electronically inclined,
  15852. you can build one for less than 50 dollars.
  15853. A speaker independent system requires no training. It should recognize words
  15854. as soon as it is turned on. Speaker independence is much harder to accomplish.
  15855. The voice pattern is broken down and analyzed for certain features -- features
  15856. chosen to distinguish among the words to be recognized.
  15857. Speaker independent systems are very attractive, and will become more so in
  15858. the future. At this time however, most speaker independent units are
  15859. expensive, achieve accuracies of only 85% to 95%, and often recognize fewer
  15860. words as well. Speaker independent systems require more processing power to do
  15861. the analysis, require dedicated processors and start in the 500 dollar range.
  15862. Radio Shack is now selling a speaker independent isolated word recognizer chip
  15863. for about 10 dollars. Build an amplifier circuit for it and it will recognize
  15864. nine different words. This particular chip is used in a number of voice
  15865. activated toys.
  15866.  
  15867.  
  15868. Specifications
  15869.  
  15870.  
  15871. How many words do we need to recognize? The ultimate dream, of course, is the
  15872. voice typewriter which would have to be speaker independent and to recognize
  15873. about 50,000 or so words. But many applications can get away with much less. A
  15874. simple voice calculator for example, would need to recognize only 10 digits,
  15875. four operations, a decimal point, and an equal sign -- only 16 words. Several
  15876. companies use voice inventory systems. These require the digits and a few
  15877. commands, again around 16 or so words.
  15878. Many applications need only a few words. A small vocabulary has important
  15879. advantages. Larger vocabularies require more memory and more processing time
  15880. to find a match. With larger vocabularies, accuracy will typically decrease.
  15881. Given the current state of the art, it would be best to limit the vocabulary
  15882. to the smallest possible number of words.
  15883. Speech habits also affect recognition systems. Most humans talk in what is
  15884. known as continuous speech. Most SR units depend upon isolated speech.
  15885. Research has shown that it is very difficult to separate the words in
  15886. continuous speech. The pauses between words in continuous speech are very
  15887. short -- sometimes even shorter than the natural pauses that occur within
  15888. words. The emphasis and accents common to continous speech create additional
  15889. problems. There is typically a marked difference between the pronunciation of
  15890. a sentence and a collection of words.
  15891. Dictating to an isolated word recognizer for dictation takes some getting used
  15892. to. You must pause, typically for one tenth of a second or more, between
  15893. words. Until you get used to it, it can be frustrating.
  15894.  
  15895.  
  15896. The Hardware
  15897.  
  15898.  
  15899. In order to talk to your computer, we need a device to translate voice waves
  15900. into binary values. Figure 1 shows a block diagram of a typical SR unit. The
  15901. microphone is fed into a preamp circuit. The output of the preamp is fed into
  15902. two bandpass filters. The lowband filter has a range of 150Hz to 900Hz. The
  15903. highband filter has a range of 1Khz to about 4Khz. The output of the bandpass
  15904. filters is a sinewave whose frequency falls within the range of the associated
  15905. filter.
  15906. Using these bandpass filters we can isolate the high and low frequencies of
  15907. the utterance. More sophisticated SR units might have more bandpass filters to
  15908. include midranges as well.
  15909. The output of the bandpass filters goes into a zero crossing detector (ZCD)
  15910. and a rectifier/averager (R/A) circuit. The ZCD is a comparator that tests its
  15911. input against a reference voltage. When the signal is above the reference
  15912. voltage, the output is one; when less than the reference voltage, it is zero.
  15913. The timing output of the ZCD will approximate the input frequency.
  15914. The R/A circuit converts the average RMS AC signal to its DC equivalent. This
  15915. signal is then inverted and fed into a comparator set to slightly less than
  15916. the reference voltage. With no speech, the normal output would be zero. When
  15917. speech enters the system, the output of the comparator will go to one,
  15918. indicating that voice input is present.
  15919. These four outputs are fed into a parallel port and read by the computer.
  15920.  
  15921.  
  15922. Speaker Independent Systems
  15923.  
  15924.  
  15925. Now that we can get the speech into the computer system, we need to process
  15926. it. There are several different techniques for speaker independent speech. The
  15927. technique I will describe is phonetic analysis.
  15928. The sound that we produce to form words can be broken down into several
  15929. categories:
  15930. Pure Voice Vowels (V): a,e,i,o,u,uh, aa,ee,er,uu,ar,aw
  15931. Nasal (N): m,n,ng
  15932. Voice Fricative (VF): z,zh,v,dh
  15933.  
  15934. Unvoiced Fricative (UF): s,sh,f,th
  15935. Plosive (P): b,d,g,p,t,k,h
  15936. Glide (G): r,w,l,y
  15937. Our speaker independent board and program would first take the speech
  15938. utterance and break it down into these categories. For example, vowels sounds
  15939. are continuous and generate low frequency energy, whereas fricatives are
  15940. continuous but generate high frequency energy. The software must look for the
  15941. characteristics of each group in the input speech. It would then generate a
  15942. sequence of these phonetic grouping and look up the word in a dictionary:
  15943.  
  15944. 0 VF-V-G-V YES G-V-UF
  15945. 1 G-V-N NO N-V
  15946. 2 P-V START UF-P-V-P
  15947. 3 UF-G-V STOP UF-P-V-P
  15948. 4 UF-V-G
  15949. 5 UF-V-VF
  15950. 6 UF-V-P-UF
  15951. 7 UF-V-VF-N
  15952. 8 V-P
  15953. 9 N-V-N
  15954. This small table starts to illustrate some of the problems with this
  15955. technique. As we add words, correctly grouping the phonemes becomes a parsing
  15956. nightmare. Notice that the phonetic makeup for START and STOP are the same!
  15957. How can we tell the difference? We can't. Not easily anyway. Larger
  15958. vocabularies well generate even more "collisions". In a 20,000 word
  15959. dictionary, there are only about 2000 unique phonic combinations. 10,000 of
  15960. the words are five phonetic symbols or less.
  15961. Another technique, template averaging, reads in several copies of a word and
  15962. then finds the major features of the word. This technique is a mix between
  15963. speaker dependent and independent. In the training (similar to speaker
  15964. dependent) phase, five people save the same set of words. The program finds
  15965. the major features and uses these collective patterns as the model. The system
  15966. is then speaker independent and will recognize almost any voice pronouncing
  15967. the word.
  15968.  
  15969.  
  15970. Speaker Dependent Systems
  15971.  
  15972.  
  15973. Speaker dependent systems are reasonably straightforward. They all work on the
  15974. same principle. In the training phase, the sounds are stored in a reference
  15975. template. In the speech phase, the unknown sound is compared against the known
  15976. sounds for the closest match.
  15977. I have prepared a program and data files that will allow you to experiment
  15978. with an isolated word, speaker independent system. The program appears in
  15979. Listing 1. The data files are available on the code disk for this issue.
  15980.  
  15981.  
  15982. How It Works
  15983.  
  15984.  
  15985. If we are training or analyzing the speech, the same process goes on when a
  15986. person talks into the microphone.
  15987. To capture sound and reproduce any waveform, we must sample it at twice the
  15988. highest frequency (the Nyquist rate). For speech we must capture and store
  15989. 8000 four-byte samples per second. At this rate, we would need 32K of storage
  15990. for a single word. Most SR units don't really sample at the Nyquist rate,
  15991. since they don't reproduce the sound. SR units commonly capture only 100
  15992. four-byte samples per second. Assuming a maximum of 1.5 seconds of speech per
  15993. utterance, each word will require less than 600 bytes of storage.
  15994. The comparator circuit converts the output of the bandpass filters into square
  15995. waves at a relative frequency. The software counts the number of square waves
  15996. in both the low and the high bandpass filters during each 0.01 second
  15997. interval, producing a one-byte approximation to the frequency in each band.
  15998. These numbers, along with the values of high and low energy (four values all
  15999. together), are stored in a "raw speech buffer". Up to 150 samples can be
  16000. stored, enough for about 1.5 seconds of speech. The beginning of speech is
  16001. indicated by at least 0.1 seconds of sound, and the end of speech is at least
  16002. 0.1 seconds of silence.
  16003. The "raw" bytes produced by this process are still not very useful, as they
  16004. may still be "distorted" by "time warping." If you say the word ONE twice and
  16005. each time say it slightly slower, each repetition will appear to be a longer
  16006. word. To compensate for this variance, we time-normalize the word so that it
  16007. can be matched against faster or slower pronunciations.
  16008. Once the end of speech is detected, the buffer is broken down into 16 evenly
  16009. spaced points, based on the total length of the word. At each of the 16
  16010. points, a value is taken from the high and low energy, as well as the two zero
  16011. crossing detectors. The final result is a 64-byte template of the utterance.
  16012. Time normalization forces all the templates to be the same size, making it
  16013. easier to compare against one another.
  16014. A template constructed during the training phase would be stored in an array,
  16015. indexed to the number of a word. A template constructed while analyzing the
  16016. speech is instead compared to all the known templates in the array.
  16017. During the recognition phase, the speech input is reduced to a template which
  16018. is compared against the stored templates. As the unknown template is compared
  16019. against each stored template, a difference value -- the minimum difference --
  16020. is computed.
  16021. During the matching process, each minimum difference is also compared against
  16022. a rejection limit; if the program can't find a match with a minimum difference
  16023. at least as small as the rejection limit, it will respond "unknown word"
  16024. instead of making a wild guess. Thus, the rejection limit sets the degree of
  16025. "confidence" required to announce a match. Higher rejection limits will result
  16026. in more erroneous matches; lower limits will result in more "unknown word"
  16027. responses.
  16028. Calculating all the minimum differences is time consuming. We can avoid some
  16029. of these calculations by abandoning the calculation as soon as the partial
  16030. result exceeds the rejection limit. We can get even better results by
  16031. "remembering" the best minimum difference calculated so far and abandoning the
  16032. calculation as soon as the partial result exceeds this limit. Both
  16033. optimizations can be folded into one test if during the search the "rejection
  16034. limit" is adjusted dynamically so that it is either the programmed rejection
  16035. limit or the best minimum difference seen during the current matching attempt.
  16036. We can enhance the rejection process by using the delta difference technique.
  16037. As we calculate the differences of all the words to the unknown sample, we may
  16038. get two words that are very close to each other but both under the rejection
  16039. limit. The delta difference technique requires that the correct word "beat"
  16040. the other words by a certain amount. If the delta difference is set to 10, and
  16041. one template difference is 124 and the other is 129, both candidates will be
  16042. rejected. In general, the greater the delta difference between the two best
  16043. choices, the better.
  16044. Once a speaker dependent system is trained, it is tested. If a particular word
  16045. generates a large number of errors, the user may re-train on that particular
  16046. word.
  16047.  
  16048.  
  16049. Operation Considerations
  16050.  
  16051.  
  16052. To get the best results with any voice recognition circuit, remember a few
  16053. simple guidelines: speak slowly and clearly; try to repeat the words as
  16054. consistently as possible; operate it in a quiet environment; be careful when
  16055. choosing your word lists, avoiding words that sound alike; and hold the
  16056. microphone close to your lips.
  16057. The smaller the list of words, the greater the accuracy of the system. For
  16058. example, for a game of Hi/Lo, only four words are needed: higher, lower, yes,
  16059. and no. By clearing the other templates, your recognition of these should be
  16060. 100 percent.
  16061. One can also increase recognition by storing two templates (derived from
  16062. different training sessions) in the vocabulary. This will give you two chances
  16063. to match the word.
  16064.  
  16065.  
  16066. The Program
  16067.  
  16068.  
  16069. The main program, called SPEECH.C, is the simplest version of the speech
  16070. recognition algorithm. This program will work quickly with reasonable
  16071. accuracy. The code was written in Turbo C v2.0 and has been written for
  16072. clarity rather than speed or compactness.
  16073. The program is menu-driven. It has options to load and train a set of voice
  16074. (data) files. With the "Train" option you may vary the voice files
  16075. individually. The "Debug" option (normally on) will display the waveforms of
  16076. the words and indicate the elements being extracted for the templates. During
  16077. the "Perform" choice, the debug option will also display the minimum
  16078. difference table along with the delta difference. With the debug option off,
  16079. the system will just display which word is recognized.
  16080.  
  16081.  
  16082. Data Files
  16083.  
  16084.  
  16085.  
  16086. The disk includes data files for the digits zero through none and a telephone
  16087. number (with area code). These files are taken from the raw speech buffer.
  16088. These files are included so that you can experiment with voice recognition
  16089. algorithms without having the hardware. The function getvoice() on my system
  16090. will wait for a word to be spoken and place the raw speech into an array. On
  16091. your system, it will open up and read the data file and place the raw speech
  16092. into an array.
  16093. If the debugging feature is turned on, a character-based plot of the wave
  16094. forms will be generated on the screen in a vertical fashion as they are read
  16095. in. If this drawing is too crude for your tastes, you can import these files
  16096. into a spreadsheet program, such as Lotus, and plot them.
  16097. You can use the digit files to train the SR system, and then use it to
  16098. recognize the phone number.
  16099.  
  16100.  
  16101. Alternate Techniques
  16102.  
  16103.  
  16104. I include two sets of digit data, each set captured during a separate speech
  16105. session. You can use this extra data to experiment with techniques that may
  16106. increase the accuracy of the system:
  16107. Duplicate Entries -- Train the system with both sets, so you have twenty
  16108. templates. During recognition, if the template matched is greater than nine,
  16109. subtract 10 to get the "real" number.
  16110. Template Averaging -- Take two samples of each digit, average them together
  16111. and use the result as the template.
  16112. Input Averaging -- Average each point on each band to the point directly ahead
  16113. and behind. This will help to smooth the band and eliminate noise that can
  16114. creep into the system.
  16115. Amplitude Normalization -- On some system, the volume of the sound can greatly
  16116. effect the signals coming from the speech board. You can eliminate most of
  16117. this effect with normalization. Calculate the average of an entire band,
  16118. compare this to a standard amplitude factor (for example, 20), then multiply
  16119. this factor by each element of that band. This will have a sliding effect on
  16120. the band. Alternatively, you can just subtract the average from each element.
  16121. Interpolation -- When extracting the 16 samples from the raw speech buffer, I
  16122. calculated the precise position, but used the nearest element. For a more
  16123. exact representation of the wave form, a new sample should be interpolated
  16124. from the adjacent two samples.
  16125. Number of Samples -- I have used 16 normalized samples. You can vary this
  16126. number up or down and test the results. The larger values save more
  16127. information but increase the processing time. Smaller values increase
  16128. processing speed, but make for closer delta differences.
  16129.  
  16130.  
  16131. Speech Recognition With Natural Language
  16132.  
  16133.  
  16134. Many databases now have "natural" language interfaces so that you can ask
  16135. questions such as "What is the highest mountain in New Jersey?"
  16136. While many of these interfaces seem natural, most of them have a hidden
  16137. structure behind them. For example, in the Batmobile after the word "Shields",
  16138. the system will probably accept "Open", "Close" and nothing. If only the word
  16139. "Shields" is spoken, the shields will close by default.
  16140. The SR unit would have the words Shields, Open, Close, Stop, and a wide (we
  16141. assume) list of other words. To help cut down on processing time, we can
  16142. eliminate illogical words as we parse the sentence. "Shields Stop" would be
  16143. illogical. When we identify the word "Shields", we compare the next utterance
  16144. to "Open" and "Close" only. If no word is spoken, or the word is rejected, we
  16145. close the shields.
  16146. Natural language processing also helps with the "To, Too and Two" problem of
  16147. similar sounding words. If we said "I want to go too", the NL processor would
  16148. be able to sort out the correct usage based on the context in which the word
  16149. was said.
  16150.  
  16151.  
  16152. Acknowledgements
  16153.  
  16154.  
  16155. The author would like to thank Larry Eckelkamp and Nuala Murphy for their help
  16156. with this article.
  16157.  
  16158.  
  16159. Bibliography
  16160.  
  16161.  
  16162. Ainsworth, W. A., Mechanisms of Speech Recognition, Elmsford, NY: Pergamon
  16163. Press, 1976.
  16164. Carter, John P., Electronically Hearing: Computer Speech Recognition,
  16165. Indianapolis, IN: Howard W. Sams & Co., Inc., 1984.
  16166. Staugaard, Jr. Andrew C., Robotics and AI, Englewood Cliffs, NJ:
  16167. Prentice-Hall, Inc., 1987.
  16168. Figure 1 Circuit Block Diagram
  16169.  
  16170. Listing 1
  16171. /* Speech Recognizer */
  16172.  
  16173. /* bj gleason, Upsala College, Computer Science Department */
  16174. /* East Orange, nj 07019 (201)-998-1037 */
  16175.  
  16176. #include <stdio.h>
  16177. #include <conio.h>
  16178.  
  16179. #define RAWBUFFERSIZE 150 /* 100 samples/second 1.5 seconds */
  16180. #define VOCABSIZE 10 /* 10 digits, 0 - 9 */
  16181. #define NUMSAMPLES 16 /* Number of samples to extract */
  16182. #define NUMBANDS 4 /* High, Low Freq, High, Low Energy */
  16183. #define BIGNUM 32767 /* large number for min diff. calc */
  16184.  
  16185.  
  16186. int rawspeech[RAWBUFFERSIZE][NUMBANDS]; /* to hold raw speech */
  16187.  
  16188. int index[VOCABSIZE]; /* indicate if digit trained */
  16189. int template[VOCABSIZE][NUMSAMPLES][NUMBANDS]; /* known templates */
  16190. int unknown[NUMSAMPLES][NUMBANDS]; /* unknown voice template */
  16191. int min_diff[VOCABSIZE]; /* min diff. each digit */
  16192. int sam_size; /* current sample size */
  16193. int debug; /* show debugging info */
  16194.  
  16195. /* getspeech will read in a file from disk. The length in bytes */
  16196. /* will be returned. The rawspeech buffer will be modified. */
  16197. int getspeech()
  16198. {
  16199. FILE *fptr;
  16200. int i,j;
  16201. char fname[80];
  16202.  
  16203. if (debug) printf("\nReading in Speech");
  16204. printf("\nEnter name of file?");
  16205. gets(fname);
  16206. if ((fptr=fopen(fname,"rt"))==NULL)
  16207. {
  16208. printf("\nCant find file %s",fname);
  16209. return(0);
  16210. }
  16211. else
  16212. {
  16213. for(i=0;i<=RAWBUFFERSIZE;i++)
  16214. for(j=0;j < NUMBANDS;j++)
  16215. {
  16216. if ((fscanf(fptr,"%i",&rawspeech[i][j]))==EOF)
  16217. {
  16218. fclose(fptr);
  16219. return(i);
  16220. }
  16221. }
  16222. }
  16223. }
  16224.  
  16225. int plot_it()
  16226. {
  16227.  
  16228. int i,j,x,y;
  16229.  
  16230. printf("\n\n");
  16231. for (i=0;i < sam_size;i++)
  16232. {
  16233. for (j=NUMBANDS-1; j >= 0; j--)
  16234. {
  16235. x=rawspeech[i][j]+(j*2);
  16236. gotoxy(x,wherey());
  16237. putchar(j+48);
  16238. }
  16239. printf("\n");
  16240. }
  16241. }
  16242.  
  16243. /* the closest match routine compares the unknown template with */
  16244. /* known templates. It builds a minimum difference list that is */
  16245. /* the difference between unknown and each known. We then scan */
  16246. /* list to find the closest match. */
  16247.  
  16248. int closest_match()
  16249. {
  16250. int p,i,j;
  16251. int low, next_low,digit,next_digit;
  16252.  
  16253. if (debug) printf("\nFinding Closest Match");
  16254.  
  16255. for (p = 0; p < VOCABSIZE; p++)
  16256. if (index[p] != 0)
  16257. {
  16258. min_diff[p] = 0;
  16259. for(i = 0; i < NUMSAMPLES; i++)
  16260. for(j = 0; j < NUMBANDS; j++)
  16261. /* for each digit, find the absolute difference */
  16262. /* between known and unknown templates */
  16263. min_diff[p] = min_diff[p] + abs(unknown[i][j]
  16264. -template [p] [i] [j]);
  16265. }
  16266. else
  16267. {
  16268. min_diff[p]=BIGNUM; /* put in a big number if digit not */
  16269. } /* trained. */
  16270.  
  16271. /* min_diff now has the difference for each template. Search */
  16272. /* to find the smallest difference. This will be our digit. */
  16273. /* Find the next lowest match to calculate the delta diff. */
  16274. digit = -1;
  16275. next_digit = -1;
  16276. low = BIGNUM;
  16277. next_low = BIGNUM;
  16278. if (debug) printf("\nTP# Diff Low Digit");
  16279. for (p = 0; p < VOCABSIZE; p++)
  16280. {
  16281. if(min_diff[p] < low)
  16282. {
  16283. next_low = low;
  16284. next_digit = digit;
  16285. digit = p;
  16286. low = min_diff[p];
  16287. }
  16288. if (debug) printf("\n%3i %5i %5i %2i",p,min_diff[p],low,digit);
  16289. }
  16290. if (debug == 1)
  16291. {
  16292. printf("\nMinimun Difference was %i, Digit is %i",low, digit);
  16293. printf("\nNext Closest Diff was %i, Digit is %i",next_low
  16294. ,next_digit);
  16295. printf("\nWith the delta difference of %i",next_low-low);
  16296. }
  16297.  
  16298. /* it would be right here where your would add the code */
  16299. /* to set a rejection limit or a delta difference limit. */
  16300. /* If the digit is rejected, send back error, such as -1. */
  16301. return(digit);
  16302. }
  16303.  
  16304. /* Extract template will extract a template from the raw */
  16305. /* speech buffer. This is to reduce the size of the */
  16306. /* template and to elimate time warping. */
  16307.  
  16308. /* the rate is kept in floating point to prevent truncation */
  16309. /* errors. */
  16310. int extract_template()
  16311. {
  16312. int i,j,p;
  16313. float rate,x;
  16314.  
  16315. if (debug) printf("\nExtracting Template");
  16316. rate = (float) sam_size / NUMSAMPLES;
  16317. p = 0;
  16318. if (debug)
  16319. {
  16320. printf("\nExtracting %i elements from Raw Speech",
  16321. NUMSAMPLES);
  16322. printf("\nTake every %f element", rate);
  16323. printf("\n\n UN RS");
  16324. }
  16325. for (x = 0; x < sam_size ; x = x + rate)
  16326. {
  16327. for (j = 0; j < NUMBANDS; j++)
  16328. unknown[p][j] = rawspeech[(int)x][j];
  16329. if (debug) printf("\n%3i %3i",p,(int)x);
  16330. p++;
  16331. }
  16332. }
  16333. /* During the training phase, this will take the extracted template */
  16334. /* and store it in the array of known templates. */
  16335. int store_template(int position)
  16336. {
  16337. int i,j;
  16338.  
  16339. if (debug) printf("\nStoring template at position %i",position);
  16340. for (i = 0; i < NUMSAMPLES ; i++)
  16341. for (j = 0; j < NUMBANDS; j++)
  16342. {
  16343. template[position][i][j] = unknown[i][j];
  16344. }
  16345. }
  16346.  
  16347. /* Perform - Get the speech, extract an unknown template, compare */
  16348. /* against the rest, and print the resulting digit. */
  16349. int perform()
  16350. {
  16351. int digit;
  16352.  
  16353. sam_size = getspeech();
  16354. if (debug) plot_it();
  16355. if (debug) printf("\nSize of Sample = %i",sam_size);
  16356.  
  16357. extract_template(); /* break raw buffer up and */
  16358. /* place into unknown template */
  16359.  
  16360. digit = closest_match();
  16361. printf("\nDigit spoken was %i",digit);
  16362.  
  16363. }
  16364.  
  16365. /* Training - Get the speech, extract an unknown template, */
  16366. /* find from the user what digit it was, then store it in */
  16367.  
  16368. /* the known template array. */
  16369. int train()
  16370. {
  16371. char ans[10];
  16372. int digit;
  16373.  
  16374. sam_size = getspeech();
  16375. if (debug) plot_it();
  16376. if (debug) printf("\nSize of Sample = %i",sam_size);
  16377. printf("\nEnter the digit spoken ?");
  16378. gets(ans);
  16379. digit = atoi(ans);
  16380.  
  16381. index[digit] = 1; /* indicate this digit is trained */
  16382.  
  16383. extract_template(); /* break raw buffer up and */
  16384. /* place into unknown template */
  16385.  
  16386. store_template(digit); /* store the template */
  16387.  
  16388. }
  16389.  
  16390. /* Eztrain - This is to quickly load in files a0 - a9 */
  16391. int eztrain(char fname[80], int digit)
  16392. {
  16393. FILE *fptr;
  16394. int i,j;
  16395. if ((fptr=fopen[fname,"rt"))!=NULL)
  16396. {
  16397. sam_size = 0;
  16398. printf("\nReading file %s", fname);
  16399. for(i=0;i<=RAWBUFFERSIZE;i++)
  16400. for(j=0;j < NUMBANDS;j++)
  16401. if ((fscanf(fptr,"%i",&rawspeech[i][j]))!=EOF)
  16402. sam_size = i;
  16403. fclose(fptr);
  16404. if (debug) plot_it();
  16405. if (debug) printf("\nSize of Sample = %i",sam_size);
  16406. index[digit] = 1;
  16407. extract_template();
  16408. store_template(digit);
  16409. }
  16410. }
  16411.  
  16412.  
  16413. main()
  16414. {
  16415. int i;
  16416. char ans[80];
  16417. char choice;
  16418.  
  16419. /* clear the training index... nothing has been entered */
  16420. for (i=0; i<VOCABSIZE; i++)
  16421. index[i] = 0;
  16422.  
  16423.  
  16424. printf("\nWelcome to Speech Recognition Demo, Version 1.0\n");
  16425.  
  16426. debug = 1; /* display debugging information */
  16427.  
  16428. do
  16429. {
  16430. printf("\n\nTrain, Perform, Load A or B, Debug ");
  16431. if (debug) printf("Off"); else printf("On");
  16432. printf(", or Quit? (T/P/A/B/D/Q)");
  16433. gets(ans);
  16434. choice = toupper(ans[0]);
  16435. if (choice == 'A')
  16436. {
  16437. eztrain["a0",0); eztrain["a1",1); eztrain["a2",2);
  16438. eztrain["a3",3); eztrain["a4",4); eztrain["a5",5);
  16439. eztrain("a6",6); eztrain("a7",7); eztrain("a8",8);
  16440. eztrain("a9",9);
  16441. }
  16442. if (choice == 'B')
  16443. {
  16444. eztrain("b0",0); eztrain("b1",1); eztrain("b2",2);
  16445. eztrain("b3",3); eztrain("b4",4); eztrain("b5",5);
  16446. eztrain("b6",6); eztrain("b7",7); eztrain("b8",8);
  16447. eztrain("b9",9);
  16448. }
  16449. if (choice == 'D')
  16450.  
  16451. {
  16452. debug = !debug;
  16453. printf("\n Debugging Trace ");
  16454. if (debug) printf("On"); else printf("Off");
  16455. }
  16456. if (choice == 'T') train();
  16457. if (choice == 'P') perform();
  16458. }
  16459. while(choice != 'Q');
  16460. printf("\n\nAll done.");
  16461. }
  16462.  
  16463.  
  16464.  
  16465.  
  16466.  
  16467.  
  16468.  
  16469.  
  16470.  
  16471.  
  16472.  
  16473.  
  16474.  
  16475.  
  16476.  
  16477.  
  16478.  
  16479.  
  16480.  
  16481.  
  16482.  
  16483.  
  16484.  
  16485.  
  16486.  
  16487.  
  16488.  
  16489.  
  16490.  
  16491. The World Of Command Line Options
  16492.  
  16493.  
  16494. Scott Maley
  16495.  
  16496.  
  16497. Scott Maley is a member of the technical staff at The Analytic Science
  16498. Corporation (TASC). He has more than fifteen years experience in areas of
  16499. software engineering ranging from Space Shuttle flight simulation to Cobol
  16500. maintenance.
  16501.  
  16502.  
  16503. Visual, iconic or graphic interfaces reduce complexity for the user by
  16504. relegating details to another level. Complexity is neither created or
  16505. destroyed -- it only changes its appearance or location and distribution.
  16506. Thus, beneath the surface of many visual interfaces the various graphical
  16507. tools exchange a great deal of information, often by means of command line
  16508. options, where the programmer, rather than the user, must deal with the
  16509. complexity.
  16510. This article describes a command-line option-handling package that pushes some
  16511. of the command line complexity down a level, reducing the amount of complexity
  16512. with which the programmer must deal. This article is not intended to help you
  16513. decide when to use command line options. If you have decided to use them, it
  16514. may help you decide how. I have assumed that you know how arguments (which
  16515. include options) are passed to a C program. Even if that is not completely
  16516. clear, you must at least understand that a pointer is a way to refer to an
  16517. object, and not the object itself.
  16518.  
  16519.  
  16520. Overview
  16521.  
  16522.  
  16523. Options are used in two fundamentally different ways, distinguished by their
  16524. position (in)dependence. An option may indicate that something is desired for
  16525. all arguments (position independent), or for all following arguments (position
  16526. dependent). No option can be both, because we can't distinguish which is
  16527. intended unless context is expanded to more than one option at once.
  16528. The function cmd_opts presented here assumes that it must deal with only
  16529. position independent options. It associates arguments with each recognized
  16530. option (basically a sorting process) and leaves all unrecognized options,
  16531. including position dependent options, undisturbed.
  16532. Thus, the cmd_opts function handles position independent options, and the
  16533. programmer continues to handle position dependent options.
  16534. cmd_opts accepts the same number of arguments as getopt (provided with UNIX
  16535. System V). The first and second arguments are identical to those passed to
  16536. getopt. The third encapsulates expanded information in a nil terminated array
  16537. of options structures, each option isolated in a separate structure.
  16538. cmd_opts works backwards through the command line, grouping arguments for
  16539. options as it goes. By working backwards, the pointer to each option's array
  16540. of arguments ends up right where we want it, pointing to the first associated
  16541. argument; cmd_opts leaves any options it doesn't understand where it found
  16542. them (relatively speaking) and returns an error count to warn us. See Listing
  16543. 3.
  16544.  
  16545.  
  16546. The Options Structure
  16547.  
  16548.  
  16549. Listing 1 presents the elements of the structure options. The first element,
  16550. options.s, is the character that will be used on the command line for this
  16551. option (e.g. s in -s). Options.arg_flg indicates the minimum number of this
  16552. option we expect on the command line. The third element, poptv, controls how
  16553. arguments are associated with options. If poptv is set to NULL, no arguments
  16554. can be associated with the option. Otherwise cmd_opts will point poptv to the
  16555. first such argument.
  16556. Note that the ADDRESS (&) of the array of pointers (e.g. barg) must be placed
  16557. in options.poptv, so that it may be set to point to the first of any arguments
  16558. for the option. The arg_flg for each option is used to return the number of
  16559. valid instances of the option that were encountered. It may also be directly
  16560. used as a flag, since a nonzero value is considered TRUE in C.
  16561. The second (arg_flg) and third (poptv) elements of the options structure
  16562. interact to determine how an option is handled by cmd_opts. Listing 2 presents
  16563. examples of how they interact, where:
  16564.  aoptional, no associated arguments
  16565.  boptional, arguments expected
  16566.  crequired, no associated arguments (for completeness)
  16567.  drequired, arguments expected
  16568. You can gain a better understanding of the interactions by experimenting with
  16569. various combinations (compile and link tcmdopts.c with cmd_opts. c -- both
  16570. include cmd_opts.h).
  16571.  
  16572.  
  16573. Conclusions
  16574.  
  16575.  
  16576. cmd_opts is much easier to use than the widely used getopt package provided
  16577. with UNIX System V (source code freely available). While getopt accepts a list
  16578. of option switch characters and has a means of specifying which require
  16579. arguments, it has a number of shortcomings. After all the work it does to
  16580. isolate option switches and associated arguments, getopt requires you to
  16581. perform similar work. Once getopt returns with a switch character you must
  16582. determine which it was, then associate any arguments. Worse, getopt passes
  16583. some of the information via globals (e.g. optarg).
  16584. cmd_opts handles the burden of associating arguments with options for those
  16585. which are position independent. Yet, it leaves any unrecognized options where
  16586. they were, so that we may handle position dependent options. Thus, we may
  16587. typically dispense (at least for position independent options) with the switch
  16588. statement that is often used to associate arguments with options when using
  16589. the getopt function. No globals are used by cmd_opts, so possible side-effects
  16590. have been minimized, and the package is more usable in shared code libraries.
  16591. Finally, encapsulating the information associated with each option in a
  16592. structure makes cmd_opts easier to understand and use than getopt.
  16593.  
  16594. Listing 1
  16595. /* cmd_opts.h, c\include
  16596. * structure definition for command line options
  16597. */
  16598. struct options
  16599. {
  16600. char s; /* valid switch letter */
  16601. int arg_flg; /* flag to indicate an
  16602. argument is required */
  16603. char ***poptv; /* pointer to option's
  16604. value vector */
  16605. } ;
  16606.  
  16607.  
  16608. Listing 2
  16609.  
  16610. /* tcmdopts.c, c\lib\test
  16611. * Test cmd_options routine
  16612. */
  16613. #include <stdio.h>
  16614. #include "cmd_opts.h"
  16615.  
  16616. main(argc, argv) 
  16617. int argc;
  16618. char *argv[];
  16619. {
  16620. int cmd_errs, i;
  16621. static char **barg, **darg;
  16622. static struct options sw[] =
  16623. {'a',0,NULL,
  16624. 'b',0,&barg,
  16625. 'c',1,NULL, /* generally useless */
  16626. 'd',1,&darg,
  16627. 0, 0,NULL};
  16628.  
  16629. cmd_errs= cmd_options( & argc, argv, sw);
  16630. if (sw[0].arg_flg > 0)
  16631. printf("%d -a\n",sw[0].arg_flg);
  16632. for (i= 0; i < sw[1].arg_flg; i++)
  16633. printf("-b %s\n", barg[i]);
  16634. if (sw[2].arg_flg > 0)
  16635. printf("%d -c\n",sw[2].arg_flg);
  16636. for (i= 0; i < sw[3].arg_flg; i++)
  16637. printf("-d %s\n", darg[i]);
  16638. puts("Unclaimed:");
  16639. for (i= 1; i < argc; i++) /* argv[0] is still
  16640. the command */
  16641. printf(" %s",argv[i]);
  16642. puts("\n");
  16643. if (cmd_errs != 0)
  16644. {
  16645. printf("\7\ntcmdopts [-a] [-b<value>] -c -d<value> ...\n");
  16646. printf("\n%d Command line options invalid\n", -cmd_errs);
  16647. exit(1);
  16648. }
  16649. }
  16650.  
  16651. Listing 3
  16652. /* cmd_opts.c, c\lib\src, (c) 1989 Scott D. Maley
  16653. May be freely used, as long as copyright notice is preserved
  16654.  
  16655. cmd_options(argc, argv, option)
  16656.  
  16657. int *argc; -- pointer to command line arg count
  16658. char *argv[]; -- pointer to array of pointers to
  16659. command line arguments
  16660. struct options option[]; -- structure array defining valid
  16661. options
  16662.  
  16663. This is a function to process command line options (or
  16664. switches). The full set of command line arguments is passed
  16665. to the routine via argc and argv. Every option switch
  16666. encountered that is a valid match for a switch specified in
  16667. the option array is counted, removed from argv, and the
  16668. pointer to it's associated value (if any) is moved to
  16669.  
  16670. the optv array. A count of switches which are not valid
  16671. matches of any option is returned, and those switches are
  16672. left in argv.
  16673.  
  16674. A switch's value may be contiguous with it, or be separated
  16675. from it by white-space (e.g. -svalue, -s value).
  16676. White-space is commonly blanks and tabs, but may also include
  16677. commas in some C implementations. This routine doesn't care.
  16678. The C runtime initialization routine which runs before main()
  16679. is entered parses the command line into tokens (which the
  16680. elements of argv point to), based on it's definition of
  16681. white-space.
  16682.  
  16683. The structure "options" is used to define what this routine
  16684. will parse:
  16685.  
  16686. struct options
  16687. {
  16688. char s; -- The option (switch) letter
  16689. int arg_flg; -- indicates if an arg is required
  16690. char **poptv[];-- pointer to option value vector
  16691. -- NULL, if none expected
  16692. } ;
  16693.  
  16694. The third argument to this routine, option, is an array of
  16695. the options structures. The end of this array is signaled
  16696. with s == 0.
  16697.  
  16698. This routine returns:
  16699. 0 - if all switches encountered were valid options.
  16700. -n - Negative of the count of invalid (e.g. no
  16701. value followed the switch when one was expected, or
  16702. a value was contiguous with the switch, but none
  16703. was expected) switches encountered. N also includes
  16704. a count of switches that were expected, but not
  16705. encountered in argv.
  16706.  
  16707. It also sets arg_flg to indicate how many of each
  16708. switch encountered.
  16709.  
  16710. Sample use:
  16711. ----------
  16712.  
  16713. #include <stdio.h>
  16714. #include "cmd_opts.h"
  16715.  
  16716. main(argc, argv)
  16717. int argc;
  16718. char *argv [];
  16719. {
  16720. char s, *farg[] *marg[];
  16721. static struct options sw[] =
  16722. {'a', 0, NULL, -- optional, no value
  16723. 'f', 0, &farg, -- optional, w/ value
  16724. 'm', 1, &marg, -- required, w/ value
  16725. 0, 0, NULL};
  16726.  
  16727. if (cmd_options( & argc, argv, sw) < 0)
  16728. {
  16729.  
  16730. --- error, handle it here
  16731. ---
  16732. }
  16733. ---
  16734. --- continue with rest of program
  16735. ---
  16736. }
  16737.  
  16738. *-- History:
  16739. * 30 Jan 89 SDM (TASC) No need to calloc optv, we
  16740. * can work entirely within argv (plus
  16741. * a temp pointer).
  16742. * 27 Jan 89 SDM (TASC) Handle multiple instances
  16743. * of a switch.
  16744. * Retain everything not
  16745. * specified in opts in argv, and set
  16746. * argc accordingly.
  16747. * 20 Jan 89 S.D. Maley (TASC) Initial implementation.
  16748. *-- End History
  16749. */
  16750.  
  16751. #include <stdio.h>
  16752. #include "cmd_opts.h"
  16753.  
  16754. #define EOS '\0'
  16755.  
  16756. #define MoveOptFromArg(optv,argv,i,argc) \
  16757. (char *temp;\
  16758. temp= argv[i];\
  16759. RemoveArg(argv,i,argc);\
  16760. (optv)--;\
  16761. optv[0]= temp;\
  16762. }
  16763.  
  16764. #define RemoveArg(argv,i,argc) \
  16765. {int j;\
  16766. (argc)--;\
  16767. for(j=i;j<argc;j++) argv[j]=argv[j+1];\
  16768. }
  16769.  
  16770. #define SWFLG '-'
  16771. #define SwChr *(argv[i]+1)
  16772. #define SwMatch (*argv[i] == SWFLG && opts[j].s == SwChr)
  16773. #define SwValContig (*(argv[i]+2) != EOS)
  16774. #define SwValNext (i+1 < *argc && *argv[i+1] != SWFLG)
  16775.  
  16776. cmd_options(argc, argv, opts)
  16777. int *argc;
  16778. char *argv[];
  16779. struct options opts[];
  16780. {
  16781. int i,j, njth, stat;
  16782. char **optv; /* equivalent to: *optv[] */
  16783.  
  16784. optv = argv + *argc; /* work from back to front */
  16785.  
  16786. /*-- Transfer options from argv to optv
  16787. * -- and check against expectations
  16788. */
  16789.  
  16790. stat = 0;
  16791.  
  16792. for (j = 0; opts[j].s != 0; j++)
  16793. {
  16794. njth = 0;
  16795.  
  16796. for (i = *argc - 1; i > 0; i--)
  16797. { /* back to front, we build optv */
  16798. if (SwMatch)
  16799. {
  16800. if (opts[j].poptv == NULL)
  16801. { /* no arg value desired */
  16802. if (SwValContig)
  16803. continue; /* next i */
  16804. else
  16805. RemoveArg(argv,i,*argc);
  16806. } else { /* A value is desired */
  16807. if (SwValContig)
  16808. {
  16809. argv[i] += 2; /* past "-'opt_char'" */
  16810. MoveOptFromArg(optv, argv,i,*argc);
  16811. } else if (SwValNext)
  16812. { /*-- pick up value from next arg */
  16813. RemoveArg(argv,i,*argc);
  16814. MoveOptFromArg(optv, argv,i,*argc);
  16815. } else
  16816. continue; /* next i */
  16817. }
  16818. njth++; /* only count valid switches */
  16819. } /* if SwMatch */
  16820. } /* for i */
  16821.  
  16822. if (opts[j].poptv != NULL)
  16823. *opts[j].poptv= optv; /* point to option value vector */
  16824. if (opts[j].arg_flg > njth )
  16825. stat _= opts[j].arg_flg - njth ; /* not enough */
  16826. opts[j].arg_flg = njth;
  16827.  
  16828. } /* for j */
  16829.  
  16830. for (i= 1; i < *argc; i++)
  16831. if (*argv[i] == SWFLG)
  16832. stat--; /* a switch we couldn't handle */
  16833. return(stat);
  16834. }
  16835.  
  16836.  
  16837.  
  16838.  
  16839.  
  16840.  
  16841.  
  16842.  
  16843.  
  16844.  
  16845.  
  16846.  
  16847.  
  16848.  
  16849.  
  16850.  
  16851.  
  16852.  
  16853. Multitasking With Lightweight Threads
  16854.  
  16855.  
  16856. Gregory Colvin
  16857.  
  16858.  
  16859. Trained in cognitive psychology, Dr. Colvin first learned to program in 1972,
  16860. in BASIC on a PDP-8. He later had the distinction of being the first Cornell
  16861. University graduate student to purchase an Apple II with student loan money,
  16862. and has been happily hacking microcomputers ever since. He has been
  16863. programming professionally in C since 1983. He welcomes comments and queries
  16864. at 680 Hartford, Boulder, CO 80303 (303) 499-7254.
  16865.  
  16866.  
  16867. Often, and against my better judgment, I contract to create applications under
  16868. operating systems which do not support multitasking. Lightweight threads can
  16869. sometimes be used to circumvent this limitation; both Microsoft's Windows and
  16870. Apple's Multifinder use lightweight threads to retrofit multitasking
  16871. facilities to single tasking operating systems.
  16872. This article presents the ANSI C source for a multitasking kernel based on
  16873. lightweight threads. I have tried to make this kernel as small, fast, simple,
  16874. and portable as possible. I have successfully used the predecessor of this
  16875. kernel to implement a real-time graphics display system and to provide
  16876. background query processing for a database application.
  16877.  
  16878.  
  16879. Threads
  16880.  
  16881.  
  16882. A thread of computer execution consists of at least three elements: a memory
  16883. segment containing executable machine instructions, an instruction pointer
  16884. register which indicates the next instruction to execute, and a data segment
  16885. for variable allocation. Most computers also support function calls by
  16886. providing a stack memory segment; a frame pointer register, which points to
  16887. the current stack frame; and a stack pointer register, which points to the
  16888. next available space for a stack frame. A stack frame contains the arguments
  16889. and local variables for a function, and the instruction pointer and frame
  16890. pointer of the function that called it (see Figure 1).
  16891. A function call typically creates a new stack frame by pushing the function
  16892. arguments and the current instruction and frame pointer registers on the
  16893. stack, moving the current stack pointer to the frame pointer register,
  16894. decreasing the stack pointer enough to leave room for local variables, and
  16895. moving the address of the called function to the instruction pointer. Thus
  16896. nested function calls result in a linked list of stack frames on the stack
  16897. segment, which is traversed as functions return.
  16898.  
  16899.  
  16900. Context Switching
  16901.  
  16902.  
  16903. A multitasking operating system can execute several threads "simultaneously"
  16904. on one machine. Since most machines can only execute a single instruction at a
  16905. time, only one thread is really executing at any one time, but multiple
  16906. threads appear to run simultaneously because the O.S. performs context
  16907. switches between threads at frequent intervals. At each context switch, the
  16908. O.S. saves the contents of the machine registers for the current thread and
  16909. restores the state of the registers for another thread. Each thread has its
  16910. own code, data, and stack segments, so that threads cannot ordinarily
  16911. interfere with one another.
  16912. C, unlike ADA or Modula 2, is single threaded, so that an executing C program
  16913. has one instruction pointer and one set of memory segments. However, the ANSI
  16914. Standard C library does provide a pair of functions, setjmp() and longjmp(),
  16915. for saving and restoring the contents of the machine resisters. I have used
  16916. (some might say abused) these functions to implement multiple threads within a
  16917. single C program.
  16918. The setjmp() and longjmp() calls are unusual, in that the longjmp() call, when
  16919. successfull, never returns to the function that calls it, whereas the setjmp()
  16920. call can return to the same function any number of times. The first call to
  16921. setjmp(jmp_buf) saves the current state of the machine registers in the
  16922. jmp_buf structure and returns 0. A subsequent call to longjmp(jmp_buf,int)
  16923. restores the saved registers, causing the nonzero int specified in the calling
  16924. argument to be returned by setjmp(jmp_buf) to the function that called it.
  16925. Usually longjmp() is used to abort from errors in deeply nested functions
  16926. without actually returning from all the functions. An error handler is
  16927. installed with a C statement like:
  16928. if (error=setjmp(buf))
  16929. e(error);
  16930. else
  16931. f();
  16932. The call to setjmp() returns false, so that function f() is called. Errors in
  16933. f() or any functions called within f() can then be handled by calling longjmp
  16934. (buf,error), which causes setjmp() to return error, so that e(error) is
  16935. called. To use these functions to support switching among multiple threads
  16936. requires several jmp_buf structures -- at least one for each thread of
  16937. execution.
  16938.  
  16939.  
  16940. Implementation
  16941.  
  16942.  
  16943. To implement lightweight threads, the single thread of execution of a normal C
  16944. program must be divided into independent tasks. Since a C program begins its
  16945. life with a single stack segment, this segment must be divided into pieces,
  16946. one for each thread. The ThInit(int n,int size) function does this by first
  16947. calling setjmp() to mark an entry point for a thread, then calling itself
  16948. until enough room for a thread (size bytes) has been used on the stock. It
  16949. repeats this recursive process until the desired number of threads (n) has
  16950. been created. The saved machine registers for each thread are kept in Threads,
  16951. a global array of thread structures, one structure for each thread. Having
  16952. thus divided up the stack, new threads can be created by ThNew(void (*root)
  16953. (int)), which simply saves the address of the root() function for the new
  16954. thread in Root, then does a longjmp() to restore the registers set by
  16955. ThInit(). The reactivated ThInit() calls (*Root) (ThCurr), and the new thread
  16956. is underway. ThNew() returns the ID of the new thread, which is a non-zero
  16957. index into the Threads table.
  16958. The threads created in this way are lightweight in two senses. First, they
  16959. share the same code and data segments, and are thus not protected from each
  16960. other by the operating system's memory management. Second, they are are
  16961. treated as one process by the operating system, and thus are not automatically
  16962. switched. Thus, the ThJump(int ID) function is needed so that a thread can
  16963. cause a context switch to another thread, specified by ID. ThJump calls
  16964. setjmp() to save the state of the current thread, then calls longjmp() to
  16965. restore the saved state of the destination thread. The ThJump() function does
  16966. not return until another thread jumps back to it, in which case it returns the
  16967. thread ID of the jumper. Figure 2 shows a picture of the stack and the global
  16968. Threads array while running two threads. Thread 1 is the initial thread (that
  16969. is, the thread that called ThInit()), and Thread 2 is the currently running
  16970. thread, which has made several function calls since it was jumped to by Thread
  16971. 1.
  16972.  
  16973.  
  16974. Communication And Deadlock
  16975.  
  16976.  
  16977. Since lightweight threads share the same data segment they can communicate
  16978. easily through global variables and shared memory buffers. On this basis, you
  16979. may implement semaphores, pipes, messages, or any other communication
  16980. discipline. Whatever communication method you choose, you must "beware the
  16981. Jabberwock" of deadlock.
  16982. For instance, a simple message passing scheme (not a very efficient one) can
  16983. be implemented by placing the address of a message into an array of pointers,
  16984. one for each thread, initialized to zero:
  16985. Message = (char **) calloc(N_Threads,
  16986. sizeof(char *));
  16987. and then jumping to the message destination:
  16988. void msg_send(char *message,int
  16989. destination)
  16990. {
  16991. int id = ThId();
  16992. Message[id] = message;
  16993. do
  16994. ThJump (destination);
  16995. while (Message[id]);
  16996.  
  16997. }
  16998. The ThId() macro is used to get the ID of the current thread. The sending
  16999. thread waits in a loop until the message is received. The destination thread
  17000. can then receive a message with:
  17001. char *msg_recv ()
  17002. {
  17003. int id;
  17004. char *m;
  17005. do
  17006. id = ThJump(ThNext());
  17007. while (!Message[id]);
  17008. m -- Message[id];
  17009. Message[id] = 0;
  17010. return m;
  17011. }
  17012. If no thread ever sends a message then the receiving thread will never leave
  17013. the loop, a condition called starvation. This may not be a problem, since if
  17014. no message is sent there may be nothing for the waiting thread to do. However,
  17015. consider the following code, which waits for a message from a particular
  17016. thread:
  17017. char *msg_wait(int godot)
  17018. {
  17019. int id;
  17020. char *m;
  17021. while (!Message[godot])
  17022. ThJump(godot);
  17023. m = Message[godot];
  17024. Message[godot] = O;
  17025. return m;
  17026. }
  17027. If Thread 1 is in the loop, waiting for a message from Thread 2, and Thread 2
  17028. is also in the loop, waiting for a message from Thread 1, then neither thread
  17029. will ever get out of the loop, and no other threads will get to run. This is a
  17030. deadlock.
  17031.  
  17032.  
  17033. Preventing Deadlock
  17034.  
  17035.  
  17036. In general, deadlock can occur whenever threads must block to wait for
  17037. exclusive use of a particular resource (memory buffer,screen, keyboard,disk
  17038. controller, printer, etc.) that is in use by another thread. In the examples
  17039. above I have implemented blocking by busy waiting in a loop. It would be
  17040. better to add a flag to the thread structure, so that blocking could be
  17041. handled by the kernel. It would then be possible to centralize deadlock
  17042. control within the kernel.
  17043. The easiest way to prevent deadlock is simply not to share resources. For
  17044. instance, one thread might handle all file IO, another all printer output.
  17045. Another easy solution is to arrange resources and threads in a hierarchy, so
  17046. that there is never a conflict between threads. For instance, one thread might
  17047. buffer keyboard input, while a second thread reads the keyboard buffer,
  17048. performs computations, and buffers window output, and a third thread reads the
  17049. window buffers and displays them on the screen. Other cases are harder, and
  17050. the solutions tend to be task specific.
  17051. For example, database programs can prevent deadlock, and ensure data
  17052. integrity, with the concept of a transaction. A thread needing resources, such
  17053. as write access to a set of data records, sets out to acquire them, one by
  17054. one. If all the needed resources are acquired, the transaction succeeds and
  17055. releases its resources for the next transaction. If any resource cannot be
  17056. acquired, the transaction aborts, releasing all its resources. The thread then
  17057. waits for a while, and tries again. Thus, no thread is ever holding a resource
  17058. while waiting for another resource.
  17059. For other examples on preventing deadlock, be sure to check the relevant
  17060. literature for the tasks you are implementing. A good general discussion, with
  17061. C source code and further references, can be found in Andrew Tanenbaum's
  17062. Operating Systems: Design and Implementation (Prentice Ha11,1987). If you fail
  17063. to ensure that your application is deadlock free you can look forward to
  17064. mysterious system hangs and other evidence of Murphy's Law.
  17065.  
  17066.  
  17067. Caveats
  17068.  
  17069.  
  17070. I have tested the code presented here with Microsoft C 5.0 on my 386 clone and
  17071. with MPW C 3.0 on my Macintosh SE. On my clone the kernel compiles to under
  17072. one K of code, and executes over 80,000 jumps per second. (Ed: The code is
  17073. available from the CUG Library; see New Releases.) Be sure to design and test
  17074. very carefully if you implement a similar kernel. Although the setjmp() and
  17075. longjmp() functions are portable, this implementation depends on non-portable
  17076. details of stack implementation. Be especially careful not to overrun the
  17077. stack areas set up for each thread. I have provided a simple ThProbe() macro
  17078. that exits with a message if an overrun is detected, but I have succeeded in
  17079. crashing threads with printf() between calls to ThProbe(). For a more powerful
  17080. approach to ensuring stack integrity, see the article "A Stack Checking
  17081. Function" by Eric White in The C Users Journal (Volume 7, Number 3, April
  17082. 1989). If you exercise proper care, you will find the concept of lightweight
  17083. threads to be a useful addition to your tool kit.
  17084. Figure 1
  17085. Figure 2
  17086.  
  17087. Listing 1
  17088. /*********** THREAD.C COPYRIGHT 1989 GREGORY COLVIN ************
  17089. This program may be distributed free with this copyright notice.
  17090. ***************************************************************/
  17091. #include "thread.h"
  17092.  
  17093. thread *Threads; /* table of threads */
  17094. int ThCurr=1; /* current executing thread */
  17095. static int N_Threads; /* number of threads in table */
  17096. static int Free=2; /* first free thread */
  17097. static int Next=1; /* next runnable thread */
  17098. static char *Stack; /* bottom of stack for init */
  17099. static void (*Root)(void); /* for temporary use in exec */
  17100.  
  17101. thread *ThInit(int n,int size) /* create n size byte threads */
  17102. { int i;
  17103.  
  17104. if (!N_Threads) { /* if just entered */
  17105. if (n < 2)
  17106. return 0; /* error, n too small */
  17107. Threads= /* create table */
  17108. (thread *)calloc(n,sizeof(thread));
  17109. if (!Threads)
  17110. return Threads; /* error, bad calloc */
  17111. Threads--; /* will index from 1 */
  17112. N_Threads= n, n= 1;
  17113. if (setjmp(Threads[1].exit)) /* set exit point */
  17114. exit(0); /* exit init thread */
  17115. } else if (!Stack) { /* start new thread */
  17116. Stack= (char *)&size; /* at top of stack */
  17117. if (setjmp(Threads[n].exec)) { /* set entry point */
  17118. if (!setjmp(Threads[ThCurr].exit))/* set exit point */
  17119. (*Root)(); /* call root function */
  17120. Threads[ThCurr].free= Free; /* come here on exit */
  17121.  
  17122. Free= ThCurr; /* put on free list */
  17123. Next= Threads[Free].next; /* take off run list */
  17124. for (i=1; i <= N_Threads; i++) { /* clean up table */
  17125. if (Threads[i].parent == Free) /* if abandoned child */
  17126. Threads[i].parent = 1; /* adopt by init */
  17127. if (Threads[i].next == Free) /* patch run list */
  17128. Threads[i].next= Threads[Free].next;
  17129. }
  17130. ThCurr= Threads[Free].parent; /* will jump to parent */
  17131. Threads[Free].parent= 0; /* Free is parentless */
  17132. longjmp(Threads[ThCurr].jump,Free);
  17133. }
  17134. }
  17135. if (Stack - (char *)&size < size) /* if not enough stack */
  17136. ThInit(n,size); /* push more stack */
  17137. else { /* done with a thread */
  17138. Threads[n].stack =(char *)&size; /* save top of stack */
  17139. Stack= 0 ; /* start new thread */
  17140. if (n < N_Threads) { /* if not done */
  17141. Threads[n].free= n + 1; /* link to free list */
  17142. ThInit(++n,size); /* push more stack */
  17143. } else
  17144. Threads[n].free = 0; /* at end of free list */
  17145.  
  17146. }
  17147. return Threads; /* done: return table */
  17148. }
  17149. void ThFree() /* free the thread table */
  17150. { free (Threads+1); /* goodbye */
  17151. Threads= 0, N_Threads= 0; /* can init again */
  17152. }
  17153.  
  17154. int ThNew(void (*root)(void)) /* fork and exec new thread */
  17155. { int parent, fork;
  17156. ThProbe(); /* stack probe */
  17157. fork= Free; /* fork to free thread*/
  17158. if (fork == 0)
  17159. return -1; /* error, none free */
  17160. Free= Threads[fork].free; /* take off free list */
  17161. parent= ThCurr; /* current is parent */
  17162. Threads[fork].parent= parent; /* set parent */
  17163.  
  17164. ThCurr= fork; /* will run fork next */
  17165. if (!Threads[Next].next) /* link to run list */
  17166. Threads[ThCurr].next= Next; /* make circular list */
  17167. else
  17168. Threads[ThCurr].next= Threads[Next].next;
  17169. Threads[Next].next= ThCurr;
  17170. Next= ThCurr; /* next on run list */
  17171. if (setjmp(Threads[parent].jump)) /* put parent to sleep */
  17172. return fork; /* parent is awake */
  17173. Root= root; /* who to call */
  17174. longjmp(Threads[ThCurr].exec,fork); /* call root from init */
  17175. }
  17176.  
  17177. void ThExit(void) /* exit to parent */
  17178. { ThProbe(); /* stack probe */
  17179. longjmp(Threads[ThCurr].exit,ThCurr);
  17180. }
  17181.  
  17182. int ThJump(int id) /* jump to another thread */
  17183. { int jumper, caller;
  17184. ThProbe(); /* stack probe */
  17185. if (id == 0 ) /* if no destination */
  17186. id= Threads[ThCurr].next; /* next on run list */
  17187.  
  17188. if (id < 1 id > N_Threads Threads[id].parent < 0)
  17189. return -1; /* error, bad id */
  17190. caller= ThCurr; /* where we came from */
  17191. if (id == caller)
  17192. return ThCurr; /* nowhere to go */
  17193. ThCurr= id; /* where we are going */
  17194. if (jumper=setjmp(Threads[caller].jump))
  17195. return jumper; /* return who jumped */
  17196. longjmp(Threads[id].jump,caller); /* jump to ThCurr */
  17197. }
  17198.  
  17199. static void test()
  17200. {
  17201. ThProbe();
  17202.  
  17203. printf("test: called from thread %d\n",ThId());
  17204. ThJump(0);
  17205. printf("test: falling off thread %d\n",ThId());
  17206. }
  17207.  
  17208. main()
  17209. { int i;
  17210.  
  17211. ThInit(3,2048);
  17212. for (i=1; i <= 9; i++) {
  17213. printf("main: loop %d\n",i);
  17214. printf("main: created new thread %d\n",ThNew(test));
  17215. printf("main: created new thread %d\n",ThNew(test));
  17216. printf("main: exited from thread %d\n",ThJump(0));
  17217. printf("main: exited from thread %d\n",ThJump(0));
  17218. }
  17219. }
  17220.  
  17221.  
  17222. Listing 2
  17223.  
  17224. /*********** THREAD.H COPYRIGHT 1989 GREGORY COLVIN ************
  17225. This program may be distributed free with this copyright notice.
  17226. ***************************************************************/
  17227. #ifndef THREAD
  17228. #define THREAD
  17229.  
  17230. #include <assert.h>
  17231. #include <setjmp.h>
  17232. #include <stdio.h>
  17233.  
  17234. typedef struct {
  17235. jmp_buf exec; /* state of thread for exec */
  17236. jmp_buf jump; /* state of thread for jump */
  17237. jmp_buf exit; /* state of thread for exit */
  17238. int parent; /* id of parent thread */
  17239. int nchildren; /* number of children */
  17240. int free; /* id of next free thread */
  17241. int next; /* id of next thread to run */
  17242. char *stack; /* top of stack for thread */
  17243. } thread;
  17244.  
  17245. extern thread *Threads; /* table of threads */
  17246. extern int ThCurr; /* current thread */
  17247.  
  17248. #define ThProbe() { char p; assert(Threads[ThCurr].stack < &p);}
  17249.  
  17250. #define ThId() ThCurr
  17251.  
  17252. thread *ThInit(int n,int size); /* create n size byte threads */
  17253. void ThFree(void); /* free the thread table */
  17254. int ThNew(void (*root)(void)); /* fork and exec new thread */
  17255. int ThJump(int id); /* jump to another thread */
  17256. void ThExit(void); /* exit to parent */
  17257.  
  17258. #endif
  17259.  
  17260.  
  17261.  
  17262.  
  17263.  
  17264.  
  17265.  
  17266.  
  17267.  
  17268.  
  17269.  
  17270.  
  17271.  
  17272.  
  17273.  
  17274.  
  17275.  
  17276.  
  17277.  
  17278.  
  17279.  
  17280.  
  17281.  
  17282.  
  17283.  
  17284.  
  17285.  
  17286.  
  17287. Writing Your Own Standard Headers: <stdlib.h>, <stddef.h>, <stdarg.h> And
  17288. <limits.h>
  17289.  
  17290.  
  17291. Dan Saks
  17292.  
  17293.  
  17294. Dan Saks is the owner of Saks & Associates, which offers training and
  17295. consulting in C and C++. He is a member of X3J11, the ANSI C committee. He has
  17296. an M.S.E. in computer science from the University of Pennsylvania. You can
  17297. write to him at 287 W. McCreight Ave., Springfield, OH 45504 or call (513)
  17298. 324-3601.
  17299.  
  17300.  
  17301. In "Writing Your Own Standard Headers: The String Functions" (The C Users
  17302. Journal, Jan. 1990), I presented some basic rules for creating standard
  17303. headers, and then I showed you how to apply those rules to create <string.h>.
  17304. This article shows how to write five other headers you most likely need. But
  17305. first, here is a non-standard header that simplifies writing the standard ones
  17306. and eliminates some irritating portability problems.
  17307.  
  17308.  
  17309. <quirks.h>
  17310.  
  17311.  
  17312. The standard headers frequently use void and void * types. void indicates that
  17313. a function returns no value, as in
  17314. void exit(int);
  17315. or to indicate that a function accepts no arguments, as in
  17316. int rand(void);
  17317. void * is the "generic data pointer" type used in declarations like
  17318. void *malloc(size_t);
  17319. void free(void *);
  17320. Many old compilers don't recognize void as a keyword. For these compilers
  17321. 'void' functions are written without a return type in the function declaration
  17322. (it defaults to int), and char * is used instead of void * for generic
  17323. pointers. You can express your intent more clearly if you define
  17324. typedef int void;
  17325. typedef char *void_star;
  17326. These let you write declarations like
  17327. void_star malloc();
  17328. void free();
  17329. which look more like Standard C.
  17330. If your compiler generates code so that functions return ints the same way
  17331. they return chars, then you can safely define
  17332. typedef char void;
  17333. and write declarations like
  17334. void *malloc();
  17335. void free();
  17336. which looks even more like Standard C.
  17337. Some compilers, like cc on UNIX 4.2 BSD, implement void as a keyword, but
  17338. don't allow void * as a type. On these systems, you need only define
  17339. void_star.
  17340. After putting your definitions for void or void_star in a header called
  17341. <quirks.h>, you should include it at the beginning of every standard header.
  17342. These types will then almost appear to be built-in. You will need to include
  17343. <quirks.h> explicitly only in source files that use none of the standard
  17344. headers.
  17345. quirks.h can smooth out other differences in dialects. For example, if your
  17346. compiler doesn't implement the const and volatile keywords, you can add
  17347. #define const
  17348. #define volatile
  17349. Listing 1 shows a version of <quirks.h> for DECUS C. The protective wrapper
  17350. prevents repeated definitions of void.
  17351.  
  17352.  
  17353. <stdlib.h>
  17354.  
  17355.  
  17356. Like <string.h>, <stdlib.h> was invented by the ANSI standard. It declares the
  17357. general utility functions in the standard library, summarized in Table 1.
  17358. EXIT_SUCCESS and EXIT_FAILURE are codes used with the exit function to
  17359. indicate a program's success or failure to the host environment. They expand
  17360. to integral expressions that need not be constants. (An integral type is any
  17361. of the signed or unsigned forms of char, short int, int or long int, or any
  17362. enumerated type.) On MS-DOS and UNIX, the codes are usually defined by
  17363. #define EXIT_SUCCESS 0
  17364. #define EXIT_FAILURE 1
  17365. Some systems, such as RT-11, define multiple levels of failure, such as
  17366. warning, error, severe error, etc., one of which you must pick for
  17367. EXIT_FAILURE. You can define additional codes like EXIT_WARNING, but they will
  17368. clearly be non-portable.
  17369. MB_CUR_MAX expands to a positive integer expression whose value is the maximum
  17370. number of bytes in a multibyte character as determined by the currect locale.
  17371. This is meaningful only if you already have multibyte character support, in
  17372. which case MB_CUR_MAX is already in your header. I just set it to 1.
  17373. RAND_MAX is the maximum value that can be returned by the rand function. It
  17374. must be integral and constant. The return type of rand is int, so RAND_MAX is
  17375. typically the value of the largest positive signed integer. The Standard
  17376. stipulates that RAND_MAX must be at least 32767, but if your rand operates
  17377. over a smaller range, use the smaller value until you rewrite the function.
  17378. div_t and ldiv_t are structure types returned by the div and ldiv functions,
  17379. respectively. You can define them as
  17380. typedef struct {int quot, rem} div_t;
  17381. typedef struct {long quot, rem} ldiv_t;
  17382. where quot and rem may be in either order.
  17383.  
  17384. wchar_t is the wide character type, an integral type whose range of values can
  17385. represent distinct codes for all members of the largest extended character set
  17386. specified among the supported locales. Like MB_CUR_MAX, this symbol relates to
  17387. multibyte and wide character support. If you don't have it, use
  17388. typedef char wchar_t;
  17389. Listing 2 shows my <stdlib.h> for UNIX 4.2 BSD. Notice that the definition of
  17390. NULL uses void_star from <quirks.h>. A protective wrapper surrounds wchar_t
  17391. because it appears in other headers in <stdlib.h>. No wrapper protects div_t
  17392. and ldiv_t because they appear only in <stdlib.h> and the protective wrapper
  17393. around the entire header prevents them from being redefined.
  17394. The abs and labs macros are just interim implementations, because the ANSI
  17395. standard requires that, unless explicitly exempted, all library functions must
  17396. be implemented as functions (so they are addressable). Functions declared in
  17397. headers may also be implemented as a macro, provided that the macro is "safe"
  17398. (i.e., it expands to code that evaluates each of its arguments only once), but
  17399. abs and labs aren't safe. When abs is a macro,
  17400. abs(*p++)
  17401. will evaluate *p++ twice, producing both unpredictable results and unwanted
  17402. side effects.
  17403.  
  17404.  
  17405. <stddef.h>
  17406.  
  17407.  
  17408. <stddef.h> contains some commonly used definitions, three of which -- NULL,
  17409. size_t and wchar_t -- are also in <stdlib.h>. <stddef.h> also introduces a new
  17410. type, ptrdiff_t, and a new macro, offsetof. My DECUS C implementation appears
  17411. as Listing 3.
  17412. ptrdiff_t is the type of the result of pointer subtraction. It is a signed
  17413. integral type either int or long int. It doesn't need a protective wrapper
  17414. because it isn't defined anywhere else.
  17415. The macro call offsetof(t, m) returns the offset (in bytes) of member m within
  17416. structure type t. offsetof expands to a constant expression of type size_t. m
  17417. cannot be a bit-field. In the rationale for the ANSI committee suggests three
  17418. possible definitions for offsetof:
  17419. #define offsetof(t, m) \
  17420. ((size_t)&(((t *)0)->m))
  17421. or
  17422. #define offsetof(t, m) \
  17423. ((size_t) (char *)&(((t *)O)->m))
  17424. or
  17425. #define offsetof(t, m) \
  17426. ((size_t)((char *)&(((t *)X)->m) - (char *)&(x))
  17427. where X is some predeclared address. None of these definitions is guaranteed
  17428. to be portable, but so far, the first one has worked on every system I've
  17429. tried.
  17430.  
  17431.  
  17432. <stdarg.h>
  17433.  
  17434.  
  17435. This header defines a type, va_list, and three macros, va_start, va_arg, and
  17436. va_end, which access the arguments to a function with a variable length
  17437. argument list (like printf or scanf). <stdarg.h> is very similar to
  17438. <varargs.h> found with many UNIX C compilers.
  17439. Listing 4 shows a simple function, concat, that uses <stdarg.h>. The function
  17440. heading is a prototype whose parameter list ends with an ellipsis (, ...),
  17441. indicating that the length of the list is variable. va_list is the type of a
  17442. data object that tracks the current position in the argument list. va_start
  17443. initializes ap so that the first call to va_arg returns the value of the first
  17444. argument in the list's variable part. Subsequent calls to va_arg return the
  17445. values of the succeeding arguments. You must supply the argument's type to
  17446. each call to va_arg since arguments in a variable length list may be of
  17447. different types. (Bear in mind that the type of an argument in a variable
  17448. length parameter list will be promoted so that it will not be an integer type
  17449. smaller than int, nor will it be float.) va_end does any cleanup that might be
  17450. needed.
  17451. The implementation of <stdarg.h> depends on the compiler'sparameter-passing
  17452. conventions. Most compilers pass arguments by pushing them onto the run-time
  17453. stack. The rationale for the ANSI standard states that <stdarg.h> was designed
  17454. to accommodate newer machines that may pass arguments in machine registers.
  17455. Having no experience with C compilers for these machines, I will stick to the
  17456. more common stack-oriented methods.
  17457. Most MS-DOS compilers push arguments so that the first argument has the lowest
  17458. address. Figure 1 shows the argument list format for a call to
  17459. printf("%d %f %d\n", i, x, n)
  17460. using a typical MS-DOS C compiler (where i and n are 16-bit ints and x is a
  17461. 64-bit double). SP represents the value of the stack pointer. The figure shows
  17462. the state of the stack just before jumping to printf.
  17463. Listing 5 presents an implementation of <stdarg.h> for moat MS-DOS C
  17464. compilers. va_start(ap, p) initializes ap to
  17465. (va_list)(&(p) + 1)
  17466. which is the address of the first parameter in the list's variable part (the
  17467. parameter after p). Some implementations write this expression as
  17468. (va_list)&(p) + sizeof(p)
  17469. which is equivalent as long as va_list is char *. va_start should expand to a
  17470. void expression, but many compilers erroneously omit the void cast.
  17471. va_arg(ap, t) returns the value of the current argument addressed by ap (cast
  17472. to type t), and advances ap to point to the next argument. On many compilers,
  17473. you can implement va_arg as
  17474. #define va_arg(ap, t) (*((t *)(ap))++)
  17475. This auto-increment expression may be a little easier to understand than the
  17476. one in Listing 5, but it relies on an extension to the C Standard. The
  17477. standard states that a cast expression, such as
  17478. (t *) (ap)
  17479. is not an lvalue, so it cannot be the operand of ++. The version of va_arg in
  17480. Listing 5 increments ap before applying the cast, then subscripts backwards to
  17481. obtain the argument originally referenced by ap. It's more obscure, but stays
  17482. within the standard.
  17483. If your compiler lets you use the auto-increment expression, is there any
  17484. reason not to? Yes. Consider Microsoft C v5.1. By default, the compiler lets
  17485. you use various language extensions. You can implement va_arg as an
  17486. auto-increment expression, but compiling your code with the /Za option
  17487. (disable language extensions) produces a warning from the compiler. Microsoft
  17488. implements va_arg as in Listing 5 so that it will work with every compiler
  17489. option.
  17490. On the other hand, Zortech C v1.07 also uses the auto-increment version of
  17491. va_arg. However, if you compile code using va_arg with the -A option (enforce
  17492. ANSI compatibility), you don't get a warning. This means the compiler can't
  17493. warn you about using this language extension in you code.
  17494. In most implementations va_end does nothing, but the standard states that it
  17495. should expand to a void expression. If your compiler complains that ((void)0)
  17496. is a useless expression, you can try using
  17497. #define va_end(ap) ((void)((ap) = 0))
  17498. If generating unnecessary code bothers you, you can
  17499. #define va_end(ap)
  17500. which works fine when va_end is called in a separate statement (as in Listing
  17501. 4), but produces a syntax error when va_end is embedded in nasty (but legal)
  17502. expressions like
  17503. va_end(ap), n = 1;
  17504. If your compiler pushes the arguments so that the first one is at the highest
  17505. address, then you should use an implementation of <stdarg.h> like the one in
  17506. Listing 6. It differs from Listing 5 in two ways:
  17507.  va_start initializes ap to point to (instead of beyond) the last fixed
  17508. argument, and
  17509.  va_arg uses a pre-decrement (instead of a post-increment) to step to the next
  17510. argument.
  17511.  
  17512.  
  17513. <limits.h>
  17514.  
  17515.  
  17516.  
  17517. This header contains macros that define limits for the sizes and ranges of
  17518. integral types. Table 2 lists the macro names and their meanings. The standard
  17519. specifies a minimum magnitude (absolute value) for each limit. The version of
  17520. <limits.h> in Listing 7 uses these minimums. All implementation may (may
  17521. because it's permitted by the standard!) increase the magnitude of the limits,
  17522. but any program that relies on extended limits will not be portable to all
  17523. implementations.
  17524. For example, SHRT_MIN and SHRT_MAX define the range of values for type short
  17525. int. The standard requires the range to be at least -32767 to +32767 (decimal)
  17526. -- the set of values that can be represented using 16-bit ones-complement or
  17527. sign-magnitude arithmetic. On a two-complement machine, you can increase the
  17528. magnitude of SHRT_MIN to -32768, but any program that stores -32768 in a short
  17529. int might not work on other architectures.
  17530. The standard allows the range of int to be as small as the range of short int.
  17531. Hence, the minimum magnitudes for INT_MIN and INT_MAX are the same as for
  17532. SHRT_MIN and SHRT_MAX, respectively. At the opposite extreme INT_MIN and
  17533. INT_MAX could be as large as LONG_MIN and LONG_MAX, respectively.
  17534. I recommend that you write your <limits.h> to use the actual ranges supported
  17535. by your compiler. This lets you take full advantage of your architecture when
  17536. efficiency is more important than portability. When portability is important,
  17537. you must remember to avoid depending on the larger limits.
  17538. CHAR_MIN and CHAR_MAX define the range of values for "plain" char. A compiler
  17539. can choose to represent plain char as either signed char or unsigned char. If
  17540. your compiler treats plain char as signed, then use
  17541. #define CHAR_MAX SCHAR_MAX
  17542. #define CHAR_MIN SCHAR_MIN
  17543. Otherwise, use
  17544. #define CHAR_MAX UCHAR_MAX
  17545. #define CHAR_MIN 0
  17546. Some compilers let you select the representation of "plain" char. For example,
  17547. Microsoft C v5.1 normally treats char as signed, but the /J option changes it
  17548. to unsigned. This option also defines the macro _CHAR_UNSIGNED to allow
  17549. conditional compilation (as in Listing 7) to determine the appropriate
  17550. settings for CHAR_MIN and CHAR_MAX.
  17551. Borland's Turbo C v2.0 provides a switch for selecting the the representation
  17552. of "plain" char, but doesn't define a macro like _CHAR_UNSIGNED. In place of
  17553. #ifndef_CHAR_UNSIGNED
  17554. it uses
  17555. #if (((int)((char)0x80)) < 0)
  17556. According to the standard, #if expressions cannot use type casts or the sizeof
  17557. operator. Therefore, this technique can be used only on a compiler that
  17558. supports this language extension. It also means the compiler won't warn you
  17559. about using this feature even when you ask it to disable language extensions.
  17560. The standard states that every macro, except CHAR_BIT and MB_LEN_MAX is
  17561. defined as an expression that has "the same type as would an expression that
  17562. is an object of the corresponding type converted according to the integral
  17563. promotions." For example, INT_MAX is defined as an expression of type int, and
  17564. UINT_MAX is an expression of type unsigned int. On the other hand, the
  17565. character range limits (such as UCHAR_MAX) are defined as int expressions,
  17566. rather than as (signed or unsigned) char expressions, because character types
  17567. are promoted to int when used in an expression.
  17568. Notice that the unsigned limits are defined as unsigned constants. For
  17569. example, UINT_MAX is defined by
  17570. #define UINT_MAX 65535u
  17571. in Listing 7. The u suffix on the constant makes it unsigned. Without the u, a
  17572. decimal constant is either a signed int or a signed long int, depending on the
  17573. compiler. For example, DECUS C treats 65535 as (-1), but Microsoft C treats it
  17574. as 65535L (a long int).
  17575. If your compiler doesn't support the u suffix, you can try to write unsigned
  17576. int constants in octal or hex. For instance, some compilers with 16-bit ints
  17577. treat 0100000 through 0177777 and 0x8000 through 0xFFFF as unsigned int
  17578. constants. If that doesn't work, you can try
  17579. #define UINT_MAX
  17580. ((unsigned)65535)
  17581. which might introduce another problem. Limits like UINT_MAX are supposed to be
  17582. usable in #if expressions; however, this definition uses a cast, which
  17583. (according to the standard) isn't usable. Even if your preprocessor won't
  17584. accept casts in #if expressions, you might still find this definition useful
  17585. in other contexts.
  17586. A similar problem occurs when you try to set INT_MIN to -32768 on some
  17587. two-complement machines (such as a PC) using 16-bit ints. In Microsoft C,
  17588. 32768 is greater than INT_MAX, so it's a long int. Therefore, the definition
  17589. #define INT_MIN (-32768)
  17590. is wrong because it makes INT_MIN a long int. On the other hand,
  17591. #define INT_MIN (-32767-1)
  17592. only uses constants of type int, and so correctly defines INT_MIN as an int.
  17593.  
  17594.  
  17595. What's Been Gained?
  17596.  
  17597.  
  17598. I have shown how to write five standard headers: <string.h>, <stdlib.h>,
  17599. <stddef.h>, <stdarg.h>, and <limits.h>. I have also presented <quirks.h>,
  17600. which fakes a few new keywords that are missing from older compilers. With
  17601. just these few headers, it's much easier to port Standard C code to older
  17602. compilers.
  17603. Figure 1
  17604. Table 1
  17605. Summary of <stdlib.h>
  17606.  
  17607. Macros:
  17608.  
  17609. EXIT_FAILURE, EXIT_SUCCESS, MB_CUR_MAX, RAND_MAX, NULL
  17610.  
  17611. Types:
  17612.  
  17613. div_t, ldiv_t, size_t, wchar_t
  17614.  
  17615. Function Prototypes:
  17616.  
  17617. void abort(void);
  17618. int abs(int);
  17619. int atexit(void (*)(void));
  17620. double atof(const char *);
  17621. int atoi(const char *);
  17622. long atol(const char *);
  17623. void *bsearch
  17624.  (
  17625.  const void *, const void *, size_t, size_t,
  17626.  
  17627.  int (*)(const void *, const void *)
  17628.  );
  17629. void *calloc(size_t, size_t);
  17630. div_t div(int, int);
  17631. void exit(int);
  17632. void free(void *);
  17633. char *getenv(const char *);
  17634. long labs(long);
  17635. ldiv_t ldiv(long, long);
  17636. void *malloc(size_t);
  17637. int mblen(const char *, size_t);
  17638. int wctomb(char *, wchar_t);
  17639. int mbtowc(wchar_t, const char *, size_t);
  17640. void qsort
  17641.  (
  17642.  void *, size_t, size_t,
  17643.  int (*)(const void *, const void *)
  17644.  );
  17645. int rand(void);
  17646. void *realloc(void *, size_t);
  17647. void srand(unsigned);
  17648. double strtod(const char *, char **);
  17649. long strtol(const char *, char **, int);
  17650. unsigned long strtoul(const char *, char **, int);
  17651. int system(const char *);
  17652.  
  17653. #define _STDLIB_H_INCLUDED
  17654. #endif
  17655.  
  17656. Table 2
  17657. Macros defined by <limits.h>:
  17658.  
  17659. CHAR_BIT - number of bits in the smallest object that isn't a
  17660.  bit field (a byte)
  17661. SCHAR_MIN - minimum value for an object of type signed char
  17662. SCHAR_MAX - maximum value for an object of type signed char
  17663. UCHAR_MAX - maximum value for an object of type unsigned char
  17664. CHAR_MIN - minimum value for an object of type (plain) char
  17665. CHAR_MAX - maximum value for an object of type (plain) char
  17666. MB_LEN_MAX - maximum number of bytes in a multibyte character,
  17667.  for any supported locale
  17668. SHRT_MIN - minimum value for an object of type short int
  17669. SHRT_MAX - maximum value for an object of type short int
  17670. USHRT_MAX - maximum value for an object of type unsigned short
  17671.  int
  17672. INT_MIN - minimum value for an object of type int
  17673. INT_MAX - maximum value for an object of type int
  17674. UINT_MAX - maximum value for an object of type unsigned int
  17675. LONG_MIN - minimum value for an object of type long int
  17676. LONG_MAX - maximum value for an object of type long int
  17677. ULONG_MAX - maximum value for an object of type unsigned long
  17678.  int
  17679.  
  17680. Listing 1
  17681. /*
  17682. * quirks.h - eliminate quirks (for DECUS C)
  17683. */
  17684. #ifndef _QUIRKS_H_INCLUDED
  17685.  
  17686.  
  17687. #define const
  17688. #define signed
  17689. #define volatile
  17690.  
  17691. typedef char void;
  17692.  
  17693. *define _QUIRKS_H_INCLUDED
  17694. #endif
  17695.  
  17696.  
  17697. Listing 2
  17698. /*
  17699. * stdlib.h - general utilities (for UNIX 4.2 BSD)
  17700. */
  17701. #ifndef _STDLIB_H_INCLUDED
  17702.  
  17703. #include <quirks.h>
  17704.  
  17705. #define EXIT_SUCCESS 0
  17706. #define EXIT_FAILURE 1
  17707.  
  17708. #define MB_CUR_MAX 1
  17709.  
  17710. #define NULL ((void_star)0)
  17711.  
  17712. #define RAND_MAX 2147483647
  17713.  
  17714. typedef struct {int quot, rem} div_t;
  17715. typedef struct {long quot, rem} ldiv_t;
  17716.  
  17717. #ifndef _SIZE_T_DEFINED
  17718. typedef unsigned size_t;
  17719. #define _SIZE_T_DEFINED
  17720. #endif
  17721.  
  17722. #ifndef _WCHAR_T_DEFINED
  17723. typedef char wchar_t;
  17724. #define _WCHAR_T_DEFINED
  17725. #endif
  17726.  
  17727. void abort();
  17728. double atof();
  17729. int atoi();
  17730. long atol();
  17731. void_star calloc();
  17732. void exit();
  17733. void free();
  17734. char *getenv();
  17735. void_star malloc();
  17736. void qsort();
  17737. int rand();
  17738. void_star realloc();
  17739. void srand();
  17740. int system();
  17741.  
  17742. /*
  17743. * interim macro definitions for functions
  17744. */
  17745. #define abs(j) ((j) >= 0 ? (j) : -(j))
  17746.  
  17747. #define labs(j) abs((long)(j))
  17748.  
  17749. /*
  17750. * missing functions
  17751. */
  17752. int atexit();
  17753. void_star bsearch();
  17754. div_t div();
  17755. ldiv_t ldiv();
  17756. int mblen();
  17757. int wctomb();
  17758. int mbtowc();
  17759. double strtod();
  17760. long strtol();
  17761. unsigned long strtoul();
  17762.  
  17763. #define _STDLIB_H_INCLUDED
  17764. #endif
  17765.  
  17766.  
  17767. Listing 3
  17768. /*
  17769. * stddef.h - common definitions (for DECUS c)
  17770. */
  17771. #ifndef _STDDEF_H_INCLUDED
  17772.  
  17773. #include <quirks.h>
  17774.  
  17775. #define NULL ((void *)0)
  17776.  
  17777. #define offsetof(t, m) ((size_t)&(((t *)NULL)->m))
  17778.  
  17779. typedef int ptrdiff_t;
  17780.  
  17781. #ifndef _SIZE_T_DEFINED
  17782. typedef unsigned size_t;
  17783. #define _SIZE_T_DEFINED
  17784. #endif
  17785.  
  17786. #ifndef _WCHAR_T_DEFINED
  17787. typedef char wchar_t;
  17788. #define _WCHAR_T_DEFINED
  17789. #endif
  17790.  
  17791. #define _STDDEF_H_INCLUDED
  17792. #endif
  17793.  
  17794.  
  17795. Listing 4
  17796. #include <stdio.h>
  17797. #include <stdarg.h>
  17798. #include <string.h>
  17799.  
  17800. /*
  17801. * Concatenate copies of a variable number strings into
  17802. * s1. The list of strings must be terminated by NULL.
  17803. * concat returns s1.
  17804. */
  17805. char *concat(char *s1, ...)
  17806.  
  17807. {
  17808. char *s = s1;
  17809. const char *t;
  17810. va_list ap;
  17811.  
  17812. va_start(ap, s1);
  17813. while ((t = va_arg(ap, const char *)) != NULL)
  17814. {
  17815. strcpy(s, t);
  17816. s += strlen(s);
  17817. }
  17818. va_end(ap);
  17819. return s1;
  17820. }
  17821.  
  17822. int main(void)
  17823. {
  17824. char s[100];
  17825.  
  17826. puts(concat(s, "This ", "is ", "great!", NULL));
  17827. return 0;
  17828. }
  17829.  
  17830.  
  17831. Listing 5
  17832. /*
  17833. * stdarg.h - variable-length argument processing (for stack-
  17834. * oriented argument passing with the 1st argument at the
  17835. * lowest address)
  17836. */
  17837. #ifndef _STDARG_H_INCLUDED
  17838.  
  17839. #include <quirks.h>
  17840.  
  17841. typeder char *va_list;
  17842.  
  17843. #define va_start(ap, p) ((void)((ap) = (va_list)(&(p) + 1)))
  17844.  
  17845. #define va_arg(ap, t) (((t *)((ap) += stzeof(t)))[-1])
  17846.  
  17847. #define va_end(ap) ((void)0)
  17848.  
  17849. #define _STDARG_H_INCLUDED
  17850. #endif
  17851.  
  17852.  
  17853. Listing 6
  17854. /*
  17855. * stdarg.h - variable-length argument processing (for stack-
  17856. * oriented argument passing with the 1st argument at the
  17857. * highest address)
  17858. */
  17859. #ifndef _STDARG_H_INCLUDED
  17860.  
  17861. #include <quirks.h>
  17862.  
  17863. typedef char *va_list;
  17864.  
  17865. #define va_start(ap, p) ((void)((ap) = (va_list)&(p)))
  17866.  
  17867.  
  17868. #define va_arg(ap, t) (*(t *)((ap) -= sizeof(t)))
  17869.  
  17870. #define va_end(ap) ((void)0)
  17871.  
  17872. #define _STDARG_H_INCLUDED
  17873. #endif
  17874.  
  17875.  
  17876. Listing 7
  17877. /*
  17878. * limits.h - sizes of integral types (using minimum
  17879. * magnitudes)
  17880. *
  17881. #ifndef _LIMITS_H_INCLUDED
  17882.  
  17883. #include <quirks.h>
  17884.  
  17885. #define CHAR_BIT 8
  17886. #define SCHAR_MIN (-127)
  17887. #define SCHAR_MAX 127
  17888. #define UCHAR_MAX 255
  17889.  
  17890. #ifndef _CHAR_UNSIGNED
  17891. #define CHAR_MAX SCHAR_MAX
  17892. #define CHAR_MIN SCHAR_MIN
  17893. #else
  17894. #define CHAR_MAX UCHAR_MAX
  17895. #define CHAR_MIN 0
  17896. #endif
  17897.  
  17898. #define MB_LEN_MAX 1
  17899. #define SHRT_MIN (-32767)
  17900. #define SHRT_MAX 32767
  17901. #define USHRT_MAX 65535u
  17902. #define INT_MIN (-32767)
  17903. #define INT_MAX 32767
  17904. #define UINT_MAX 65535u
  17905. #define LONG_MIN (-2147483647)
  17906. #define LONG_MAX 2147483647
  17907. #define ULONG_MAX 4294967295u
  17908.  
  17909. #define _LIMITS_H_INCLUDED
  17910. #endif
  17911.  
  17912.  
  17913.  
  17914.  
  17915.  
  17916.  
  17917.  
  17918.  
  17919.  
  17920.  
  17921.  
  17922.  
  17923.  
  17924.  
  17925.  
  17926.  
  17927.  
  17928.  
  17929.  
  17930. Linked Lists In C++
  17931.  
  17932.  
  17933. Bob Jarvis
  17934.  
  17935.  
  17936. Bob Jarvis is a Senior Capacity Planning Analyst for American Greetings
  17937. Corporation who has been programming in C for four years, and C++ for a year
  17938. and a half. His current interests include computer performance analysis and
  17939. object-oriented programming. He can be reached at American Greetings Corp.,
  17940. 10500 American Road, Cleveland, OH 44144.
  17941.  
  17942.  
  17943. C++ offers a number of useful enhancements to C, including the ability to
  17944. easily derive new specialized classes from previously-defined classes. This
  17945. class derivation technique is particularly useful when working with so-called
  17946. "container" classes (i.e., linked lists, stacks, B-trees, etc.). Programmers
  17947. should be able to derive specialized containers for each type of object used
  17948. in a manner which ensures safe and efficient storage and use. This article
  17949. presents an implementation of a double linked list class for C++, and
  17950. discusses some of the problems encountered during the design and
  17951. implementation of this class.
  17952.  
  17953.  
  17954. What's In A List?
  17955.  
  17956.  
  17957. A linked list is a data structure in which each individual data element is
  17958. stored as a single node in the list and each node contains pointers, or
  17959. "links", to other elements in the list. In a double linked list there are two
  17960. pointers -- one to the previous element in the list and another to the next
  17961. element. (In a simpler form, the single linked list, each node has a single
  17962. pointer to the next element in the list. The single linked list simplifies
  17963. list maintenance and reduces the storage requirements slightly, but eliminates
  17964. the ability to traverse the list "backwards"). Unlike an array, physical
  17965. storage order is irrelevant in a linked list; with pointers the previous and
  17966. next elements may be located at a memory address before or after the current
  17967. element.
  17968. Three positions in the list are particularly important: the "head" (the first
  17969. element in the list), the "tail" (the last element in the list), and the
  17970. "current" element (the element currently being used). Note that these three
  17971. positions may not necessarily differ from one another -- the current element
  17972. may be the same as the head or tail elements. In fact all three logical
  17973. positions will point to the same physical element if there is only one element
  17974. in the list.
  17975.  
  17976.  
  17977. Implementation Considerations
  17978.  
  17979.  
  17980. Figure 1 shows the interrelationships of nodes within a hypothetical double
  17981. linked list. We can see immediately that there are two distinct object types
  17982. -- the linked list itself, and the nodes within the list. (You could also
  17983. argue that there is a third object, namely the data stored within the list).
  17984. Although my first cut at implementing LIST also implemented the list nodes as
  17985. a true class, the present version implements the nodes as a simple structure.
  17986. This was done primarily for programming convenience (I found it easier to
  17987. derive subclasses this way).
  17988. As implemented here, a LIST copies the data to be stored in the nodes and
  17989. keeps a pointer to the copy. Copying eliminates any requirement that data
  17990. stored in a LIST be static, but requires that LIST subclasses be able to
  17991. properly allocate, copy, and delete instances of the data stored in the nodes,
  17992. which in turn means that LISTs should be "aware" of the type of data that
  17993. they're storing.
  17994. This need for awareness creates a problem. As implemented, the base LIST class
  17995. stores a string of bytes, but has no knowledge of what is actually being
  17996. stored. If the object being stored in the list has pointers to
  17997. dynamically-allocated memory, those pointers will be copied, creating two
  17998. objects which point to the same memory area. When either one of those objects
  17999. is destroyed, the memory pointed to by the one object is freed, leaving the
  18000. remaining object with a pointer to a memory area which is no longer valid.
  18001. The solution to this problem uses virtual functions to create, copy, and
  18002. delete copies of the data stored in the LIST. The default functions provided
  18003. in the base LIST class should work properly for all simple objects which do
  18004. not contain pointers to dynamically-allocated storage. If the data objects
  18005. being held in a class derived from LIST do contain pointers to dynamic storage
  18006. the virtual functions (the create_data(), copy_data(), and delete_data()
  18007. member functions) will have to be rewritten to correctly invoke the
  18008. constructors and destructors as appropriate. (Note that having an operator=()
  18009. function defined as part of the data object class greatly simplifies the task
  18010. of rewriting the copy_data() function). In Listing 5 the copy_data() function
  18011. demonstrates how casts can be used to simplify the process of rewriting the
  18012. virtual functions.
  18013.  
  18014.  
  18015. Usage
  18016.  
  18017.  
  18018. Using the LIST class is fairly straightforward. An instance of the class is
  18019. declared, then elements are added to it, retrieved, etc. In Listing 1 a LIST
  18020. named ilist is declared and filled with a sequence of integers. These integers
  18021. are then retrieved and printed.
  18022.  
  18023.  
  18024. Deriving Sub-Classes
  18025.  
  18026.  
  18027. While using the generic LIST class to hold a series of integers works quite
  18028. well, adding elements to the list is a bit awkward. Two parameters must be
  18029. supplied -- the address of the integer and the size of the element being added
  18030. (in this case an integer). While a #define could be used to "neaten" things
  18031. up, a LIST which specifically handled integers would be better. Creating such
  18032. a LIST is not difficult to do since all of the member functions can be
  18033. replaced by inline calls to the member functions in the original LIST class
  18034. (Listing 4).
  18035. In this case we create a class named INTLIST, derived from LIST. The INTLIST
  18036. class does not automatically inherit the public members of LIST (it is
  18037. declared as INTLIST : LIST rather than INTLIST : public LIST). This
  18038. declaration limits users to the interface defined for INTLIST; otherwise,
  18039. users could add other non-integer items by using the generic functions defined
  18040. for LISTs.
  18041. A new data item (curr_size) was added to the INTLIST class. curr_size holds
  18042. the size of the current item in the list, and is equivalent to sizeof(int)
  18043. except in the case where an action could not be satisfied (such as getting the
  18044. next element after the last element in the list), in which case curr_size is
  18045. set to zero. The test code using the LIST class can now be modified to use an
  18046. INTLIST as in Listing 2.
  18047.  
  18048.  
  18049. Summary
  18050.  
  18051.  
  18052. The addition of container classes and the ability to derive specialized
  18053. classes from them makes using C++ faster and more reliable than C. Programmers
  18054. can concentrate on developing code to solve problems rather than writing and
  18055. rewriting repetitive functionality such as linked lists.
  18056. Figure 1
  18057.  
  18058. Listing 1
  18059. #include <stream.hpp>
  18060. #include "list.hpp"
  18061.  
  18062. main()
  18063. {
  18064. LIST ilist;
  18065. int i,size,*iptr;
  18066.  
  18067.  
  18068. for(i = 0 ; i < 10 ; ++i)
  18069. {
  18070. cout << "Adding " << i << "\n";
  18071. ilist.add_tail(&i, sizeof(i));
  18072. }
  18073.  
  18074. cout << "\n";
  18075.  
  18076. iptr = ilist.get_head(size);
  18077.  
  18078. while(iptr != NULL)
  18079. {
  18080. cout << "Retrieved " << *iptr << "\n";
  18081. iptr = ilist.get_next(size);
  18082. }
  18083. }
  18084.  
  18085.  
  18086. Listing 2
  18087. #include <stream.hpp>
  18088. #include "intlist.hpp"
  18089.  
  18090. main()
  18091. {
  18092. INTLIST ilist;
  18093. int i;
  18094.  
  18095. for(i = 0 ; i < 10 ; ++i)
  18096. {
  18097. cout << "Adding " << i << "\n";
  18098. ilist.add_tail(i);
  18099. }
  18100.  
  18101. cout << "\n";
  18102.  
  18103. i = ilist.get_head();
  18104. while(ilist.get_curr_size() != 0)
  18105. {
  18106. cout << "Retrieved " << i << "\n";
  18107. i = ilist.get_next();
  18108. }
  18109. }
  18110.  
  18111.  
  18112. Listing 3
  18113. #include <stream.hpp>
  18114. #include <stddef.h>
  18115. #include <string.h>
  18116. #include "list.hpp"
  18117.  
  18118. LIST::LIST() // constructor
  18119. {
  18120. head = curr = tail = NULL;
  18121. }
  18122.  
  18123. LIST::~LIST() // destructor
  18124. {
  18125. struct listelem *work;
  18126.  
  18127.  
  18128. while(head != NULL)
  18129. {
  18130. delete_data(head);
  18131. work = head->next;
  18132. delete head;
  18133. head = work;
  18134. }
  18135. }
  18136.  
  18137. void *LIST::get_head(unsigned int &sz)
  18138. {
  18139. curr = head;
  18140. return get_curr(sz);
  18141. }
  18142.  
  18143. void *LIST::get_curr(unsigned int &sz)
  18144. {
  18145. if(curr == NULL)
  18146. {
  18147. sz = 0;
  18148. return NULL;
  18149. }
  18150. sz = curr->size;
  18151. return curr->data;
  18152. }
  18153.  
  18154. void *LIST::get_tail(unsigned int &sz)
  18155. {
  18156. curr = tail;
  18157. return get_curr (sz);
  18158. }
  18159.  
  18160. void *LIST::get_prev(unsigned int &sz)
  18161. {
  18162. if(curr->prev != NULL)
  18163. {
  18164. curr = curr->prev;
  18165. return get_curr(sz);
  18166. }
  18167. else
  18168. {
  18169. sz = 0;
  18170. return NULL;
  18171. }
  18172. }
  18173.  
  18174. void *LIST::get_next(unsigned int &sz)
  18175. {
  18176. if(curr->next != NULL)
  18177. {
  18178. curr = curr->next;
  18179. return get_curr(sz);
  18180. }
  18181. else
  18182. {
  18183. sz = 0;
  18184. return NULL;
  18185. }
  18186. }
  18187.  
  18188.  
  18189. void LIST::add_before(void *vptr, unsigned int sz)
  18190. {
  18191. struct listelem *lptr;
  18192.  
  18193. lptr = new struct listelem;
  18194. if(lptr == NULL)
  18195. exit(99); // ugly - should be fixed later
  18196.  
  18197. lptr->size = sz;
  18198. create_data(lptr);
  18199. copy_data(lptr,vptr);
  18200.  
  18201. // rearrange pointers
  18202.  
  18203. if(curr != NULL)
  18204. {
  18205. lptr->prev = curr->prev;
  18206. lptr->next = curr;
  18207.  
  18208. if(lptr->prev != NULL)
  18209. lptr->prev->next = lptr;
  18210. else
  18211. head = lptr;
  18212.  
  18213. curr->prev = lptr;
  18214. }
  18215. else // curr == NULL - must be first element in list
  18216. {
  18217. lptr->prev = lptr->next = NULL;
  18218. head = curr = tail = lptr;
  18219. }
  18220. }
  18221.  
  18222. void LIST::add_after(void *vptr, unsigned int sz)
  18223. {
  18224. struct listelem *lptr;
  18225.  
  18226. lptr = new struct listelem;
  18227. if(lptr == NULL)
  18228. exit(99); // ugly - should be fixed later
  18229.  
  18230. lptr->size = sz;
  18231. create_data(lptr);
  18232. copy_data(lptr,vptr);
  18233.  
  18234. // rearrange pointers
  18235.  
  18236. if(curr != NULL)
  18237. {
  18238. lptr->prev = curr;
  18239. lptr->next = curr->next;
  18240.  
  18241. curr->next = lptr;
  18242.  
  18243. if(lptr->next != NULL)
  18244. lptr->next->prev = lptr;
  18245. else
  18246. tail = lptr;
  18247.  
  18248. }
  18249. else // curr == NULL - must be first element in list
  18250. {
  18251. lptr->prev = lptr->next = NULL;
  18252. head = curr = tail = lptr;
  18253. }
  18254. }
  18255.  
  18256. void LIST::add_head(void *vptr, unsigned int sz) 
  18257. }
  18258. struct listelem *lptr;
  18259.  
  18260. lptr = new struct listelem;
  18261. if(lptr == NULL)
  18262. exit(99); // ugly - should be fixed later
  18263.  
  18264. lptr->size = sz;
  18265. create_data(lptr);
  18266. copy_data(lptr,vptr);
  18267.  
  18268. if(head != NULL)
  18269. {
  18270. lptr->prev = NULL;
  18271. lptr->next = head;
  18272. head->prev = lptr;
  18273. head = lptr;
  18274. }
  18275. else
  18276. {
  18277. lptr->prev = lptr->next = NULL;
  18278. head = curr = tail = lptr;
  18279. }
  18280. }
  18281.  
  18282. void LIST::add_tail(void *vptr, unsigned int sz)
  18283. {
  18284. struct listelem *lptr;
  18285.  
  18286. lptr = new struct listelem;
  18287. if(lptr == NULL)
  18288. exit(99); // ugly - should be fixed later
  18289.  
  18290. lptr->size = sz;
  18291. create_data(lptr);
  18292. copy_data(lptr,vptr);
  18293.  
  18294. if(tail != NULL)
  18295. {
  18296. lptr->next = NULL;
  18297. lptr->prev = tail;
  18298. tail->next = lptr;
  18299. tail = lptr;
  18300. }
  18301. else
  18302. {
  18303. lptr->prev = lptr->next = NULL;
  18304. head = curr = tail = lptr;
  18305. }
  18306. }
  18307.  
  18308.  
  18309. void LIST::delete_curr()
  18310. {
  18311. struct listelem *lptr;
  18312.  
  18313. if(curr == NULL)
  18314. return;
  18315.  
  18316. lptr = curr;
  18317.  
  18318. if(curr->prev != NULL)
  18319. curr->prev->next = curr->next;
  18320. else
  18321. head = curr->next;
  18322.  
  18323. if(curr->next != NULL)
  18324. curr->next->prev = curr->prev;
  18325. else
  18326. tail = curr->prev;
  18327.  
  18328. if(curr->prev != NULL)
  18329. curr = curr->prev;
  18330. else if(curr->next != NULL)
  18331. curr = curr->next;
  18332. else if(head == NULL && tail == NULL)
  18333. curr = NULL; // list is now empty
  18334. else
  18335. {
  18336. cerr << "LIST::delete_curr() : deletion sequence error\n";
  18337. exit(99);
  18338. }
  18339.  
  18340. delete_data(lptr);
  18341. delete lptr;
  18342. }
  18343.  
  18344. // The following three functions should be replaced in all
  18345. // derived classes. create_data() should allocate and
  18346. // initialize space for a new instance of the class being
  18347. // stored in the list (implying that you should derive a
  18348. // new class for each type of object). delete_data() should
  18349. // handle the destruction of class instances stored in a list.
  18350. // copy_data() must take care of copying a complete class
  18351. // instance from one place to another - it must properly
  18352. // handle the situation where a class being stored in a list
  18353. // has dynamically-allocated storage. As written these
  18354. // functions should properly handle all simple types (that
  18355. // is, types which have no dynamic storage).
  18356.  
  18357. void LIST::create_data(struct listelem *lptr)
  18358. {
  18359. lptr->data = new char[lptr->size];
  18360. if(lptr->data == NULL)
  18361. exit(99);
  18362. }
  18363.  
  18364. void LIST::delete_data(struct listelem *lptr)
  18365. {
  18366. if(lptr->data !=NULL)
  18367.  
  18368. delete lptr->data;
  18369. }
  18370.  
  18371. void LIST::copy_data(struct listelem *lptr, void *from)
  18372. {
  18373. memcpy(lptr->data,from,lptr->size);
  18374. }
  18375.  
  18376.  
  18377. Listing 4
  18378. // LIST.HPP - linked list interface
  18379.  
  18380. #ifndef _LIST_HPP
  18381. #define _LIST_HPP
  18382.  
  18383. class LIST
  18384. {
  18385. struct listelem
  18386. {
  18387. struct listelem *prev, *next;
  18388. void *data;
  18389. unsigned int size;
  18390. };
  18391. struct listelem *head, *curr, *tail;
  18392. public:
  18393. LIST();
  18394. ~LIST();
  18395. void *get_head(unsigned int &sz);
  18396. void *get_curr(unsigned int &sz);
  18397. void *get_tail(unsigned int &sz);
  18398. void *get_prev(unsigned int &sz);
  18399. void *get_next(unsigned int &sz);
  18400. void add_before(void *vptr, unsigned int sz);
  18401. void add_after(void *vptr, unsigned int sz);
  18402. void add_head(void *vptr, unsigned int sz);
  18403. void add_tail(void *vptr, unsigned int sz);
  18404. void delete_curr(void);
  18405. virtual void delete_data(struct listelem *lptr);
  18406. virtual void create_data(struct listelem *lptr);
  18407. virtual void copy_data(struct listelem *lptr, void *from);
  18408. };
  18409.  
  18410. #endif // ifndef _LIST_HPP
  18411.  
  18412.  
  18413. Listing 5
  18414. // INTLIST.HPP - list of integers - derived from LIST
  18415.  
  18416. #ifndef _INTLIST_HPP
  18417. #define _INTLIST_HPP
  18418.  
  18419. #include "list.hpp"
  18420.  
  18421. class INTLIST : LIST // a LIST of integers...
  18422. {
  18423. unsigned int curr_size;
  18424. public:
  18425. INTLIST(void) {curr_size = 0;}
  18426. int get_head(void) {return *((int*)LIST::get_head(curr_size));}
  18427.  
  18428. int get_curr(void) {return *((int*)LIST::get_curr(curr_size));}
  18429. int get_tail(void) {return *((int*)LIST::get_tail(curr_size));}
  18430. int get_prev(void) {return *((int*)LIST::get_prev(curr_size));}
  18431. int get_next(void) {return *((int*)LIST::get_next(curr_size));}
  18432. void add_before(int i) {LIST::add_before(&i, sizeof(int));}
  18433. void add_after(int i) {LIST::add_after(&i, sizeof(int));}
  18434. void add_head(int i) {LIST::add_head(&i, sizeof(int));}
  18435. void add_tail(int i) {LIST::add_tail(&i, sizeof(int));}
  18436. void delete_curr(void) {LIST::delete_curr();}
  18437. unsigned int get_curr_size(void)
  18438. {return curr_size;}
  18439. void copy_data(struct listelem *lptr, void *from)
  18440. {*((int*)lptr->data) = *((int*)from);}
  18441. };
  18442.  
  18443. #endif // _INTLIST_HPP
  18444.  
  18445.  
  18446.  
  18447.  
  18448.  
  18449.  
  18450.  
  18451.  
  18452.  
  18453.  
  18454.  
  18455.  
  18456.  
  18457.  
  18458.  
  18459.  
  18460.  
  18461.  
  18462.  
  18463.  
  18464.  
  18465.  
  18466.  
  18467.  
  18468.  
  18469.  
  18470.  
  18471.  
  18472.  
  18473.  
  18474.  
  18475.  
  18476.  
  18477.  
  18478.  
  18479.  
  18480.  
  18481.  
  18482.  
  18483.  
  18484.  
  18485.  
  18486.  
  18487.  
  18488.  
  18489.  
  18490.  
  18491. Standard C
  18492.  
  18493.  
  18494. Quiet Changes, Part II
  18495.  
  18496.  
  18497.  
  18498.  
  18499. P.J. Plauger
  18500.  
  18501.  
  18502. P.J. Plauger has been a prolific programmer, textbook author, and software
  18503. entrepreneur. He is secretary of the ANSI C standards committee, X3J11, and
  18504. convenor of the ISO C standards committee.
  18505.  
  18506.  
  18507. Last month, I began the process of describing all of the quiet changes in
  18508. Standard C. (See "Quiet Changes, Part I," CUJ February '90.) A quiet change is
  18509. a change in the meaning of Standard C versus some earlier (and presumably
  18510. popular) dialect of C. It is a change that converts an acceptable program with
  18511. one behavior to an acceptable program with different behavior. You get no
  18512. diagnostic to warn you that your program may have to be altered to keep its
  18513. old behavior.
  18514. Needless to say, quiet changes are not nice. Committee X3J11 did its best to
  18515. minimize them. It considered each such change very carefully. And it
  18516. documented all the quiet changes it felt compelled to make in the rationale
  18517. that accompanies the standard.
  18518. My goal with this column and the previous one is to show you all the quiet
  18519. changes. With each one, I give an example of code that might be affected. I
  18520. also endeavor to explain how the committee was led to introduce the change.
  18521. Last issue, I covered about half of the quiet changes. Here are the rest.
  18522.  
  18523.  
  18524. More Quiet Changes
  18525.  
  18526.  
  18527. "A program that depends upon unsigned preserving arithmetic conversions will
  18528. behave differently, probably without complaint.This is considered the most
  18529. serious semantic change made by the committee to a widespread current
  18530. practice."
  18531. For example,
  18532. unsigned char uc = digit;
  18533.  
  18534. if (uc - '0' > 'g')
  18535. printf("not a digit\n");
  18536. The message is no longer printed for all cases where digit is out of range.
  18537. Some older implementations always performed an unsigned compare and hence
  18538. cleverly got the test right for all cases.
  18539. The fact that I devoted a whole column to this issue should tell you that it
  18540. has a lot of ramifications. (See "Standard C Promotes Types According to Value
  18541. Preserving Rules," CUJ August '88.) Here I will simply summarize the issue. It
  18542. required the committee to choose between two divergent classes of dialects.
  18543. Neither could be dropped without causing quiet changes in programs written for
  18544. the other dialect.
  18545. The divergence occurred when C acquired the additional unsigned types besides
  18546. unsigned int. These new types required additional rules for promoting integer
  18547. types, such as when you subtract an unsigned char and an int. The decision
  18548. inside Bell Labs was to preserve unsignedness. That is, if either operand is
  18549. an unsigned type, both operands are promoted to the "cheapest" computational
  18550. unsigned type that is at least as wide as the wider operand. (The
  18551. computational types are the signed and unsigned versions of int and long.) So
  18552. unsigned char minus int yields a result of type unsigned int.
  18553. What I and some other implementors chose to do was different. We chose to
  18554. promote both operands to the cheapest computational type that would represent
  18555. all the values of each of the two operand types. So unsigned char minus int
  18556. yields a result of type int, so long as unsigned char has a narrower
  18557. representation than int.
  18558. Unlike the "unsigned preserving" promotion rules, the result type is different
  18559. for different target machines. On the other hand, the result more often has a
  18560. type that correctly reports a negative value as a negative signed result, not
  18561. some huge unsigned result. That is why the second set of promotion rules has
  18562. been dubbed "value preserving."
  18563. After much heated debate, the value preserving faction won. The most
  18564. convincing argument was that value preserving promotions produced surprising
  18565. results less often. The most convincing counter argument was that UNIX, and
  18566. lots of other important code, was written using unsigned preserving
  18567. promotions. Groups had successfully ported UNIX, however, using a compiler
  18568. with value preserving promotions. That quelled enough fears for the committee
  18569. to reach consensus.
  18570. My belief is that this was a tempest in a teapot. (Naturally, it didn't feel
  18571. that way to those of us in the teapot at the time.) It's hard to contrive
  18572. realistic examples that quietly change along with these promotion rules. I
  18573. gave the most compelling one I could think of above, in the spirit of fair
  18574. play. If you're still worried, however, go back and read the full diatribe in
  18575. my original column on the subject.
  18576. "Expressions with float operands may now be computed at lower precision. The
  18577. Base Document specified that all floating point operations be done in double."
  18578. For example,
  18579. float x, y;
  18580. x = x - y;
  18581. The subtraction can now be performed to float precision, which could retain
  18582. less significance than in the past.
  18583. C has traditionally performed all floating point arithmetic in double. This
  18584. minimized type mismatches for floating point arguments back before there were
  18585. function prototypes. It also happened to model the behavior of the PDP-11
  18586. floating point hardware used in the first implementation of C.
  18587. Unfortunately, that traditional behavior has been one of the principal reasons
  18588. why more FORTRAN programs have not been converted to C. The performance
  18589. penalty can be substantial. What you gain in retained precision is often not
  18590. worth the cost, particularly to programmers skilled in juggling precisions.
  18591. The committee had little trouble changing the promotion rules to match FORTRAN
  18592. more closely. We felt that few programs depend critically on the higher
  18593. intermediate precision promised in the past when adding operands of type
  18594. float.
  18595. "A program that uses #if expressions to determine properties of the execution
  18596. environment may now get different answers."
  18597. #if -1U/2+1 == 1U<<31
  18598. /* int is 32-bits, 2's-complement */
  18599. The comment is not necessarily true for the target environment.
  18600. Some C programs determined properties of the execution environment by testing
  18601. how the C preprocessor performed integer arithmetic. The assumption was that
  18602. preprocessor arithmetic was the same as for the target environment. That
  18603. happens not to be true for most cross compilers. It is also not true for many
  18604. compilers designed to support numerous target environments.
  18605. The committee decided that target-independent preprocessors were a Good Thing.
  18606. To be sure, compile time arithmetic must retain at least the same precision
  18607. and range as arithmetic on the target. But it need not slavishly match all of
  18608. the foibles of the target.
  18609. Instead of writing clever (and unreadable) expressions in #if directives,
  18610. programmers are now urged to test the values of appropriate macros. The
  18611. standard header <limits.h> defines macros that tell you all sorts of
  18612. interesting things about the representation of integers on the target. The
  18613. standard header <float.h> defines macros that tell you more than you're likely
  18614. ever to want to know about the representation of floating point types. The
  18615. above example should be changed to
  18616. #include <limits.h>
  18617. #if INT_MAX == 2147483647 && \
  18618. INT_MIN == -2147483648
  18619. /* int is 32-bits, 2's-complement */
  18620. An implementation can still promise to model the target arithmetic in the
  18621. preprocessor. In that case, the clever programs need not change. If you want
  18622. to port them to another implementation, however, you'd probably have to change
  18623. the #if expressions anyway.
  18624. "The empty declaration struct x; is no longer innocuous.
  18625. For example,
  18626.  
  18627. f() {
  18628. struct x; /* special meaning */
  18629. struct y {
  18630. struct x *px;
  18631. ..... };
  18632. struct x {
  18633. struct y *py;
  18634. ..... };
  18635. The first declaration now assures that the two structures point at each other,
  18636. regardless of any outer context.
  18637. Just as block structure does not mix well with external linkage, it collides
  18638. at times with forward references as well. C lets you declare a structure tag
  18639. before you declare the contents of the structure. (It is an incomplete type.)
  18640. You need to make such a forward reference when you declare two structures each
  18641. of which contains a pointer to the other.
  18642. Unfortunately, C had no way to shield a forward reference from a structure tag
  18643. definition visible in an outer block. Should you wish to plunk down a patch of
  18644. code containing forward references to tags in an arbitrary code environment,
  18645. you ran the risk of having the patch misbehave in some contexts. This defeats
  18646. much of the purpose of block structuring to protect name spaces.
  18647. This is an esoteric problem. You've probably never encountered it and you
  18648. probably never will. Nevertheless, the committee decided to solve it by giving
  18649. special meaning to an otherwise empty declaration such as struct x;. You can
  18650. contrive a program that breaks because this esoteric problem has been fixed.
  18651. You will have trouble convincing me that it is a program worth writing.
  18652. "Code which relies on a bottom-up parse of aggregate initializers with
  18653. partially elided braces will not yield the expected initialized object."
  18654. You can, of course, initialize structures containing structures, or arrays of
  18655. arrays, or arrays of structures. For any complex initializer involving
  18656. aggregates, you write braces around the stuff for each aggregate to set it
  18657. off. Unfortunately, C has a long tradition of letting you omit all but the
  18658. outermost set of braces. For a compiler writer, this is a nightmare.
  18659. When implementors on the committee started comparing nightmares, matters got
  18660. even worse. It seems that people had settled on at least two different ways to
  18661. parse aggregate initializers with partially elided braces. In terms of parsing
  18662. theory, the two general camps can be characterized as "top-down" and
  18663. "bottom-up." I will not bore you with detailed examples to illustrate the
  18664. subtle differences.
  18665. What you need to know is that the committee eventually endorsed the top-down
  18666. approach to parsing initializers. You also need to know that omitting braces
  18667. is a great way to confuse yourself and future maintainers. Never mind that
  18668. Standard C translators are all supposed to guess the same way. If an
  18669. initializer is sufficiently complex, you are asking for trouble if you omit
  18670. any of the internal braces.
  18671. Any program that suffers from this quiet change already faced portability
  18672. problems in the past.
  18673. "Type long expressions and constants in switch statements are no longer
  18674. truncated to int."
  18675. For example, on an implementation where type int occupies 16 bits,
  18676. long lo = 0x20001;
  18677. switch (lo) {
  18678. case 0x0001: /* no longer
  18679. matches */
  18680. The switch comparisons are now performed with long arithmetic, so the first
  18681. case does not match a truncated value.
  18682. The committee entertained proposals for all sorts of improvements to switch
  18683. statements. We decided against permitting floating point or pointer
  18684. expressions to control switch statements. On the other hand, we found little
  18685. justification for continuing to rule out the other integer computational types
  18686. besides int. A switch statement whose control expression is of type long will
  18687. now do all its comparisons against case values with long arithmetic.
  18688. Conceivably, an existing program contains a switch expression of type long.
  18689. Conceivably, the program depends upon the value being altered when it is
  18690. converted to type int for the comparisons. If so, then the program will
  18691. quietly change its behavior. The likelihood of such an occurrence is
  18692. reasonably remote.
  18693. "Functions that depend on char or short parameter types being widened to int,
  18694. or float to double, may behave differently."
  18695. For example,
  18696. f(x, y)
  18697. char x;
  18698. {
  18699. if (y == 0)
  18700. x = 500;
  18701. In Standard C, x remains type char. Hence, the stored value would probably be
  18702. truncated. Many past implementations would promote x to type int.
  18703. One school of thought in the past was that a parameter of type char was silly.
  18704. Everyone knew that the argument value was actually passed as type int, so why
  18705. not just rewrite the type of the parameter to match the passed value?
  18706. The alternate school was that the programmer's wishes should be obeyed.
  18707. However the argument value was passed, it should be stored in a data object of
  18708. the declared type. You get fewer surprises that way. Besides, for most integer
  18709. parameters, you can make the conversion "free" just by picking the right piece
  18710. of the argument value as the parameter data object.
  18711. The second school of thought prevailed in the end. That causes trouble for
  18712. programs written for translators that rewrite parameter types. (Again, the
  18713. trouble was already there for people who tried to move such programs between
  18714. implementations.)
  18715. Look for places where you declare a parameter as other than the "widened" type
  18716. (the type actually passed). If the function stores values too large for the
  18717. declared type, you will have a quiet change. If the function takes the address
  18718. of that parameter, you may well have a quiet change. Surprisingly, however,
  18719. the change often has no effect.
  18720. "A macro that relies on formal parameter substitution within a string literal
  18721. will produce different results."
  18722. For example,
  18723. #define pr(msg) printf("msg\n")
  18724. Standard C will not alter the string when it expands the macro. Some earlier
  18725. implementations would do so.
  18726. The folks at Berkeley decided that macros should be able to generate tailored
  18727. string literals. Consequently, they adopted the convention that a macro
  18728. parameter name within a string literal should be replaced by the actual
  18729. parameter. (This happens only for string literals written as part of the
  18730. expansion text of a macro, never outside macro expansions.)
  18731. There was general support for such a mechanism among members of the committee.
  18732. Some of us, however, objected to this particular approach. (I was the loudest
  18733. of the objectors.) It would mean greatly complicating the lexical description
  18734. of string literals. They were already complicated enough with escape sequences
  18735. and string concatenation. Adding the concept of embedded identifiers was
  18736. appalling.
  18737. Instead, the committee adopted a new convention. Any parameter name you
  18738. precede with a # gets turned into a string literal. With string concatenation,
  18739. you can paste the "stringized" parameter into a larger string. The capability
  18740. is the same, just the machinery differs. The example above must be changed to
  18741. #define pr(msg) printf(#msg "\n")
  18742. Nevertheless, any program that depends upon this practice will suffer a quiet
  18743. change.
  18744. "A program which relies on size-0 allocation requests returning a non-null
  18745. pointer will behave differently."
  18746. For example,
  18747. sscanf("%u", &size);
  18748. p = malloc(size);
  18749. If size is zero, the behavior is now undefined. The value stored in p may be a
  18750. null pointer, or the program may abort, for example.
  18751. An unfortunate schism developed within the committee over the proper behavior
  18752. of malloc(0). Should this produce a non-null pointer to an object of zero
  18753. size? Or should it be considered invalid so that an implementation must
  18754. diagnose it (perhaps at run time)? It is a religious issue, touching on
  18755. people's basic beliefs as to what constitutes elegant behavior. Like most
  18756. religious issues, many bystanders quickly tire of arguments on either side of
  18757. the matter. That made it all the more difficult for the committee to achieve
  18758. an informed resolution.
  18759. The net result was that both sides lost. The "compromise" was to label
  18760. malloc(0) undefined behavior. This means that programmers can't depend on its
  18761. working right. They also can't depend on its being diagnosed. A program that
  18762. allocates objects of varying size may now suffer a quiet change. Where once it
  18763. could handle the occasional zero-size object without special code, now it can
  18764. fail.
  18765.  
  18766.  
  18767. Conclusion
  18768.  
  18769.  
  18770.  
  18771. If you look back over this set of quiet changes, you will find much cause for
  18772. hope. The hope is that nearly all of the changes need not remain quiet. A good
  18773. translator can have extra checks to look for these cases and emit warning
  18774. messages. Only the last, concerning zero-size objects, must be augmented by
  18775. run-time checking.
  18776. I have not yet heard of an implementation that offers to check for quiet
  18777. changes. One that does will be a valuable migration tool. (You don't want the
  18778. checks turned on all the time, only for old C code that you are upgrading.)
  18779. Vendors please take note.
  18780.  
  18781.  
  18782.  
  18783.  
  18784.  
  18785.  
  18786.  
  18787.  
  18788.  
  18789.  
  18790.  
  18791.  
  18792.  
  18793.  
  18794.  
  18795.  
  18796.  
  18797.  
  18798.  
  18799.  
  18800.  
  18801.  
  18802.  
  18803.  
  18804.  
  18805.  
  18806.  
  18807.  
  18808.  
  18809.  
  18810.  
  18811.  
  18812.  
  18813.  
  18814.  
  18815.  
  18816.  
  18817.  
  18818.  
  18819.  
  18820.  
  18821.  
  18822.  
  18823.  
  18824.  
  18825.  
  18826.  
  18827.  
  18828.  
  18829.  
  18830.  
  18831.  
  18832.  
  18833.  
  18834.  
  18835.  
  18836.  
  18837.  
  18838.  
  18839.  
  18840.  
  18841. Dr. C's Pointers(R)
  18842.  
  18843.  
  18844. Void Pointers, Jump Tables, And Friends
  18845.  
  18846.  
  18847.  
  18848.  
  18849. Rex Jaeschke
  18850.  
  18851.  
  18852. Rex Jaeschke is an independent computer consultant, author and seminar leader.
  18853. He participates in both ANSI and ISO C Standards meetings and is the editor of
  18854. The Journal of C Language Translation, a quarterly publication aimed at
  18855. implementers of C language translation tools. Readers are encouraged to submit
  18856. column topics and suggestions to Rex at 2051 Swans Neck Way, Reston, VA, 22091
  18857. or via UUCP at uunet!aussie!rex.
  18858.  
  18859.  
  18860. This column started out as an example of using a void pointer to point at an
  18861. object having one of a set of possible types. The intent was to also record
  18862. the type to which it currently pointed and to discuss an efficient way of
  18863. accessing the underlying object at a later time. I did achieve these goals but
  18864. along the way, digressed into a number of other interesting areas as you shall
  18865. see.
  18866.  
  18867.  
  18868. Void Pointers
  18869.  
  18870.  
  18871. ANSI C adopted the notion of a generic pointer from C++. A generic pointer is
  18872. declared as void *pv and may point to any object or function. C does not
  18873. require pointers to different types to have the same representation, and on
  18874. word and segmented architectures there are often two or more different pointer
  18875. representations. Since a generic pointer must be able to store an address with
  18876. the smallest resolution and a char is the smallest addressable object in C, a
  18877. void pointer must be at least as big as a char pointer. (In fact, ANSI C
  18878. requires they have exactly the same representation.)
  18879. A void pointer may contain any arbitrary address and at different times, could
  18880. point to a char, a double, or a structure of some type, for example. A void
  18881. pointer does not record any information about the object (or function) to
  18882. which it currently points -- it is the programmer's responsibility to keep
  18883. track of this (just like the current contents of a union).
  18884. Since the compiler knows nothing about the object (or function) to which a
  18885. void pointer points, such a pointer has several restrictions: You cannot
  18886. dereference a void pointer, and you cannot do arithmetic on it -- both
  18887. operations require knowledge of the underlying object type.
  18888.  
  18889.  
  18890. The Linked List Problem
  18891.  
  18892.  
  18893. In some applications, it is useful to have a linked list where each link
  18894. describes an object whose type may be different from that of other objects
  18895. described by other links. An example might be a linked list of device control
  18896. blocks in an operating system. The format of control blocks for different
  18897. devices will likely vary.
  18898. If each link in a list has a different format, how can the linked list be
  18899. declared? This would require the forward and backward pointers (assuming a
  18900. doubly-linked list) in a link to be of type void * (so they could point to any
  18901. object type) and it would also require a flag in the link to indicate the type
  18902. of the forward and backward objects. This can be cumbersome particularly if a
  18903. link points to more than two places. (You may have multiple linked lists
  18904. linked to each other, for example.)
  18905. The approach I have taken is somewhat similar but it avoids the flag field for
  18906. forward and backward pointers. Essentially, each link contains a void pointer
  18907. that points to some underlying object and the type of that object is stored in
  18908. a field in the link. When you point to an object of type A, the flag is
  18909. updated to record that. And a special flag value is used to indicate when the
  18910. pointer does not point to an object.
  18911.  
  18912.  
  18913. The Switch Solution
  18914.  
  18915.  
  18916. There are several ways to implement the code. One uses the switch construct,
  18917. as in Listing 1.
  18918. The five macros TYPE* represent the five possible states of the flag field
  18919. objtype. Their values must be distinct but otherwise are immaterial, as is the
  18920. fact that objtype is unsigned -- it could just as easily have been signed.
  18921. To simplify the example, I have allocated space for only one link and have
  18922. ignored the possibility of malloc returning NULL.
  18923. Each link contains a pointer (pfwd) to the next link in the list, a pointer
  18924. (pbwd) to the previous link, a pointer (pobject) to some generic object, and a
  18925. flag (objtype) indicating the type of the object to which pobject points.
  18926. The link is initialized to point to a double object and the forward and
  18927. backward pointers are set to NULL to indicate the end of the list. You might
  18928. well ask, "Why not use calloc to allocate the space so it's initialized and
  18929. the pointers need not be set to NULL in the program?"
  18930. Certainly, that approach will succeed on some systems. However, it is not
  18931. maximally portable. calloc initializes the allocated space to all-bits-zero
  18932. and while that's the representation of integer zero it need not represent
  18933. floating-point zero, or in this case, the null pointer constant. ANSI C does
  18934. not require that NULL be represented internally as all-bits-zero. It simply
  18935. requires that the null pointer be a value that is not the address of an object
  18936. or function, and that comparing and assigning pointers with integer zero
  18937. actually works.
  18938. Once the list has been constructed, it becomes necessary to process the
  18939. objects to which each link points. The problem here is how to do this
  18940. efficiently? In this example, the switch construct is used and the correct
  18941. answer is produced -- the link does indeed point to a double object. But is
  18942. this approach efficient?
  18943. The real question comes down to "How is a switch implemented?" ANSI C does not
  18944. specify this; it simply requires that in the absence of a break statement (or
  18945. similar) that you drop through from one case to the next. And if each case is
  18946. mutually exclusive, the ordering of the cases (including default) can be
  18947. arbitrary.
  18948. A lot of programmers believe (not necessarily for any good reason) that the
  18949. order in which they specify the case labels, is important. This may or may not
  18950. be true depending on your implementation and the set of case label values. For
  18951. example, if the set of labels is dense (as in this case), the compiler might
  18952. generate a jump table of addresses. (It might even be able to take advantage
  18953. of a hardware case instruction such as exists on the VAX.) Certainly, this
  18954. would make for efficient code.
  18955. If, on the other hand, the set of label values is sparse the compiler may
  18956. generate a series of nested if/else constructs and it may do them in the order
  18957. in which the cases are specified, the reverse order, or possibly in some other
  18958. order. (As an exercise, if your compiler can produce a machine-code listing,
  18959. do so for each of the three solutions shown in this article. Compare the code
  18960. generated for the switch, if/else, and jump table approaches.)
  18961. The bottom line is that you are never guaranteed (by C) that the first case is
  18962. tested for before the second (and the third, etc.) so specifying the most
  18963. common case label value first need not be the most efficient approach. And if
  18964. nested if/elses are used, the number of tests made to resolve the branch will
  18965. be proportional to the number of cases defined.
  18966. The casts are needed since you cannot dereference a void pointer directly.
  18967.  
  18968.  
  18969. The if/else Solution
  18970.  
  18971.  
  18972. Whereas the switch construct provides no guaranteed order of case value
  18973. matching, the if/else construct does. For example:
  18974. if (pnode-objtype == TYPECHAR)
  18975. printf ("char: %c\n", *(char *)pnode-pobject);
  18976. else if (pnode-objtype == TYPEINT)
  18977.  
  18978. printf("int: %d\n", *(int *)pnode-pobject);
  18979. else if (pnode-objtype == TYPELONG)
  18980. printf("long: %ld\n", *(long *)pnode-pobject);
  18981. else if (pnode-objtype == TYPEDOUBLE)
  18982. printf("double: %f\n", *(double *)pnode-pobject);
  18983. else if (pnode-objtype == TYPENONE)
  18984. printf("none:\n");
  18985. Now we have complete control of the order in which the tests are done.
  18986. However, this ordering is fixed and favors those values near the front of the
  18987. set of tests. It also cannot take advantage of any jump table generation the
  18988. compiler might be able to do (unless the compiler has a very, very clever
  18989. optimizer).
  18990.  
  18991.  
  18992. The Jump Table Solution
  18993.  
  18994.  
  18995. It so happens this problem can be solved and in a manner that involves no
  18996. priority of testing. That is, the underlying object can be processed
  18997. efficiently without regard to its type. (More correctly, I should say the code
  18998. to do the processing is dispatched without favoritism.) Of course, there are
  18999. always trade-offs and in this case, the code to process each object type must
  19000. be in a function. That is, we must call a function to do the work whereas with
  19001. the switch and if/else approaches, the work could be done inline(Listing 2).
  19002. The key to the solution is the object funtable. It's an array of five objects
  19003. each of which is a pointer to a function that has no return value and has one
  19004. argument, of type pointer to void. The array is initialized with the addresses
  19005. of the five object type processing functions pro*. An array of function
  19006. pointers is often referred to as a jump table.
  19007. It is absolutely critical that the order of the initializer expressions for
  19008. funtable exactly match the values assigned in the TYPE* macros since we will
  19009. use these macros to index into the funtable array. That is, the macro
  19010. corresponding to pronone (TYPENONE) must have a value of zero since that is
  19011. the first subscript value.
  19012. The expression
  19013. (*funtable[pnode-objtype])
  19014. (pnode-pobject);
  19015. actually dispatches the type processing code. Following the operator
  19016. precedence table, funtable is first subscripted using the type flag giving the
  19017. address of the appropriate function. Then that function is called with the
  19018. generic address of the underlying object being passed as the only argument.
  19019. Regardless of the number of possible values for pobject, you only need this
  19020. one statement to call the processing function -- all type processing functions
  19021. take equally long to dispatch since they all require one lookup in funtable.
  19022. To change the number of types, you simply need to define the new processing
  19023. functions and add them to the table initializer list. The concept of
  19024. controlling the order in which types are tested for no longer exists since
  19025. using the flag as a subscript you intuitively know the function to be used
  19026. each time.
  19027. The messy looking cast expressions are still present in each processing
  19028. function. Why couldn't proint (for example) be defined as
  19029. void proint(int *parg)
  19030. {
  19031. printf("int: %d\n", *parg);
  19032. }
  19033. instead of
  19034. void proint(void *parg)
  19035. {
  19036. printf("int: %d\n", *(int *)parg);
  19037. }
  19038. Again, this may work on some systems but, according to ANSI C, the behavior is
  19039. undefined. Specifically, in main a void pointer is passed yet in proint an int
  19040. pointer is expected. As stated earlier, these two pointer types are not
  19041. required to have the same size and representation. (On a word machine such as
  19042. a Cray supercomputer such mismatching will likely result in the wrong answer
  19043. for all characters except the first in a given memory word.)
  19044. The function prochar is a special case since it could be defined to expect a
  19045. char pointer. And since char pointers and void pointers are required to have
  19046. the same representation, this would work. However, in both cases (proint and
  19047. prochar) the formal argument list in the definition would not match the
  19048. prototypes for these functions. And if you change the prototypes to match, the
  19049. table initializer will be erroneous. By definition, every function pointer
  19050. must point to a function having the same argument list as well as return type.
  19051. You could bypass the strict checking rules by leaving the argument information
  19052. out of the table declaration but this still won't help you. In the absence of
  19053. a prototype in the table declaration, the actual void pointer argument will be
  19054. passed as is, giving rise to the mismatch problem with the formal argument as
  19055. discussed earlier.
  19056. In short, the functions must all have the exact same argument list thus
  19057. requiring the explicit cast before dereferencing. Even pronone must have an
  19058. argument despite the fact it is never used.
  19059. Just what is the cost of a cast anyway? None at all on systems where all
  19060. pointers are created equal. (This is typically the case on byte architectures
  19061. having a linear address space.) On word and segmented architectures, most
  19062. pointer conversions are also nonevents except where either the cast operand or
  19063. the cast type is a char (or possibly short int) pointer. So don't be too
  19064. concerned about the cast generating code.
  19065. It was implied earlier that requiring each type's processing code to be a
  19066. function might be inefficient since we have added the overhead of calling a
  19067. function. Depending on this cost, it may or may not be significant. Also, an
  19068. increasing number of compilers are adding the ability to automatically inline
  19069. functions in each place they are called. (VAX C recently added this in V3 and
  19070. C++ supports it explicitly using the inline keyword.)
  19071.  
  19072.  
  19073. Enumerations Versus Macros
  19074.  
  19075.  
  19076. In all three solutions, macros were used to come up with a set of unique
  19077. integer values. The same result can be achieved using an enumerated type as
  19078. follows:
  19079. enum {TYPENONE, TYPECHAR,
  19080. TYPEINT,
  19081. TYPELONG, TYPEDOUBLE};
  19082. Not only do we get a set of unique int values, they also start at zero (as
  19083. required by the jump table approach). And we are relieved from having to
  19084. assign the numbers explicitly.
  19085. Regarding the spelling of the enumerations constant identifiers; should they
  19086. be in upper- or lower-case? If you follow the rule "All upper-case for macros
  19087. and all lower- or mixed case for other identifiers" then they should be all
  19088. lower-case. When I see an identifier written in upper-case I immediately
  19089. understand that identifier might expand into an arbitrarily complex expression
  19090. and I should take care how it's used. Since an enumeration constant "expands"
  19091. to a simple integer constant the connotations of spelling it in upper-case are
  19092. unwarranted. In the final analysis though, I don't think your choice will have
  19093. significant stylistic ramifications.
  19094. One final thing about the enum declaration; it has no tag and as such, no
  19095. objects can later be declared to have that type. And although tagless
  19096. structure and union declarations declared like this are useless, this is not
  19097. so for enumerations. The scope of the enumeration constants declared inside
  19098. the braces goes beyond the use of objects of that enumerated type. These
  19099. constants have type int and can be used even though no enumerated objects of
  19100. that type are actually declared. From my experience, enums are mostly used in
  19101. just this manner.
  19102.  
  19103. Listing 1
  19104. #include <stdio.h>
  19105. #include <stdlib.h>
  19106.  
  19107. /* structure type flag values */
  19108.  
  19109. #define TYPENONE 0 /* Not pointing at an object */
  19110. #define TYPECHAR 1 /* char */
  19111. #define TYPEINT 2 /* int */
  19112.  
  19113. #define TYPELONG 3 /* long */
  19114. #define TYPEDOUBLE 4 /* double */
  19115.  
  19116. struct node {
  19117. struct node *pfwd; /* forward ptr */
  19118. struct node *pbwd; /* backward ptr */
  19119. void *pobject; /* ptr to object */
  19120. unsigned int objtype; /* indicate object type */
  19121. };
  19122.  
  19123. main()
  19124. {
  19125. char c = 'A';
  19126. int i = 10;
  19127. long int 1 = 123456;
  19128. double d = 123.45;
  19129. struct node *pnode;
  19130.  
  19131. pnode = malloc(sizeof(struct node));
  19132.  
  19133. /* let's point to a double */
  19134.  
  19135. pnode->pobject = &d;
  19136. pnode->objtype = TYPEDOUBLE;
  19137. pnode->pfwd = NULL;
  19138. pnode->pbwd = NULL;
  19139.  
  19140. /* at a later point, let's process the object to which we point */
  19141.  
  19142. switch (pnode->objtype) {
  19143.  
  19144. case TYPECHAR:
  19145. printf ("char: %c\n", *(char *)pnode->pobject);
  19146. break;
  19147.  
  19148. case TYPEINT:
  19149. printf("int: %d\n", *(int *)pnode->pobject);
  19150. break;
  19151.  
  19152. case TYPELONG:
  19153. printf("long: %ld\n", *(long *)pnode->pobject);
  19154. break;
  19155.  
  19156. case TYPEDOUBLE:
  19157. printf("double: %f\n", *(double *)pnode->pobject);
  19158. break;
  19159.  
  19160. case TYPENONE:
  19161. printf ("none:\n");
  19162. break;
  19163. }
  19164. }
  19165.  
  19166. The output generated by this program is:
  19167. double: 123.450000
  19168.  
  19169.  
  19170. Listing 2
  19171. #include <stdio.h>
  19172.  
  19173. #include <stdlib.h>
  19174.  
  19175. void prochar(void *parg);
  19176. void proint(void *parg);
  19177. void prolong(void *parg);
  19178. void prodouble(void *parg);
  19179. void pronone(void *parg);
  19180.  
  19181. /* structure type flag values */
  19182.  
  19183. #define TYPENONE 0 /* Not pointing at an object */
  19184. #define TYPECHAR 1 /* char */
  19185. #define TYPEINT 2 /* int */
  19186. #define TYPELONG 3 /* long */
  19187. #define TYPEDOUBLE 4 /* double */
  19188.  
  19189. struct node {
  19190. struct node *pfwd; /* forward ptr */
  19191. struct node *pbwd; /* backward ptr */
  19192. void *pobject; /* ptr to object */
  19193. unsigned int objtype; /* indicate object type */
  19194. };
  19195.  
  19196. main()
  19197. {
  19198. char c = 'A';
  19199. int i = 10;
  19200. long int 1 = 123456;
  19201. double d = 123.45;
  19202. static void (*funtable[])(void *parg) = {
  19203. pronone,
  19204. prochar,
  19205. proint,
  19206. prolong,
  19207. prodouble
  19208. };
  19209. struct node *pnode;
  19210.  
  19211. pnode = malloc(sizeof(struct node));
  19212.  
  19213. /* let's point to a double */
  19214.  
  19215. pnode->pobject = &d;
  19216. pnode->objtype = TYPEDOUBLE;
  19217. pnode->pfwd = NULL;
  19218. pnode->pbwd = NULL;
  19219.  
  19220. /* at a later point, let's process the object to which we point */
  19221.  
  19222. (*funtable[pnode->objtype])(pnode->pobject);
  19223. }
  19224.  
  19225. /* processing functions */
  19226. void prochar(void *parg)
  19227. {
  19228. printf("char: %c\n", *(char *)parg);
  19229. }
  19230.  
  19231. void proint(void *parg)
  19232.  
  19233. {
  19234. printf("int: %d\n", *(int *)parg);
  19235. }
  19236.  
  19237. void prolong(void *parg)
  19238. {
  19239. printf("long: %ld\n"; *(long *)parg);
  19240. }
  19241.  
  19242. void prodouble(void *parg)
  19243. {
  19244. printf("double: %f\n", *(double *)parg);
  19245. }
  19246.  
  19247. void pronone(void *parg)
  19248. {
  19249. printf("none:\n");
  19250. }
  19251.  
  19252.  
  19253.  
  19254.  
  19255.  
  19256.  
  19257.  
  19258.  
  19259.  
  19260.  
  19261.  
  19262.  
  19263.  
  19264.  
  19265.  
  19266.  
  19267.  
  19268.  
  19269.  
  19270.  
  19271.  
  19272.  
  19273.  
  19274.  
  19275.  
  19276.  
  19277.  
  19278.  
  19279.  
  19280.  
  19281.  
  19282.  
  19283.  
  19284.  
  19285.  
  19286.  
  19287.  
  19288.  
  19289.  
  19290.  
  19291.  
  19292.  
  19293.  
  19294.  
  19295.  
  19296. Questions & Answers
  19297.  
  19298.  
  19299. More On Keyboard Routines, A Preprocessor Puzzler
  19300.  
  19301.  
  19302.  
  19303.  
  19304. Kenneth Pugh
  19305.  
  19306.  
  19307. Kenneth Pugh, a principal in Pugh-Killeen Associates, teaches C language
  19308. courses for corporations. He is the author of C Language for Programmers and
  19309. All On C, and is a member on the ANSI C committee. He also does custom C
  19310. programming for communications, graphics, and image databases. His address is
  19311. 4201 University Dr., Suite 102, Durham, NC 27707.
  19312.  
  19313.  
  19314. You may fax questions for Ken to (919) 493-4390. When you hear the answering
  19315. message, press the * button on your telephone. Ken also receives email at
  19316. kpugh@dukeac.ac.duke.edu (Internet) or dukeac!kpugh (UUCP).
  19317.  
  19318.  
  19319. Q
  19320. I am writing a program using Microsoft C v5.1 that connects to a mainframe
  19321. session to execute an on-line mainframe application. I used the High Level
  19322. Language Application Programming Interface (HLLAPI) to do the terminal
  19323. emulation.
  19324. One of the options that I am trying to incorporate into this program is the
  19325. ability to toggle a debug switch on or off that will print certain information
  19326. to an audit file. My intention is to use a function key for this purpose. When
  19327. I send a string of keystrokes to the mainframe session using HLLAPI, these
  19328. characters are placed in the keyboard buffer. If I press a function key, this
  19329. is also placed in the keyboard buffer and sent with the other characters to
  19330. the mainframe session, thus messing up the mainframe command in addition to
  19331. losing the function key before the program finally gets around to testing to
  19332. see if the function key has been pressed.
  19333. In IBM Compiler BASIC, the ON KEY (x) command would handle this situation,
  19334. presumably by doing an interrupt whenever the appropriate function key is
  19335. pressed. I have not been able to find a similar prewritten function in C.
  19336. Therefore, I have been attempting to learn how to code an interrupt, but have
  19337. not been able to find a really good book that explains them well. From the
  19338. bits and pieces I have been able to find, I think that I need to change
  19339. interrupt 9 or 15 or 16, but can't figure out which one or how. What I need is
  19340. an example of how to code this interrupt and a good book on the subject (one
  19341. that uses C code for examples, not assembler, if possible).
  19342. Mike Drew
  19343. Woodridge, IL
  19344. A
  19345. You could write a TSR that intercepts the keyboard interrupt, tests the key,
  19346. and either sets a debug flag or passes it on. The Blaise toolkit provides an
  19347. easy way of creating a TSR, although there is a bit more overhead than what
  19348. you require for your one operation. That's the simple approach, with no
  19349. assembler required.
  19350. Microsoft provides an "interrupt" modifier for a function type which allows it
  19351. to be a handler for an interrupt. It receives as parameters the values of the
  19352. register when the interrupt was called. To handle an interrupt you:
  19353. 1. create a function to perform the desired service with the interrupt type
  19354. modifier.
  19355. 2. call_dos_getvect() to get the current interrupt handler's address
  19356. 3. call_dos_setvect() to set the interrupt location to your function's address
  19357. 4. call_chain_intr() to chain to the previous interrupt's address.
  19358. For your keyboard routine, you want to connect to the key I/O interrupt 0x16.
  19359. The characters typed on the keyboard are handled by the keyboard interrupt
  19360. routines (0x9) and are placed in a circular buffer. When the key I/O routine
  19361. is called by a user program (such as by kbhit() or getch()), it checks the
  19362. keyboard buffer for the next character to return.
  19363. Using far pointers, your function can look at this circular buffer and
  19364. eliminate your hot key, if it is in there. The offset of the head of the
  19365. buffer is at 0040:001A, the tail offset is at 0040:001C, and the offset of the
  19366. end is at 0040:001C. The offsets are relative to segment 0040. Note that these
  19367. addresses are only good for IBM-PC compatibles. The IBM Technical Reference
  19368. Manual gives the full information, but unfortunately it is documented in
  19369. comments to assembly code.
  19370. Q
  19371. I am currently using Turbo C v2.0 with MS-DOS v2.2 on my Zenith PC. However, I
  19372. also own an older KayPro portable equipped with a Z-80 processor and running
  19373. CPM 2.2. At the present time finances preclude replacing the KayPro with an
  19374. MS-DOS battery powered laptop.
  19375. As a result, I am searching for a CPM/Z-80 C compiler for the KayPro, that
  19376. would allow me to develop C code on the PC and then recompile and run it on
  19377. the KayPro. In addition I am not willing to pay a large price for the CPM
  19378. compiler because of its memory and speed limits. If you have information
  19379. regarding CPM/Z-80 C compilers, I would appreciate your response.
  19380. Ronald L. Nave
  19381. Horsham, PA
  19382. A
  19383. I used to use the Manx Aztec Compiler to perform exactly what you are talking
  19384. about. In addition, I also ported the C code from the IBM-PC to an Apple II.
  19385. Although you could use the latest version of the compiler on the PC, it might
  19386. be better to use an old version that matches the version available for CP/M.
  19387. (None of the recent ANSI features are available in the CP/M version.) If you
  19388. use the older version on both machines, you will not be tempted to use some of
  19389. the more modern features, such as structure assignment. This version may be
  19390. available from Manx or on the used compiler market.
  19391. Q
  19392. We are using Microsoft C v5.0. We would like to define a manifest constant to
  19393. be used both as a number and as part of a message string. For example:
  19394. #define Max 10
  19395.  
  19396. extern short Value;
  19397. extern DoMsg(char *Msg)
  19398.  
  19399. if (Value > Max)
  19400. DoMsg(<what goes here?>);
  19401. One way to do this is to itoa() the constant and assemble the message (at
  19402. runtime), (or printf could be made to do this), but we thought we could get
  19403. the preprocessor to do this sort of thing. All our attempts -- pasting,
  19404. stringizing, etc. -- have been foiled. For example, use of the stringizer,
  19405. "#", suppresses macro expansion, so the constant comes through unsubstituted.
  19406. If we set up a pasting macro, there's no way (that we could find) to paste
  19407. quotes onto something. The point of all this is to be able to have just one
  19408. manifest constant to change if the value changes, and have that apply to both
  19409. value uses and string uses within the module. Help!
  19410. Josh Cohen
  19411. Stuart Downing
  19412. Dexter, MI 48130
  19413. A
  19414. You got me on this one. I had assumed that the following would have worked, as
  19415. you tried, till I read the ANSI standard again.
  19416. #define MAX 10
  19417. #define STRING_WITH_VALUE(X)"Error message " #X
  19418.  
  19419.  
  19420. DoMsg(STRING_WITH_VALUE(MAX))
  19421. The # operator is a new ANSI preprocessor operator that only works as part of
  19422. a #define with tokens (a function-like macro). It is a "stringizing" operator.
  19423. It forms a string literal (a set of characters in quotes) from the token that
  19424. follows. However the token is not processed for replacement. Thus the output
  19425. of the preprocessor looks like:
  19426. DoMsg("Error message " "MAX")
  19427. and not
  19428. DoMsg("Error message " "10")
  19429. In the first case, the implicit concatenation of string literals in ANSI C
  19430. yields the following:
  19431. DoMsg("Error message MAX")
  19432. If the output of the preprocessor had been the second case, then you would
  19433. have gotten exactly what you wanted.
  19434. Replacement of #define names is not supposed to occur within a string literal.
  19435. The operation of the # operator appears to be consistent with that philosophy.
  19436. Perhaps a reader can solve this problem for us. A solution would certainly be
  19437. useful. For example, I have been meaning to rewrite quite a bit of code to
  19438. look something like:
  19439. #define WFIRST_NAME 20
  19440. #define WLAST_NAME 30
  19441. struct s_record
  19442. {
  19443. char first_name[WFIRST_NAME];
  19444. char last_name[WLAST_NAME];
  19445. ...
  19446. };
  19447. struct s_record record;
  19448. ...
  19449. #define quote(x) #x
  19450.  
  19451. printf("\n Record is %" quote(WFIRST_NAME) "s %"
  19452. quote (WSECOND_NAME) "s",
  19453. record.first_name, record.last_name);
  19454. which would yield (if it worked):
  19455. printf("\n Record is %20s %30s",
  19456. record.first_name, record.last_name);
  19457. One alternative is:
  19458. #define MAX_BUFFER 200 /* Or whatever */
  19459. char DoMsgBuffer[MAX_BUFFER];/* Local or Global buffer */
  19460. #define DoMsgInt(string, integer) \
  19461. { \
  19462. sscanf(DoMsgBuffer, "%s %d", string, integer); \
  19463. DoMsg(DoMsgBuffer); \
  19464. }
  19465. Now instead of calling DoMsg directly, you call;
  19466. DoMsgInt("Error message", MAX);
  19467. This at least eliminates having to change two constants.
  19468. An uglier alternative uses a separate quote constant with lots of comments
  19469. about changing both.
  19470. #define MAX 10 /* If you change this, change S_MAX
  19471. to match - or else */
  19472. #define S_MAX "10"
  19473. DoMsg("Error Message" S_MAX);
  19474. Q
  19475. This month's C Users Journal (Nov. 89) contains an article by P.J. Plauger
  19476. describing the print facilities in Standard C. His review of these commands
  19477. brings back a gripe I have long held about C, which I wish to raise.
  19478. I was brought up as a Fortran programmer, and was weaned on its FORMAT
  19479. statements. Say what you like, Fortran allows a form which is much missed by
  19480. those of us who have to format complicated output, and that is the "repeat"
  19481. format construct. Thus in Fortran, a format like 10I3 would replace a lot of
  19482. writing in Standard C. Even more useful are statements like 4(3I2,2X,4F6.4),
  19483. which formats 28 variables in 15 characters, and even adds blank spaces where
  19484. desired.
  19485. I am well aware that no two languages are equivalent, but it would seem to me
  19486. that somebody by now has worked out a scheme to allow multiple format
  19487. statements of similar types to be written with less difficulty than Standard C
  19488. allows. What do the gurus say to this?
  19489. David Tal
  19490. Haifa, Israel
  19491. A
  19492. I am an ancient Fortran programmer also, or should I say, one who used Fortran
  19493. in relative antiquity. One of the least desirable aspects of the FORMAT
  19494. statements was having to count the number of characters (e.g. 8HVALUE IS) for
  19495. character output. Because this was the syntax for characters, the leading
  19496. digits on a format specifier (I, X, F) could be used as a repetition count.
  19497. Where the language does not feature a particular item you are interested in,
  19498. you can always come up with your own version.
  19499. With the printf format specifier, everything that is not preceded by a % is
  19500. output as a literal character. You might come up with a scheme such as: "%r%Z"
  19501. where r is a repetition count (an integer) for the next format specifier. You
  19502. might even use "%r (x)" where the x represents a string that will be repeated
  19503. r number of times.
  19504. All you need to do is write a routine something like:
  19505. char *repeat_format(format)
  19506. char *format;
  19507. which you would use in a printf such as:
  19508. printf(repeat_format("%5%3d %2(%d ABC)",a,b,c,d,e,f,g,h);
  19509.  
  19510. and which would print the equivalent of:
  19511. printf("%3%3d%3d%3d%3d %d ABC %d ABC",a,b,c,d,e,f,g,h);
  19512. The routine might be easier if you always required the parenthesis. Otherwise
  19513. it will have to determine where the end of a simple format specifier is. The
  19514. function would need an internal buffer, whose address it could return.
  19515.  
  19516.  
  19517. Reader Requests
  19518.  
  19519.  
  19520. I have a question which concerns especially the IBM PC but maybe you know a
  19521. solution for my problem.
  19522. In writing a special data transfer program for an IBM PS and some
  19523. telecommunications equipment I encountered the following task: Is there any
  19524. way to determine whether there is a mouse connected to a specific serial COM
  19525. port? Of course I know how to detect the presence of the mouse driver (Int
  19526. 33h, function 0). But this doesn't tell me which port is used!
  19527. Thank you, and I hope to receive a reply soon.
  19528. Michael Wiedmann
  19529. West Germany
  19530. That is an interesting problem. The mouse driver that I use does not seem to
  19531. check for presence of a mouse on the COM port. It assumes that one is there
  19532. and displays a random position. Anyone have an answer? (KP)
  19533. I am a new subscriber to The C Users Journal. I found the article "Pointer
  19534. Arithmetic at Memory Segment Boundaries" by D. and N. Saks (Vol.7 #7 pp. 27)
  19535. very informative.
  19536. I'm interested in writing applications in C that use expanded memory to store
  19537. variables and data. I'm also interested in finding information regarding
  19538. writing applications in C that can run in expanded memory. Can you direct me
  19539. to sources of such information?
  19540. Thank you for your time and continued success with The C Users Journal.
  19541. Phil Pistone
  19542. Chicago, IL
  19543. The Intel/Lotus specifications give the full details on the interface (KP).
  19544. Anyone have suggestions as to a readable version?
  19545. (Ed. Note: You might find what you're after in two articles, both in old
  19546. Microsoft Systems Journals: "Expanded Memory: Writing Programs That Break the
  19547. 640K Barrier", M. Hansen, Krueger, B., and Stuecklen, N., March 1987, p. 21;
  19548. and "Extended Memory Specification 2.x: Taking Advantage of the 80286
  19549. Protected Mode", Chip Anderson, July 1989, p. 17.--rlw)
  19550.  
  19551.  
  19552. Reader Responses:
  19553.  
  19554.  
  19555. Here is some information for Jeff Saraiva's questions concerning graphics and
  19556. the MSC graphics library that was published in your November issue. Sample
  19557. Programs:
  19558. I have not seen any extensive programs that use the MSC graphics library. Two
  19559. good books that I have used are: Advanced Graphics in C and Graphics
  19560. Programming in C. The authors develop their own graphics library, but the
  19561. concepts are the same and it should be an easy matter of replacing calls to
  19562. these libraries to the MSC graphics library routines.
  19563. UNIX Tests:
  19564. You pretty much named the worthwhile ones.
  19565. TIFF Information:
  19566. The May 1988 issue of Dr. Dobb's Journal has an excellent article about TIFF
  19567. starting on page 26. The article has an address to which you can write for the
  19568. source code and documentation to a TIFF library that reads and writes TIFF
  19569. files. The code and documentation is free. There are two libraries; one for
  19570. the MAC and one for the PC. I have used both of these and they work very well.
  19571. The documentation is very complete.
  19572. Ray-tracing Algorithms:
  19573. Sorry, can't help you on this one.
  19574. Joseph K. Vossen
  19575. Duluth, GA
  19576. Printscreen:
  19577. In your "Q?/A!" column in The C Users Journal, v7n8, you suggested that Roger
  19578. Glocke kill the "printscreen" key by replacing the vector for INT 5. That is
  19579. the hard way, and (as you point out) can lead to trouble.
  19580. The easy way is to set PrtScr's data byte (at address 0050:0000) to "busy" so
  19581. the ROM BIOS interrupt handler will ignore the key stroke. The attached
  19582. listing (Listing 1) illustrates the technique with a small stand-alone utility
  19583. to set the byte or restore it to "not busy." To use the technique embedded in
  19584. another program: Initialize PrtScr as a static far pointer as shown, assign -1
  19585. to the data byte (*PrtScr) at the beginning of main(), and assign 0 to it
  19586. before exit (it is safest to use a local function called by atexit() for
  19587. this).
  19588. Turbo C has the MK_FP macro (in dos. h), which could have been used to
  19589. initialize the far pointer in line 12. However, Mr. Glocke said he uses
  19590. Microsoft C, which does not provide this macro. The construction shown should
  19591. work with Microsoft C.
  19592. Murray L. Lesser
  19593. Yorktown Heights, NY
  19594. I had mixed emotions about seeing my letter in your column of the November
  19595. issue of The C Users Journal that arrived just today.
  19596. I was pleased because I learned something from your very lucid discussion of
  19597. the problem raised in my letter. I was a little embarrassed because within a
  19598. week after I wrote the letter the problem was solved. A discussion with Fred
  19599. Crigger of Watcom Products, Inc., revealed that the solution was to write some
  19600. string functions that would accept far pointers to data items and return near
  19601. pointers to data items, and vice versa.
  19602. I wrote the necessary functions, virtually all the string functions in the
  19603. standard library, and made the appropriate changes in the program. As of this
  19604. moment everything is working very well and I am rather proud of my efforts.
  19605. Incidentally, I have switched completely to Watcom C v7.0 and am delighted
  19606. with it. It does compile slowly but yields .exe files that are consistently
  19607. two-thirds the size of those produced by Microsoft. And, the people at Watcom
  19608. have been very generous with their support.
  19609. I do appreciate both your going to the trouble to answer my letter and your
  19610. column -- I am always better informed after reading it.
  19611. Fred C. McDaniel
  19612. Richardson, Texas
  19613.  
  19614. Listing 1
  19615. KILLIT.C
  19616.  
  19617. /*
  19618. *KILLIT.COM - A small utility to kill or re-enable the PrtScr function
  19619. * Usage: Call with no argument to disable PrtScr function
  19620. Call with any argument to enable PrtScr function
  19621. Written by M.L. Lesser, 10/27/89
  19622. Compiled with Turbo C (TCC) v 2.00, switches -mt -lt -o
  19623.  
  19624. to link as COM file
  19625. */
  19626.  
  19627. #include <MIO><N>.h
  19628.  
  19629. char far *PrtScr = (void far *)((unsigned long)0x50 << 16);
  19630.  
  19631. void main(argc)
  19632. {
  19633. if (argc == 1) /* No arguments on the command line */
  19634. {
  19635. *PrtScr = 1; /* Set PrtScr "busy" signal */
  19636. cprintf("Print screen function has been disabled");
  19637. }
  19638. else
  19639. {
  19640. *PrtScr = 0; /* Set PrtScr <169>available<170> signal */
  19641. cprintf("Print screen function has been enabled");
  19642. }
  19643. }
  19644.  
  19645.  
  19646.  
  19647.  
  19648.  
  19649.  
  19650.  
  19651.  
  19652.  
  19653.  
  19654.  
  19655.  
  19656.  
  19657.  
  19658.  
  19659.  
  19660.  
  19661.  
  19662.  
  19663.  
  19664.  
  19665.  
  19666.  
  19667.  
  19668.  
  19669.  
  19670.  
  19671.  
  19672.  
  19673.  
  19674.  
  19675.  
  19676.  
  19677.  
  19678.  
  19679.  
  19680.  
  19681.  
  19682.  
  19683.  
  19684.  
  19685.  
  19686.  
  19687. Applying C++
  19688.  
  19689.  
  19690. Building A Text Editor, Part 2: Buffers, Sloops, And Yachts
  19691.  
  19692.  
  19693.  
  19694.  
  19695. Tsvi Bar-David
  19696.  
  19697.  
  19698. This article is not available in electronic form.
  19699.  
  19700.  
  19701.  
  19702.  
  19703.  
  19704.  
  19705.  
  19706.  
  19707.  
  19708.  
  19709.  
  19710.  
  19711.  
  19712.  
  19713.  
  19714.  
  19715.  
  19716.  
  19717.  
  19718.  
  19719.  
  19720.  
  19721.  
  19722.  
  19723.  
  19724.  
  19725.  
  19726.  
  19727.  
  19728.  
  19729.  
  19730.  
  19731.  
  19732.  
  19733.  
  19734.  
  19735.  
  19736.  
  19737.  
  19738.  
  19739.  
  19740.  
  19741.  
  19742.  
  19743.  
  19744.  
  19745.  
  19746.  
  19747.  
  19748. Implementer's Notebook
  19749.  
  19750.  
  19751. Life With Static Buffers, Part 2
  19752.  
  19753.  
  19754.  
  19755.  
  19756. Don Libes
  19757.  
  19758.  
  19759. This article is not available in electronic form.
  19760.  
  19761.  
  19762.  
  19763.  
  19764.  
  19765.  
  19766.  
  19767.  
  19768.  
  19769.  
  19770.  
  19771.  
  19772.  
  19773.  
  19774.  
  19775.  
  19776.  
  19777.  
  19778.  
  19779.  
  19780.  
  19781.  
  19782.  
  19783.  
  19784.  
  19785.  
  19786.  
  19787.  
  19788.  
  19789.  
  19790.  
  19791.  
  19792.  
  19793.  
  19794.  
  19795.  
  19796.  
  19797.  
  19798.  
  19799.  
  19800.  
  19801.  
  19802.  
  19803.  
  19804.  
  19805.  
  19806.  
  19807.  
  19808.  
  19809. The HALO Graphics Library
  19810.  
  19811.  
  19812. Victor Volkman
  19813.  
  19814.  
  19815. Victor R. Volkman received a BS in Computer Science from Michigan
  19816. Technological University in 1986. Mr. Volkman is a frequent contributor to The
  19817. C Users Journal and the C Gazette. He is currently employed as Software
  19818. Engineer at Cimage Corporation of Ann Arbor, MI. He can be reached at the HAL
  19819. 9000 BBS, (313) 663-4173, 1200/2400/9600 baud.
  19820.  
  19821.  
  19822. The HALO Graphics Library by Media Cybernetics, Inc. supports
  19823. device-independent graphics programming with more than 200 functions. HALO
  19824. provides device drivers for dozens of vector and bitmap graphics boards, dot
  19825. matrix and laser printers, page scanners and video digitizers, graphics
  19826. tablets, mice, and plotters. Some of the more popular graphics boards
  19827. supported include CGA, EGA, VGA, Extended-VGA, MCGA, PGA, Hercules, and AT&T
  19828. Targa. HALO for DOS lists at $395. HALO for OS/2 lists at $695 and is
  19829. source-code compatible with the DOS version.
  19830. The BARGRAPH application in Listing 1 demonstrates the style and ease with
  19831. which HALO can be integrated with C programs.
  19832.  
  19833.  
  19834. System Requirements
  19835.  
  19836.  
  19837. HALO makes only modest system hardware requirements. It will run on an IBM XT,
  19838. AT, 3270 PC, AT&T 6300 or other true compatible computer with a base memory of
  19839. 256k RAM. The computer must have at least one supported graphics device.
  19840. Additionally, HALO requires MS-DOS v2.1 or later. For software development,
  19841. you must have any one of the supported languages: Microsoft MASM v5.0, BASICA,
  19842. QuickBASIC v4.0, Turbo BASIC v1.0, Lattice C v3.0+, Microsoft C v3.0+, Turbo
  19843. C, Microsoft FORTRAN, Ryan-McFarland FORTRAN, Gold Hills Lisp, Microsoft
  19844. Pascal, or Turbo Pascal v4.0. Development for the BARGRAPH application was
  19845. completed on a 12.5 Mhz AT-compatible with 640K RAM and an Everex EV-640
  19846. graphics card (CGA and Hercules compatible). The BARGRAPH program was compiled
  19847. with Microsoft C 5.1 and linked with the small model HALO library.
  19848. HALO is a graphics kernel system structured like a layercake (see Fig. 1).
  19849. Each layer may only talk to the layers directly above and below it. On the top
  19850. layer is your source application program as written in any supported language
  19851. (C, BASIC, Pascal, etc.). The application program layer contains many function
  19852. call references to HALO . Since each language has its own parameter passing
  19853. mechanism, a language binding layer is needed. The language binding presents
  19854. the function arguments to the graphics kernel in a standard fashion.
  19855. The operations of the graphics functions themselves are split between the
  19856. graphics kernel and device driver layers. The graphics kernel is linked into
  19857. your application program. It performs the device independent functions such as
  19858. polygon drawing, text manipulation, and viewport management. The components in
  19859. the device driver layer speak directly to the hardware. Typical graphic device
  19860. driver functions are vector drawing and bitmap panning. For maximum
  19861. flexibility the device drivers are loaded dynamically.
  19862. HALO supports devices with four types of color palette management: devices
  19863. which have a fixed set of colors that cannot be changed; devices which allow
  19864. you to switch between several predefined palletes; devices which support more
  19865. colors than can be displayed at one time (e.g. IBM EGA); and devices which
  19866. support a programmable palette. (In the third case, colors are changed by
  19867. specifying both index and bitmask for the palette.)
  19868. HALO supports devices in modes up to 16 bits per pixel (65,536 colors). In
  19869. general, the bits of the same magnitude (i.e. power of 2) of each pixel in the
  19870. display are referred to collectively as a bit plane. The graphics bitmap is
  19871. defined as the sum of all the bit planes. The actual physical mapping of
  19872. pixels in memory varies enormously between various graphics cards and their
  19873. display modes. Fortunately, the HALO device drivers sufficiently hide this
  19874. information so the programmer need never be concerned with such low-level
  19875. details.
  19876. The HALO package supports three different types of coordinate systems: device
  19877. coordinates, world coordinates, and normalized device coordinates. Which
  19878. system you choose depends entirely on your requirements for
  19879. device-independence. HALO provides functions to convert between any of the
  19880. coordinate systems.
  19881. Dealing with aspect ratios is an important part of the graphic environment.
  19882. Aspect ratio is used to convert from the perfect mathematical coordinate plane
  19883. to the real-world imperfect graphics device. Specifically, the aspect ratio is
  19884. the ratio of a pixel's width to its height. For example, the IBM EGA displays
  19885. 640 x 350 pixels on a display 9.6" wide and 6.0" high. Each pixel is
  19886. 9.6 inches / 640 pixels = 0.015 inches / pixel (width)
  19887. 7.2 inches / 350 pixels = 0.0205 inches / pixel (height)
  19888. Click Here for Equation
  19889. HALO automatically corrects circles, ellipses, and arcs for aspect ratio. If
  19890. the correction was not applied, then a circle with a 100 pixel radius would
  19891. appear to be 100 x 0.015 = 1.5 inches wide and 100 x 0.0205 = 2.05 inches
  19892. tall. HALO always corrects in the vertical component so that it would actually
  19893. produce a circle 100 x 0.015 = 1.5 inches wide and 100 x 0.73 x 0.0205 = 1.5
  19894. inches tall. However, it is strictly up to the programmer to include aspect
  19895. ratio in his own calculations for boxes, lines, and other objects.
  19896.  
  19897.  
  19898. Graphics Objects
  19899.  
  19900.  
  19901. HALO offers the programmer all the necessary tools for drawing a variety of
  19902. graphics objects including filled polygons and spline curves. All of the
  19903. drawing operations make use of the graphics cursor. The graphics cursor is an
  19904. invisible reference point on the display. The graphics cursor may be set at an
  19905. absolute location or moved relative to its current position. The graphics
  19906. cursor is used as the first point from which all line and polyline functions
  19907. are drawn. It is also the center point for circle, arc, pie wedge, and ellipse
  19908. drawing functions. Lastly, the graphics cursor is the starting point for fill
  19909. functions.
  19910. Since lines are the most frequently used graphics objects, they require the
  19911. most flexibility. HALO will draw a line from the current graphics cursor to a
  19912. relative position (line relative) or to an absolute position (line absolute).
  19913. You may specify both the width in pixels and the style of the line. HALO
  19914. offers three basic line styles and seven user-defined line styles.
  19915. HALO has built-in functions for creating filled circle, pie wedge, box, and
  19916. polygon shapes. Objects may be filled in the current color as solid or with a
  19917. hatch style. Objects may be filled as they are drawn or filled later with a
  19918. flood-fill function. HALO offers five basic hatching styles and five
  19919. user-defined hatching styles.
  19920. In addition to geometric objects, HALO supports three types of graphics text:
  19921. dot text, fast text, and stroke text. Dot text is a general purpose bitmapped
  19922. font. HALO includes six dot text fonts, whose height and width may be scaled
  19923. in integer multiples. Dot text may be drawn in any of the four compass
  19924. directions. HALO maintains a special cursor called a text cursor for dot and
  19925. stroke text.
  19926. Fast text is a special purpose bitmapped font whose data is taken from the
  19927. graphics board's own ROMs. Additional font files are thus not used for fast
  19928. text. Fast text may only be drawn at integer row and column text positions.
  19929. Additionally, fast text may only be drawn from left to right.
  19930. Stroke text is HALO's most sophisticated text display. Stroke text is not
  19931. defined as a bitmapped font but rather as a series of brush strokes or
  19932. vectors. Since stroke text is displayed as vectors, it uses all the current
  19933. line settings. Stroke text may be sized and rotated to any angle desired. When
  19934. using stroke text drawn at an angle, the programmer must consider the aspect
  19935. ratio of the display. The BARGRAPH program uses only the stroke text to
  19936. achieve the highest quality image. Fig. 6 summarizes the tradeoffs between the
  19937. various HALO text display schemes.
  19938.  
  19939.  
  19940. Advanced Features
  19941.  
  19942.  
  19943. HALO has a variety of features essential to the development of advanced
  19944. graphics applications including area moves, rubberband functions, and the
  19945. "Virtual Rasterizer Interface". Area moves involve copying from one part of
  19946. the bitmap to another. The movefrom() and moveto() functions allow a rectangle
  19947. of the display to be cut and pasted respectively. The moveto() function allows
  19948. the buffer to be pasted in one of several modes including XOR, AND, OR, and
  19949. complement.
  19950. Rubberband functions, like area moves, are designed to make interactive
  19951. graphics programs easier to write. A rubberband object is one that can be
  19952. stretched and dragged across the graphics screen without disturbing it. For
  19953. example, you could write a simple function which polled the mouse to
  19954. interactively position the endpoint of a vector. Each successive call of a
  19955. rubberband function deletes (XORs) the previously displayed object and
  19956. simultaneously writes it at a new position. HALO supports rubberband lines,
  19957. boxes, and circles.
  19958. The "Virtual Rasterizer Interface" (VRI) allows you to create a virtual
  19959. graphics display of any horizontal, vertical, and color resolution desired.
  19960. VRI will use any combination of MS-DOS base memory, EMS memory, and disk space
  19961. to store the image. The most common use of VRI is to assemble an image for a
  19962. laser printer page. For example, an "A" size drawing (8.5" x 11") at 300 dpi
  19963. is effectively a 2550 x 3300 pixel image, requiring just under one megabyte of
  19964. storage. Once the VRI device is initialized, it accepts the same HALO calls as
  19965. any other raster device. A VRI can be configured for up to a 16383 x 16383
  19966. resolution image or 32 megabytes, whichever is smaller.
  19967.  
  19968.  
  19969. BARGRAPH - A Small Application For HALO
  19970.  
  19971.  
  19972. The BARGRAPH demonstration application produces high-quality charts simply and
  19973. efficiently. BARGRAPH takes a language-driven approach to specify the
  19974. parameters of a chart. The PC-DOS usage of this program is "BARGRAPH datafile"
  19975. where datafile is a plain ASCII file containing command strings. Each command
  19976. string specifies a single detail of the chart such as the scale or legend.
  19977. BARGRAPH input files include the HALO specific configuration data as well as
  19978. the actual graph data. A typical BARGRAPH data file is shown in Fig. 7.
  19979. The operation of the BARGRAPH program is roughly divided into two phases. In
  19980. the first phase, the datafile is parsed a line at a time and stored into the
  19981. cmd_data[] static structure. The function process_graphics_cmd_line() is
  19982. called once per input line. This function determines the command keyword and
  19983. parses its arguments into the appropriate slot of cmd_data[]. The DATA,
  19984. COLORS, MODE, and SCALE commands call parse_delimited_number_list() to store
  19985. data in the numeric half of the udata union. Similarly, the LEGEND, FONT,
  19986. TITLES, and DEVICE commands call parse_delimited_string_list() to place data
  19987. in the string array half of the udata union. The COMMENT and END commands
  19988. serve only documentation purposes and are thus ignored. The complete BARGRAPH
  19989. syntax is diagrammed in Fig. 8.
  19990. The second phase uses data supplied in the static structures to setup the HALO
  19991. environment and plot the graph on the screen. The HALO environment is
  19992. established in two phases. First, the function setup_halo_globals() both
  19993. inquires about the capabilities of, and sets the parameters for, the graphics
  19994. device. A global structure called halo, devised expressly for this program,
  19995. tracks HALO environment values throughout the program. The setdev() and
  19996. initgraphics() functions must be the first two HALO calls in an application
  19997. program. These load the device driver from disk and set the hardware graphics
  19998. mode respectively. The remainder of the HALO calls in setup_halo_globals() set
  19999. the degree mode, world coordinate rectangle, line width, line style, drawing
  20000. color, and the stroke text font and color (see Fig. 9)
  20001. The function setup_graph_globals supervises the second phase of
  20002. initialization. A global structure graph separates the BARGRAPH program data
  20003. from the HALO data. The graph structure holds data in a form which will
  20004. simplify calculations later. If the user does not supply SCALE Y-Axis upper
  20005. and lower bounds, BARGRAPH will use the min and max data points as the scale
  20006. range.
  20007. The bar graph is drawn by draw_bar_graph(). First, draw_axes() produces the
  20008. axes in three steps. First, the legend string, horizontal X-Axis, and vertical
  20009. Y-Axis are drawn at predefined coordinates. Secondly, tick marks and their
  20010. labels are drawn along the Y-Axis. (The draw_axes() function makes a total of
  20011. ten ticks above the Y-Axis.) Finally, the title for each bar is drawn below
  20012. the X-Axis at a 45 degree angle -- the angle keeps titles from overwriting
  20013. each other. Since each stroke text character is a different size, the
  20014. inqstsize() function must be called to determine the actual space required for
  20015. each title string.
  20016. Once the axes are complete the bars are placed on the screen. If the graphics
  20017. device is monochrome or the user has not specified any bar colors then a
  20018. sequence of four hatch styles will be used. This ensures that default graphs
  20019. are displayed similarly on monochrome and color graphics devices. The
  20020. equations for determining the bar size are shown in Fig. 10.
  20021.  
  20022.  
  20023.  
  20024. Improving BARGRAPH
  20025.  
  20026.  
  20027. Some simple enhancements which might greatly increase the utility of BARGRAPH
  20028. include the following:
  20029. (1) Read the HALO-dependent commands (DEVICE, PRINTER, MODE, etc.) from a
  20030. default configuration file (e.g. BARGRAPH.CFG) so they need not be repeated in
  20031. each data file.
  20032. (2) Add aspect-ratio calculations to standardize the look of the graph.
  20033. (3) Add line graph and pie-slice graph types to the program. Create a new
  20034. command called CHART to specify the graph type.
  20035. (4) Allow the user to capture the graph display and save it with a file format
  20036. which can be read by desktop publishing programs.
  20037.  
  20038.  
  20039. Conclusion
  20040.  
  20041.  
  20042. The HALO Graphics Library by Media Cybernetics is a highly useful programming
  20043. tool for developing your own graphically oriented programs. The versatility,
  20044. efficiency, and functionality of HALO are easily demonstrated by BARGRAPH. The
  20045. BARGRAPH applications program as presented required less than two dozen
  20046. different functions out of the 200 offered in HALO. The executable file
  20047. amounts to just under 100K plus about 12K for device drivers, a fairly modest
  20048. memory requirement. The most important contribution to BARGRAPH is the ability
  20049. to operate with any combination of the dozens of screen and printer drivers
  20050. that HALO offers.
  20051.  
  20052.  
  20053. Rasters, Pixels, Vectors, Palletes -- Elements Of The Graphic Environment
  20054.  
  20055.  
  20056. Graphics objects may be constructed from pixels or vectors. A pixel (or
  20057. Picture Element) is the smallest resolvable discrete point on a graphics
  20058. device. Graphics devices addressable only by pixels are known as raster
  20059. devices. The resolution of a raster-type graphics card or mode is expressed in
  20060. pixels. For example, the minimum resolution of the IBM EGA card is 640 columns
  20061. x 350 rows of pixels. In the special case of a monochrome display, a pixel
  20062. directly corresponds to a single bit in display memory. Color displays require
  20063. more than one bit per pixel to describe the color of the pixel. For example,
  20064. the IBM EGA card uses four bits per pixel to produce a total of 24 = 16
  20065. colors.
  20066. In contrast, vectors are line segments defined by a starting point, direction,
  20067. and length. Although every raster device can display vectors, vector devices
  20068. do not have bitmaps and cannot display pixels, as such. For example, a pen
  20069. plotter typically has no knowledge of vectors it has already drawn. Certain
  20070. hybrid graphics devices, such as the Control Systems Artist, accept both
  20071. raster and vector data.
  20072. Every graphics device, raster and vector, has a finite set of discrete
  20073. displayable colors called the palette. On color devices, each pixel is
  20074. displayed in the color corresponding to its index in the palette. For example,
  20075. the IBM EGA has a palette of 16 colors out of 64 available. Fig. 2 shows a
  20076. portion of an example IBM EGA palette: a pixel with index of 15 would be
  20077. bright white (all bits set) whereas a pixel with index of 3 would be dull red
  20078. (only 1 red bit set).
  20079. The most flexible graphics devices support a programmable palette.
  20080. Programmable palette devices allow you to specify integer values for the
  20081. amount of Red, Green, and Blue (RGB) components of each color. For example,
  20082. the Number Nine Revolution in 832 x 624 resolution has a palette of 16 colors.
  20083. Each index of the palette has 256 possible values for each RGB color
  20084. component.
  20085. Coordinate Systems
  20086. The HALO package supports three different types of coordinate systems: device
  20087. coordinates, world coordinates, and normalized device coordinates. Which
  20088. system you choose depends entirely on your requirements for
  20089. device-independence. A summary of the coordinate systems is presented in Fig.
  20090. 10. HALO provides functions to convert between any of the coordinate systems.
  20091. The device coordinate system maps each logical coordinate directly to its
  20092. physical coordinate or pixel. In the device coordinate system, the upper-left
  20093. corner of the screen is at (0,0) and the lower-right corner is at the maximum
  20094. coordinate. For example, on the Hercules Monographics card with a resolution
  20095. of 720 x 350 the upper-left corner is (0,0) and the lower-right hand corner is
  20096. (719,349) (see Figure 3). In HALO, device coordinates have the advantage that
  20097. they can be expressed in integers rather than floats.
  20098. Since device coordinates are dependent on the resolution of the output device
  20099. you use, they are a poor choice for writing portable applications. The world
  20100. coordinate system allows you to specify your own resolution independently of
  20101. the hardware. This coordinate translation means that even though the Hercules
  20102. and IBM CGA cards have different heights and widths, your program can operate
  20103. exactly the same for both of them.
  20104. When enabled, HALO will translate from world coordinates to device coordinates
  20105. automatically. For example, if you were to define the world coordinates from
  20106. (-100.0,-100.0) to (100.0,100.0) then a reference to (0.0,0.0) would map to
  20107. the center of the display. World coordinates assume a Cartesian orientation.
  20108. In HALO, world coordinates are expressed as floats rather than integers. The
  20109. BARGRAPH program uses a world coordinate system from (0.0,0.0) to (1.0,1.0).
  20110. Normalized Device Coordinates (NDCs) are another way of mapping from logical
  20111. coordinates to physical coordinates. NDCs are like device coordinates because
  20112. the upper-left corner is always the origin of the screen (see Figure 4B). NDCs
  20113. differ from device coordinates in that the location of lower-right corner of
  20114. the screen is always the same regardless of the actual output device being
  20115. used. The only difference between NDCs and world coordinates is that the
  20116. upper-left corner and lower-right corners are fixed at (0.0,0.0) and (1.0,1.0)
  20117. respectively in the NDC system. NDCs are used in the HALO function
  20118. set-viewport() to allow viewports (i.e. windows) to be nested in a
  20119. device-independent way.
  20120. A viewport is a region of the display into which graphics are mapped. By
  20121. default, the viewport includes the entire screen from (0.0,0.0) to (1.0,1.0)
  20122. in NDCs. After setting a viewport, all graphics calls in world coordinates
  20123. will map into the new viewport. Only one viewport can be in effect at any
  20124. time. for example, to put a viewport in the upper-right hand quadrant of the
  20125. screen you would specify (0.5,0.5) and (1.0,1.0). Figure 5 shows a bargraph
  20126. mapped into the upper-right quadrant specified.
  20127. Figure 1
  20128. Figure 2 Example Palette for IBM EGA
  20129. Figure 3 Example Device Coordinates
  20130. Figure 4a Example World Coordinates
  20131. Figure 4b Normalized Device Coordinates
  20132. Figure 5
  20133. Figure 6 HALO '88 Graphics Text Summary
  20134.  Display Drawing Display
  20135. Text Type Quality Speed Flexibility
  20136. Stroke text High Slow High
  20137. Dot text Medium Medium Medium
  20138. Fast text Low Fast Low
  20139. Figure 7 Example BARGRAPH data file
  20140. COMMENT this is a test of the bargraph application
  20141. DEVICE HALOHERC.DEV
  20142. MODE 0
  20143. PRINTER HALOEPSN.PRN
  20144. ATTRIBUTES -1,-1,0,0,0,0,0,0,-1,0,0,-1,1,-1,-1,-1,0
  20145. FONT HALO104.FNT
  20146. COLORS 1,2,3
  20147. LEGEND 1989 Projected Sales
  20148. TITLES Jan,Feb,Mar,Apr,May,Jun,
  20149. TITLES Jul,Aug,Sep,Oct,Nov,Dec
  20150. SCALE 0.0,200.0
  20151. DATA 10.0,42.0,130.0,80.0,54.3,140.0
  20152. DATA 180.0,135.0,300.0,69.0,94.7,101.0
  20153. END
  20154.  
  20155.  
  20156. Figure 8 Complete BARGRAPH Command Syntax
  20157. COMMAND MEANING DEFAULT
  20158. ------- ------- -------
  20159. COMMENT Documentation only N/A
  20160. DEVICE s1 Name of HALO screen device HALOIBMG.DEV
  20161. PRINTER s1 Name of HALO printer device none
  20162. FONT s1 Name of HALO stroke font to use HALO104.FNT
  20163. LEGEND s1 Legend is centered over top of graph none
  20164. TITLES s1,s2...sn Titles are displayed underneath BARs none
  20165. MODE v1 Graphics mode (device dependent) 0
  20166. ATTRIBUTES v1,v2...vn Printer attributes (device dependent) none
  20167. SCALE v1,v2 Set extent of Y-Axis from v1...v2 autoscaling
  20168. DATA v1,v2...vn Input n data values (may be repeated) none
  20169. COLORS c1,c2...cn Color pattern to use for bars monochrome hatch
  20170. END Signifies end of a graph N/A
  20171.  
  20172. Figure 9
  20173. Initialization of HALO '88 in setup_halo_globals()
  20174.  
  20175. setdev(halo.device); /* Initialize the graphics device */
  20176. setdegree(&halo.degree_mode); /* Use degrees, not radians */
  20177. setworld(&halo.x1,&halo.y1,&halo.x2,&halo.y2); /* World rectangle */
  20178. setlnwidth(&halo.lnwidth) ; /* Line width is 1 pixel */
  20179. setlnstyle(&halo.lnstyle); /* Line style is solid */
  20180. setcolor(&halo.maxcolor); /* Max screen color is usually white */
  20181. setfont(halo.font); /* Load font from disk file */
  20182. setstclr(&halo.maxcolor,&halo.maxcolor) ; /* Set stroke text color */
  20183.  
  20184. Figure 10
  20185.  Upper-left Lower-right Hardware
  20186. Coordinate System Corner Corner Independence
  20187. Norm. Device Coord 0,0,0,0 1,0,1,0 Yes
  20188. World Coordinates User-defined User-defined Yes
  20189. Device Coordinates 0,0 Hardw. depend. No
  20190.  
  20191. Figure 11
  20192.  
  20193.  
  20194.  
  20195.  
  20196.  
  20197.  
  20198.  
  20199.  
  20200.  
  20201.  
  20202.  
  20203.  
  20204.  
  20205.  
  20206.  
  20207.  
  20208.  
  20209.  
  20210.  
  20211.  
  20212.  
  20213.  
  20214.  
  20215.  
  20216.  
  20217.  
  20218. A Small Prolog Interpreter
  20219.  
  20220.  
  20221. Lindsey Spratt
  20222.  
  20223.  
  20224. Lindsey Spratt is a graduate student in computer science at the University of
  20225. Kansas, concentrating in artificial intelligence. He received a B.S. in
  20226. mathematics from MIT. He worked developing the Multics operating system for
  20227. seven years, and then developed CASE tools and researched program
  20228. understanding (for specification recovery). A logic programming devotee, most
  20229. of his work in recent years has been done in Prolog.
  20230.  
  20231.  
  20232.  
  20233.  
  20234. Introduction
  20235.  
  20236.  
  20237. Small Prolog 1.32 by Henri de Feraudy is a minimal-featured public domain
  20238. implementation of a Prolog interpreter which uses the Cambridge (Lisp-like)
  20239. syntax (CUG 297). The source is provided, as well as makefiles for MS-DOS, Sun
  20240. and Atari. An executable file named SPROLOG.EXE is provided for the MS-DOS
  20241. environment. The distribution also includes 11 files of Prolog examples and
  20242. documentation.
  20243. I ran Small Prolog under MS-DOS emulation -- SoftPC on a Macintosh IIx. It ran
  20244. without any problems and was easy to use.
  20245.  
  20246.  
  20247. The Question Of Syntax
  20248.  
  20249.  
  20250. There is no official standard for Prolog. However, as is true for some other
  20251. languages without official standards (e.g., Common LISP), there is a de facto
  20252. standard known as Edinburgh syntax Prolog (a variant developed at the
  20253. University of Edinburgh, Scotland). Most Prolog texts use the Edinburgh syntax
  20254. or some close variant. Nearly all commercial implementations of Prolog are
  20255. Edinburgh syntax.
  20256. Cambridge syntax represents everything as parenthesis-delimited lists, giving
  20257. it a very LISP-like appearance. The only commercial implementation which used
  20258. Cambridge syntax has completely converted to Edinburgh syntax (for a while,
  20259. this implementation supported both syntaxes).
  20260. In Edinburgh syntax, a predicate to relate an element to a list containing
  20261. that element is:
  20262. member(Element, [Element \ LisTail]).
  20263.  
  20264. member(Element, [IgnoredListHead \ListTail]) :-
  20265. member(Element, ListTail).
  20266. In Cambridge syntax, this same predicate is:
  20267. ((member Element (Element \ ListTail)))
  20268.  
  20269. ((member Element (IgnoredListHead \ ListTail))
  20270. (member Element ListTail))
  20271. In the Small Prolog documentation, Feraudy lists "improve the syntax" as one
  20272. of the projects you might undertake.
  20273. There are 11 files of example Small Prolog programs. Considered as a whole,
  20274. they provide a nice tutorial introduction to Prolog.
  20275. According to the documentation, Feraudy set out to meet these design goals:
  20276. A minimal usable implementation.
  20277. Maximum portability.
  20278. Educational code.
  20279. Extensibility.
  20280. A small object code.
  20281. Embeddability.
  20282. Facilitate meta-programming.
  20283. Small Prolog meets most of these goals fairly well. The implementation is
  20284. usable for executing small Prolog programs. It is minimally usable in that the
  20285. programs it supports should be less than a hundred clauses, use only modest
  20286. amounts of recursion, and do only simple arithmetic (if any). The code is
  20287. portable, extensible, small, embeddable and it supports meta-programming. The
  20288. support of meta-programming is important to provide a feel for how one
  20289. programs in (real) Prolog. Unfortunately, the source is very lightly
  20290. commented, a lamentable condition regardless of purpose, but particularly
  20291. unfortunate when the code is intended to be studied.
  20292. Small Prolog provides complete support of the logic programming paradigm. I
  20293. was particularly pleased to see that Small Prolog supported lack of data
  20294. typing and the ability to handle incomplete data structures. Incomplete data
  20295. structures are extremely useful in logic programming.
  20296. Small Prolog has some of the common Prolog extensions, but lacks others. Small
  20297. Prolog is unusual in requiring all arithmetic to be either integer or real --
  20298. no mixed type arithmetic is supported. Further, the programmer must choose the
  20299. correct arithmetic procedures based on the type of the arguments.
  20300. The Small Prolog debugging environment is incomplete. The common facilities
  20301. (found, for instance, in Quintus Prolog, C-Prolog, and LPA Prolog) allow the
  20302. user several choices when stepping through the execution of a program. These
  20303. choices are generally called abort, retry, fail, exit, skip, leap, and
  20304. continue. Small Prolog appears to only provide abort and continue.
  20305. Most Full commercial implementations, unlike Small Prolog, also allow the
  20306. programmer to set spy points on selected procedures. When tracing, the
  20307. debugger starts stepping when it encounters a spy point. The skip command
  20308. directs the debugger to skip to the next encounter of a spy point. The leap
  20309. command directs the debugger to leap to the exit of the current procedure call
  20310. (ignoring spy points encountered on the way). Small Prolog also does not
  20311. include a portray procedure, which allows the user to define how terms are
  20312. printed during a trace. The portray procedure is useful since the data in the
  20313. arguments of the goals is commonly large and complexly structured.
  20314.  
  20315.  
  20316. Performance
  20317.  
  20318.  
  20319. To test Small Prolog's capabilities, I used it to solve the classic N Queens
  20320. problem:
  20321. Given a square board of N by N cells, find a distribution of (chess) queens on
  20322. the board such that no two queens attack each other. Two queens attack each
  20323. other if they are on the same row, the same column, or the same diagonal.
  20324. The MS-DOS version of Small Prolog can solve up to 9 queens, which is a
  20325. relatively small number. The limit is due to the extensive recursion in the
  20326. program. This test convinced me that Small Prolog is not useful for any kind
  20327. of application development (its too slow and its memory limitations are too
  20328. severe). These limitations make it unlikely that Small Prolog could be
  20329. successfully embedded in any non-trivial application.
  20330.  
  20331.  
  20332.  
  20333. The Source Code
  20334.  
  20335.  
  20336. Because the code is sparsely commented, the reader should have another source
  20337. for describing how a Prolog interpreter works. De Feraudy mentions the books
  20338. on which he based his implementation, and I recommend that you use these when
  20339. studying his source.
  20340. The files most critical to your understanding are PRLUSH.C and PRUNIFY.C.
  20341. PRLUSH.C contains the algorithm for how procedures are executed. PRUNIFY.C
  20342. contains the algorithm for matching terms (unification). The execution of
  20343. procedures is a depth-first search, with backtracking. Procedures are selected
  20344. by pattern-matching between the arguments of the call and the parameters of
  20345. the procedure definition. The pattern-matching is done via unification.
  20346.  
  20347.  
  20348. Conclusion
  20349.  
  20350.  
  20351. Small Prolog is particularly valuable for aiding in the study of how Prolog is
  20352. implemented. Because the Cambridge syntax is not used in the available
  20353. teaching materials, I don't recommend it as a learning environment for the
  20354. serious Prolog student, though if you are just curious about how Prolog works,
  20355. you may find Small Prolog useful. This implementation is not suitable for
  20356. supporting the development of applications written in Prolog.
  20357. Still, this is the only Prolog product which provides the source code for the
  20358. interpreter. Now, if only someone would provide the source code for a Prolog
  20359. compiler.
  20360.  
  20361.  
  20362. A Crash Course In Cambridge Syntax Prolog
  20363.  
  20364.  
  20365. A Prolog program is a set of facts and rules. One executes it by posing a
  20366. query to the Prolog system, which it then tries to prove using the facts and
  20367. rules in the program. If there are variables involved, they are generally
  20368. bound to values in the course of building a proof. The output from a query is
  20369. the set of bindings of values to the variables in the query.
  20370. A query is true if there is some fact which matches it, or if there is a rule
  20371. which has a head which matches the query and which has a true body. The body
  20372. of a rule is true if all of the goals in it are true. A goal is true if there
  20373. is a matching fact, or if there is a rule which has a head which matches the
  20374. goal and which has a true body. To simplify the terminology somewhat, instead
  20375. of using the terms fact and rule, I speak of both of these as clauses. A
  20376. clause has a head and a possibly empty body. If the body is empty, the clause
  20377. is a fact. If the body is not empty, then the clause is a rule.
  20378. A Prolog program consists of an ordered set of clauses. A clause is a list.
  20379. The first element of the list is the head of the clause, and the rest of the
  20380. elements of the clause list are goals and are known collectively as the body
  20381. of the clause. A goal is a list with an atom as its first element and any term
  20382. for its other elements (a goal is a special kind of list, used in a particular
  20383. way). The first element is the functor of the goal, the number of elements
  20384. following the functor is the arity of the goal. All of the clauses having
  20385. heads with the same functor and arity are collectively known as a procedure. I
  20386. frequently refer to a procedure by its functor and arity separated by a slash,
  20387. /.
  20388. An atom is an extended alphanumeric (including underscore, _) token with an
  20389. initial lowercase alphabetic character (e.g. foo). A variable is an extended
  20390. alphanumeric token with an initial uppercase alphabetic character or an
  20391. initial underscore, _ ( e.g. Foo). The head of a clause has the same syntax as
  20392. a goal. There are three syntaxes for a list. In all three cases a list begins
  20393. with a left parenthesis, (, and ends with a right parenthesis, ). An empty
  20394. list has nothing between the parentheses (e.g. ()). A simple list has a series
  20395. of tokens separated by whitespace inside the parentheses (e.g. (a b c)). A
  20396. cons list (to borrow some terminology from Lisp), has a series of one or more
  20397. tokens separated by whitespace starting at the left parenthesis, followed by a
  20398. vertical bar, , followed by a term, followed by the right parenthesis (e.g. (a
  20399. b Foo)). Generally this last syntax is used when the term following the
  20400. vertical bar is either an unbound variable or a list.
  20401. Examples are:
  20402. Term Type
  20403.  
  20404. a atom
  20405. atom atom
  20406. a_thing atom
  20407.  
  20408. A variable
  20409. Atom variable
  20410. X variable
  20411. _vat variable
  20412. A_Variable variable
  20413.  
  20414. () list (empty)
  20415. (foo) list
  20416. (foo bar baz) list
  20417. (foo bar X) list (with tail of X)
  20418. (foo bar (baz)) list (with tail of (baz))
  20419. In the examples of lists, (foo bar (baz)) has the exact same meaning as (foo
  20420. bar baz).
  20421. In the member/2 procedure:
  20422. ((member Element (Element ListTail)))
  20423.  
  20424. ((member Element (IgnoredListHead ListTail))
  20425. (member Element ListTail))
  20426. There are two clauses:
  20427. ((member Element (Element ListTail)))
  20428. and
  20429. ((member Element (IgnoredListHead ListTail))
  20430. (member Element ListTail))
  20431. The first clause has an empty body (i.e. there are no goals in its body). Its
  20432. head is (notice that one layer of parentheses has been stripped away):
  20433. (member Element (Element ListTail))
  20434. This head is a list of the three elements: member, Element, and (Element
  20435. ListTail). The first element is an atom, the second element is a variable, and
  20436. the third element is a cons list. The first element of the cons list is the
  20437. variable Element. The tail of the cons list is the variable ListTail. The
  20438. second clause is similar in structure to the first, with the addition of a
  20439. goal in its body. The functor and arity of the goal is the same as the functor
  20440. and arity of the clause, thus this is a recursive procedure.
  20441. The interpreter prompts the user for a query with ?-. In the example uses of
  20442. member/2 below, the ?- is provided by the system.
  20443. ?- (member a (a b))
  20444. Yes
  20445.  
  20446. ?- (member b (a b))
  20447.  
  20448. Yes
  20449. ?- ((member X (a b)) (display X) (nl) (fail))
  20450. a
  20451. b
  20452. No
  20453. In the last example display/1 is a procedure to display its argument, n1/0 is
  20454. a procedure to print a newline, and fail/0 is a procedure which always fails.
  20455. Failure in Prolog forces the preceding (successful) goal to try to find
  20456. another solution. If it succeeds in finding another solution, then the rest of
  20457. the goals (starting with the one which failed) are re-executed. If it fails,
  20458. then its preceding goal is retried.
  20459.  
  20460.  
  20461. Meta-programming
  20462.  
  20463.  
  20464. Meta-programming refers to writing procedures which use procedures as data.
  20465. For example, an if procedure can be written via meta-programming:
  20466. ((if Test ThenGoals ElseGoals)
  20467. (Test)
  20468. (cut)
  20469. (ThenGoals) )
  20470.  
  20471. ((if _ _ ElseGoals)
  20472. (ElseGoals))
  20473. This procedure can be used as follows:
  20474. (if (iless X 3) (writes "Less than 3") (writes
  20475. "Greater or equal to 3"))
  20476. The data-like procedures in the if procedure are the three arguments, Test,
  20477. ThenGoals, and ElseGoals. Each of these arguments is called in the bodies of
  20478. the two if clauses. In the example use of if, Test is (iless X 3), ThenGoals
  20479. is (writes Less than 3), and ElseGoals is (writes Greater or equal to 3).
  20480. The Test Program
  20481. This is the N queens program used to test the
  20482. interpreters performance. The program is invoked by:
  20483. (queens N Solution) where N is the number of queens
  20484. (and the size of the board) and Solution is the
  20485. resulting positions of the queens.
  20486.  
  20487.  
  20488. /*
  20489. (queens +N -Positions)
  20490. queens/2 is the main procedure for solving the
  20491. N-queens problem. N is input, the number of
  20492. queens for which a solutions is desired.
  20493. Positions is output, it is the list of the
  20494. positions of the N queens such that they dont
  20495. attack each other.
  20496. */
  20497.  
  20498. ((queens N Positions)
  20499. (template N Positions)
  20500. (solution N Positions))
  20501.  
  20502. /*
  20503. (solution +N +Position)
  20504. N is input, the number of queens for which a
  20505. solutions is desired. Positions is partially
  20506. instantiated as input and fully instantiated
  20507. as output, it is the list of the positions of
  20508. the N queens such that they dont attach each
  20509. other. On input, each position has only its X
  20510. value instantiated. On output, the Y value is
  20511. also instantiated.
  20512. */
  20513.  
  20514. ((solution N ()))
  20515.  
  20516.  
  20517. ((solution N (PosOthers))
  20518. (solution N Others)
  20519. (pos_y Pos Y)
  20520. (between 1 Y N)
  20521. (noattack Pos Others))
  20522.  
  20523.  
  20524. /*
  20525. (noattack +NewPosition +EstablishedPositions)
  20526. NewPosition is input, a new queen position to
  20527. check against the list of established list of
  20528. queen positions. EstablishedPositions is input,
  20529. a list of established queen positions to check
  20530. against the new queen position.
  20531. It is known that there are no attacks among the
  20532. EstablishedPositions.
  20533. */
  20534.  
  20535. ((noattack _ ()))
  20536.  
  20537. ((noattack NewPos (CheckPosOthers))
  20538. (pos NewPos NewX NewY)
  20539. (pos CheckPos CheckX CheckY)
  20540. (not (eq NewY CheckY))
  20541. (iminus CheckY NewY DiffY)
  20542. (iminus CheckX NewX DiffX1)
  20543. (not (eq DiffY DiffX1))
  20544. (iminus NewX CheckX DiffX2)
  20545. (not (eq DiffY DiffX2))
  20546. (noattack NewPos Others))
  20547. /*
  20548. (member ?X ?List)
  20549. X is input or output, it is a term in the List.
  20550. List is input or output, it is a list which
  20551. contains the term X.
  20552. */
  20553.  
  20554. ((member X (XL)))
  20555.  
  20556. ((member X (_L)) (member X L))
  20557.  
  20558.  
  20559. /*
  20560. (template +N -Positions)
  20561. N is input, it is the number of queen positions.
  20562. Positions is output, it is a list of queen partially
  20563. instantiated positions.
  20564. The X value is instantiated and the Y value is
  20565. unbound. This procedure is used to create the
  20566. position template which is used by solution/2.
  20567. */
  20568.  
  20569. ((template 0 ()))
  20570.  
  20571. ((template N (Position OtherPositions))
  20572. (iless 0 N)
  20573. (pos_x Position N)
  20574. (iminus N 1 NextN)
  20575. (template NextN OtherPositions))
  20576.  
  20577.  
  20578.  
  20579. /*
  20580. The following 3 procedures are for accessing
  20581. the pos data structure, which is used to describe
  20582. the positions of the queens.
  20583. */
  20584.  
  20585. ((pos (position X Y) X Y))
  20586.  
  20587. ((pos_x (position X _) X))
  20588.  
  20589. ((pos_Y (position - Y) Y))
  20590.  
  20591.  
  20592. /*
  20593. (between +Low -Middle +High)
  20594. Low isinput, it is the low value in an
  20595. integer-valued interval. Middle is output, it is an
  20596. integer value between the Low and High values.
  20597. High is input, it is the high value in an
  20598. integer-valued interval. between/3 can be
  20599. used to generate and test integers between Low
  20600. and High, until an integer is found which
  20601. is satisfactory.
  20602. */
  20603.  
  20604. ((between L L _))
  20605.  
  20606. ((between L M H)
  20607. (iless L H)
  20608. (iplus L 1 NextL)
  20609. (between NextL M H))
  20610.  
  20611.  
  20612.  
  20613.  
  20614.  
  20615.  
  20616.  
  20617.  
  20618.  
  20619.  
  20620.  
  20621.  
  20622.  
  20623.  
  20624.  
  20625.  
  20626.  
  20627.  
  20628.  
  20629.  
  20630.  
  20631.  
  20632.  
  20633.  
  20634.  
  20635.  
  20636.  
  20637.  
  20638.  
  20639.  
  20640. Understanding C
  20641.  
  20642.  
  20643. Harold C. Ogg
  20644.  
  20645.  
  20646. This article is not available in electronic form.
  20647.  
  20648.  
  20649.  
  20650.  
  20651.  
  20652.  
  20653.  
  20654.  
  20655.  
  20656.  
  20657.  
  20658.  
  20659.  
  20660.  
  20661.  
  20662.  
  20663.  
  20664.  
  20665.  
  20666.  
  20667.  
  20668.  
  20669.  
  20670.  
  20671.  
  20672.  
  20673.  
  20674.  
  20675.  
  20676.  
  20677.  
  20678.  
  20679.  
  20680.  
  20681.  
  20682.  
  20683.  
  20684.  
  20685.  
  20686.  
  20687.  
  20688.  
  20689.  
  20690.  
  20691.  
  20692.  
  20693.  
  20694.  
  20695.  
  20696.  
  20697.  
  20698.  
  20699.  
  20700.  
  20701. Publisher's Forum
  20702. This issue debuts a redesigned CUJ. Don't panic, we've only changed some of
  20703. the artwork -- the editorial focus and content remains the way you like it.
  20704. We have, however, reorganized the mast and table of contents. We've
  20705. standardized the treatment of columns and made the artwork for each of uniform
  20706. size. Our staff artist Susan Buchanan has designed little icons for each
  20707. department and refined the illustrations with each column. In short, in
  20708. keeping with our general philosophy, we've made a lot of incremental changes,
  20709. but the end effect should be a more attractive, accessible, and manufacturable
  20710. product.
  20711. We had meant to spring this redesign fully-grown with this issue.
  20712. Unfortunately Susan and Howard -- half our editorial staff -- have been ill
  20713. for a major portion of this cycle. As a consequence, there remains some little
  20714. tweaking to finish the project.
  20715. Periodic redesigns are one of the unavoidable "passages" for a magazine. While
  20716. the eager j-school graduates will contend fervently that a "fresh" and
  20717. successful design will increase sales and attract readers, jaded old editors
  20718. like myself learn to relate to redesigns much as experienced husbands relate
  20719. to home redecoration. Sure, it's nice to change your environment once and a
  20720. while, but it's certainly not nirvana -- and there's certainly nothing to be
  20721. gained by placing the sofa in the kitchen.
  20722. So, don't expect to find anything that will replace your Van Gogh collection.
  20723. Even so, I think Susan's icons are excellent work -- and the table of contents
  20724. (the collective work of Ann, Susan and Howard) is a vast improvement. We hope
  20725. you find the new design cleaner, easier to use, and just as informative as
  20726. ever.
  20727. Sincerely yours,
  20728. Robert Ward
  20729.  
  20730.  
  20731.  
  20732.  
  20733.  
  20734.  
  20735.  
  20736.  
  20737.  
  20738.  
  20739.  
  20740.  
  20741.  
  20742.  
  20743.  
  20744.  
  20745.  
  20746.  
  20747.  
  20748.  
  20749.  
  20750.  
  20751.  
  20752.  
  20753.  
  20754.  
  20755.  
  20756.  
  20757.  
  20758.  
  20759.  
  20760.  
  20761.  
  20762.  
  20763.  
  20764.  
  20765.  
  20766.  
  20767.  
  20768.  
  20769.  
  20770.  
  20771.  
  20772.  
  20773.  
  20774.  
  20775.  
  20776.  
  20777.  
  20778.  
  20779.  
  20780.  
  20781.  
  20782. New Products
  20783.  
  20784.  
  20785. Industry-Related News & Announcements
  20786.  
  20787.  
  20788.  
  20789.  
  20790. Interactive Bundles LPI C
  20791.  
  20792.  
  20793. LPI and Interactive Systems Corporation have signed an agreement in which
  20794. Interactive will be bundling LPI's ANSI-C development environment with the
  20795. 386/ix Software Development System v2.2.
  20796. LPI has also signed an agreement with Sequoia Systems, Inc., in which LPI will
  20797. port its COBOL, NEW C, and Code Watch language products to the Sequoia Series
  20798. 300, a UNIX-based fault-tolerant system based on Motorola's 68030 processor.
  20799. Sequoia will have marketing and distribution rights to LPI's COBOL, NEW C and
  20800. CodeWatch products.
  20801.  
  20802.  
  20803. Sage Acquires Plink 86 Rights
  20804.  
  20805.  
  20806. Sage Software, Inc. has acquired the exclusive worldwide marketing and source
  20807. code development rights to Plink86 +, from Phoenix Technologies, Inc. of
  20808. Norwood, MA.
  20809. Plink86+ operates on PC/XT/AT, PS/2 or compatibles, running MS-DOS v3.0 or
  20810. higher with 256K of systems memory. Plink86+ retails for $495.
  20811. For more information, contact Sage Software at (800) 547-4000, (503) 645-1150
  20812. or FAX (503) 645-4576.
  20813.  
  20814.  
  20815. Z-World Releases Z80 Compiler
  20816.  
  20817.  
  20818. Z-World has released FLASH C, a C programming tool for the Z80/HD64180/Z180 mP
  20819. which includes a compiler and source level debugger.
  20820. FLASH C enables the programmer to edit, compile, debug, and run in one
  20821. integrated environment. A 200 line C program will compile in approximately
  20822. eight seconds.
  20823. For more information, contact Z-World at 1340 Covell Blvd., #101, Davis, CA
  20824. 95616 (916) 753-3722; FAX (916) 753-5141.
  20825.  
  20826.  
  20827. New SCO UNIX Supports 486/25
  20828.  
  20829.  
  20830. The Santa Cruz Operation, Inc. has released SCO UNIX System V/386 v3.2
  20831. Operating System, a multiprocessing extension, SCO MPX and a graphical user
  20832. interface, JSB MultiView DeskTop. SCO UNIX System V/386 v3.2 and Open Desktop
  20833. will support the new IBM PS/2 486/25 Power Platform for the Model 70-A21. SCO
  20834. has also demonstrated new SCO UNIX System technology that enables applications
  20835. to run on the new Intel i860-based IBM PS/2 Wizard Adapter.
  20836. Multi-user, multi-tasking SCO UNIX System V/386 v3.2 is an AT&T-licensed
  20837. implementation of UNIX System V/386 v3.2 for 386- and 486-based computers. It
  20838. complies with POSIX and X/Open standards and is designed to meet the U.S.
  20839. government's Department of Defense C2-level security requirements. It runs
  20840. both XENIX and UNIX System-based applications, and will also run MS-DOS
  20841. applications when combined with SCO VP/ix.
  20842. Open Desktop is SCO's graphical operating system. Based on SCO UNIX System V,
  20843. it includes a relational database, networking, X windows with OSF/Motif, and
  20844. MS-DOS compatibility.
  20845. SCO MPX is a multiprocessing extension to the SCO UNIX System V/386 v3.2
  20846. Operating System and to SCO's new Open Desktop.
  20847. SCO MPX is based on multiprocessing software technology developed by
  20848. Corollary, Inc. Through a joint development agreement between Corollary and
  20849. SCO this technology was adapted to become the standard multiprocessing
  20850. extension for SCO Operating Systems.
  20851. In addition to OEM designs based on the Corollary 386/smp and 486/smp, SCO MPX
  20852. supports the Apricot MC486, the Compaq Systempro, the Mitac Series 500, and
  20853. the Zenith Z1000.
  20854. SCO MPX will install on any supported computer running SCO UNIX System V/386
  20855. v3.2 or SCO's Open Desktop, and will support one additional CPU per package.
  20856. In addition to the SCO UNIX System V/386 Operating System, as many as 15 SCO
  20857. MPX packages can be utilized on a single machine, thereby supporting 16 total
  20858. CPUs.
  20859. Modifications are installed into the SCO UNIX System kernel to support
  20860. symmetrical, closely coupled multiprocessing. Each CPU runs simultaneously
  20861. from a single SCO UNIX System, processing the system and user tasks in
  20862. priority order. This automatically balances the load across all CPUs. On
  20863. supported hardware, there is nearly a linear increase in overall system CPU
  20864. throughput with each added CPU, maximizing the total available computing
  20865. resources of the system.
  20866. JSB MultiView DeskTop is a graphical user interface for MS-DOS users who want
  20867. to share data and files with SCO XENIX and SCO UNIX Systems on a network.
  20868. JSB MultiView DeskTop enables users to connect any 286- or 386-based PC
  20869. running Microsoft Windows to an SCO XENIX or SCO UNIX System-based host via a
  20870. direct RS232 connection or a local area network. The JSB MultiView DeskTop
  20871. enables users to choose from local MS-DOS applications or remote SCO XENIX or
  20872. SCO UNIX System applications. When selected, these applications appear in
  20873. concurrently running windows on the PC. Users can transfer files and "copy and
  20874. paste" data among the discrete MS-DOS, XENIX, and/or UNIX System applications.
  20875. When used in a "mixed" environment, JSB MultiView DeskTop lets users protect
  20876. their investment in third-party MS-DOS software and training while taking
  20877. advantage of all the features of the multi-user UNIX System, including
  20878. electronic mail and shared programs such as databases.
  20879. SCO MPX will be available through all SCO distribution channels in the first
  20880. quarter of 1990, and will list for $895. JSB MultiView DeskTop is $149 for a
  20881. one-user license, $495 for a five-user license, and $795 for a ten-user
  20882. license. For more information, contact The Santa Cruz Operation, Inc., 400
  20883. Encinal St., PO Box 1900, Santa Cruz, CA 95061 (408) 425-7222.
  20884.  
  20885.  
  20886. TSR Library For Turbo, Microsoft C
  20887.  
  20888.  
  20889. Microsystems Software Inc. has released CodeRunneR, an optimized library for
  20890. creating Terminate-and-Stay-Resident (TSR) programs with full MS-DOS access,
  20891. using Borland's Turbo C and Microsoft C.
  20892. With CodeRunneR, all program initialization code and data is eliminated when
  20893. the program goes resident.
  20894. Contact Microsystems Software Inc., 600 Worcester Road, Framingham MA 01701
  20895. (508) 626-8511; FAX (508) 626-8515.
  20896.  
  20897.  
  20898.  
  20899. Clarion Offers LEM Maker
  20900.  
  20901.  
  20902. Clarion Software Corp. released the Clarion LEM Maker, a collection of tools
  20903. for creating LEMs from object modules written in Borland International's Turbo
  20904. C.
  20905. Priced at $199 retail, the Clarion LEM Maker includes a Clarion program that
  20906. creates an assembler language interface between Clarion and Turbo C code, an
  20907. extensive library of C functions and two sample LEMs.
  20908. Clarion has also released ZIP Code Language Extension Module (LEM), which
  20909. retails for $199 and offers library and data files for creating software
  20910. applications that retrieve, check, and manipulate ZIP codes and other
  20911. information referenced by ZIP codes. For more information contact Clarion
  20912. Software Corp., 150 E. Sample Road, Pompano Beach, FL 33064 (305) 785-4555;
  20913. FAX (305) 946-1650.
  20914.  
  20915.  
  20916. Netwise Offers University Grants
  20917.  
  20918.  
  20919. Netwise, Inc. has formed the Netwise University Grant Program and the Netwise
  20920. University Discount Program. The grant program entitles qualified researchers
  20921. to receive free Netwise software development products. The discount program
  20922. allows all university applicants to receive a 75 percent discount on the price
  20923. of Netwise products. For more information contact Wayne Moore at Netwise,
  20924. (303) 442-8280.
  20925.  
  20926.  
  20927. New B-Tran Now Available
  20928.  
  20929.  
  20930. Software Translations Inc. (STI) has released v7.5 of its B-Tran Basic to C
  20931. translator, which translates QuickBASIC v4.5 to C source code.
  20932. B-Tran v7.5 is priced from $499 for the Microsoft C compiler under MS-DOS. For
  20933. more information contact Software Translations, Inc., The Carriage House, 28
  20934. Green Street, Newburyport, MA 01950 (508) 462-5523; FAX (508) 462-9198.
  20935.  
  20936.  
  20937. dAnalyst Code Manipulates dBase Files
  20938.  
  20939.  
  20940. dAnalyst for C is the latest addition to the dAnalyst product line from
  20941. Buzzwords International.
  20942. The product includes an application generator which allows users to create C
  20943. applications, such as pop-up and pull-down menus and AT SAY-GETs, with the
  20944. look and feel of dBase IV. The generated code calls a set of high-speed video
  20945. libraries compatible with MS-DOS, Xenix, UNIX, Desqview, the Apex ADL Library
  20946. and the Lattice DBC Library.
  20947. dAnalysts's Report Writer and Source Code Generator generates C to process
  20948. reports on dBase data files. The libraries that work with the application
  20949. generator also work with the report writer. The report writer is also fully
  20950. relational with dAnalyst's Screen Painter.
  20951. The Windowing Editor allows users to split their screen, then cut-and-paste
  20952. code from one side to the other. dAnalyst can convert any single-user dBase
  20953. application to multi-user, without any recoding.
  20954. The dAnalyst Series supports dBase III Plus, dBase IV, FoxBase+, Nantucket's
  20955. Clipper, QuickSilver and C, as well as Xenix and UNIX (under 8086, 68000 or
  20956. '386 platforms).
  20957. For more information, contact Buzzwords International at 2879 Hopper, Cape
  20958. Girardeau, MO 63701 (314) 334-6317.
  20959.  
  20960.  
  20961. Utility Structures FORTRAN Code
  20962.  
  20963.  
  20964. Cobalt Blue has released FOR_STRUCT v1.1, a structuring utility that
  20965. transforms spaghetti FORTRAN-IV and FORTRAN-77 into fully structured code,
  20966. with or without VAX and FORTRAN-8X extensions. FOR_STRUCT is available for
  20967. MS-DOS, Xenix/UNiX/386, Sun-3 and Sun-4.
  20968. The Sun-3 and Sun-4 versions are priced at $1850, Xenix/UNIX/386 are at $1450
  20969. and MS-DOS is $825, and include two months free technical support and
  20970. upgrades.
  20971. Contact Cobalt Blue, 2940 Union Ave., Suite C, San Jose, CA 95124 (408)
  20972. 723-0474.
  20973.  
  20974.  
  20975. Source Level Debugger Works With Aztec, Z180 ICE
  20976.  
  20977.  
  20978. Softaid has released v2.0 of its Source Level Debugger (SLD), which supports
  20979. Aztec C, and an in-circuit emulator for the Zilog Z180 microprocessor, the
  20980. Z180 IceAlyzer.
  20981. Softaid's SLD is a source debugger for the firm's line of in-circuit
  20982. emulators. Running on a PC, it gives the user a multi-window emulator
  20983. interface. The entire state of the user's target system is shown, including
  20984. the registers, disassembled code, stack, memory, and I/O. The debugger shows
  20985. the user's program, whether written in C or assembly language, in its own
  20986. source window.
  20987. All facets of Aztec C are supported. The debugger will display any C variable
  20988. using the type (integer, character, float, etc.) defined in the program.
  20989. Variables local to a function are all automatically shown in the "Watchpoint"
  20990. window.
  20991. The Source Level Debugger is compatible with all of Softaid's emulators. Both
  20992. 8- and 16-bit versions of the compiler are completely supported.
  20993. The Z180 IceAlyzer costs $3090 and the Source Level Debugger is $795. Both are
  20994. available from stock. For more information contact Softaid, Inc., 8930 Route
  20995. 108, Columbia, MD 21045 (301) 964-8455 or (800) 433-8812; FAX (301) 596-1852.
  20996.  
  20997.  
  20998. C + + Class Library Does Matrix Ops
  20999.  
  21000.  
  21001. Rogue Wave has released a set of object-oriented numerical tools that extend
  21002. all of the standard C arithmetic operators to include vectors and matrices.
  21003. Extended versions of the standard C math functions (e.g. cos and abs) are also
  21004. included. Many new functions for statistics and numerical modeling
  21005. applications have also been provided. A complete complex number class is
  21006. included. Fast Fourier Transform server classes allow you to take the FFT or
  21007. inverse FFT of any length series (real or complex). Using the inheritance
  21008. property of C + +, new classes can be created from the Rogue Wave classes to
  21009. do specialized tasks. The classes compile under a variety of C + + compilers
  21010. under both MS-DOS and UNIX.
  21011. A complete 120-page User's Guide and Reference Manual is provided and full
  21012. source code is included for $150.
  21013. For more information, contact Rogue Wave, PO Box 85341, Seattle, WA 98145-1341
  21014. (206) 523-5831.
  21015.  
  21016.  
  21017. UNIX Version 4 Now Available
  21018.  
  21019.  
  21020.  
  21021. UNIX International, Inc. and AT&T's UNIX Software Operation have released UNIX
  21022. system V, v4. The new release unifies the UNIX System installed base providing
  21023. upward compatibility for more than 80 percent of current UNIX System
  21024. installations.
  21025. Version 4's primary advantages are compatibility, portability of software from
  21026. platform to platform, interoperability of software between heterogeneous
  21027. systems and scalability from PCs to mainframes.
  21028. Contact UNIX International, Waterview Corporate Centre, 20 Waterview
  21029. Boulevard, Parsippany, NJ 07054 (201) 263-8400.
  21030.  
  21031.  
  21032. HCR Releases C + + For SCO UNIX
  21033.  
  21034.  
  21035. HCR/C + + for SCO UNIX includes a C + + compiler that is compatible with
  21036. AT&T's v2.0 of C + +, and a window-based source level debugger, dbXtra, based
  21037. on dbx from Berkeley v4.3 BSD.
  21038. HCR/C + + provides type safe linkages, default membership initialization, and
  21039. the ability of each class to define its own operators. It will run on most
  21040. 386-based system platforms.
  21041. HCR's dbXtra adds the ability to operate through windows, permitting users to
  21042. review their output and source code easily, even on standard terminals. In
  21043. HCR/C + +, dbXtra is linked to C + +, allowing direct debugging of C + + and
  21044. window access to the translated C source code. Because all C + + code is
  21045. translated into C before execution, programmers can apply dbXtra to examine
  21046. either C or C + + code during debugging.
  21047. HCR/C + + is $995. Each user of HCR/C + + v1 will also have the option to
  21048. upgrade to v2 for $99 delivered. For more information, contact HCR Corp. at
  21049. (416) 922-1937, 130 Bloor Street West, 10th Floor, Toronto, Ontario M5S 1N5.
  21050.  
  21051.  
  21052. JAM Supports VAX Rdb/VMS
  21053.  
  21054.  
  21055. JYACC's JAM and JAM/OB's front end tools are now available to VAX/VMS users.
  21056. VMS versions of these tools can now be used to design and develop applications
  21057. using Rdb as their database.
  21058. Applications developed with JAM are portable across 50 hardware platforms and
  21059. 10 operating systems.
  21060. The JAM and JAM/DBi development kit is $990 for PCs running MS-DOS. For more
  21061. information contact JYACC, 116 John Street, New York, NY 10038 (212) 267-7722;
  21062. FAX (212) 608-6753.
  21063.  
  21064.  
  21065. Enhanced MIRACL Library Released As Commercial Product
  21066.  
  21067.  
  21068. After a previous existence as Shareware, a more polished v3.0 of the MIRACL
  21069. library is now available as a commercial product. This package allows the C or
  21070. C + + programmer to use multiprecision integer and fractional data-types in
  21071. their programs.
  21072. All routines in the basic MIRACL library are written in standard portable C,
  21073. and the source code is included. A full C + + Interface is provided. The
  21074. MIRACL library has been successfully implemented on the IBM PC, the Apple
  21075. Macintosh, Acorn Archimedes and Digital VAX machines, using a variety of
  21076. compilers. MIRACL is only available on PC compatible diskettes, but can be
  21077. uploaded from there to any computer which supports a full C compiler. The PC
  21078. version needs MS-DOS v2.1 or higher, with a minimum of 256K of memory and a
  21079. 360K floppy disk drive. Introductory Price is IR£50-00 Irish pounds (£45-00
  21080. Sterling) for the PC version on two 5¼ inch diskettes, with full
  21081. documentation.
  21082. For more information, contact Shamus Software Ltd., 94 Shangan Road, Ballymun,
  21083. Dublin 9, Ireland Tel: 425430.
  21084.  
  21085.  
  21086. Oregon C + + New On VAX
  21087.  
  21088.  
  21089. Oregon Software, Inc., has released Oregon C + + for VAX/VMS. Major features
  21090. include a souree-level debugger, the NIH OOPS class library, and support of
  21091. shared libraries and VAX C calling sequence. Oregon C + + can call any DEC
  21092. language as well as Oregon Software's C, Pascal-2 and Modula-2. Oregon C + +
  21093. for VAX/VMS runs on VMS v5.0 and later. The Oregon C + + compiler also
  21094. includes an ANSI C and K&R C compiler at no additional charge. The release
  21095. will include complete compatibility with v2.0 of AT&T's cfront and a task
  21096. library.
  21097. License fees in the US range from $2000 to $34,000 depending on machine,
  21098. cluster or network configuration. Contact Oregon Software, Inc. at 6915 S.W.
  21099. Macadam Ave., Suite 200, Portland, OR 97219 (503) 245-2202; FAX (503)
  21100. 245-8449.
  21101.  
  21102.  
  21103. Blaise Updates C Tools Plus Function Library
  21104.  
  21105.  
  21106. Blaise Computing Inc. has released C Tools Plus v6.0, a library product for
  21107. Microsoft C.
  21108. The Library includes virtual, stackable menus and windows with full mouse
  21109. support and optional "drop shadows"; multiple virtual pop-up help screens; a
  21110. miniature multi-line editor for gathering user responses; a single function
  21111. call which can move, resize, and promote a window or menu on top of all
  21112. others; the ability to update covered windows automatically when they are
  21113. written to; support for EGA, VGA, and MCGA text modes including 30-, 43-, and
  21114. 50-line modes; and support for the enhanced (101/102 key) keyboard. C Tools
  21115. Plus requires Microsoft C v5.0 or later or QuickC, v2.0 or later. The mouse
  21116. functions require a Microsoft-compatible mouse and its driver software.
  21117. C Tools Plus v6.0 is priced at $149.
  21118. Blaise Computing is located at 2560 Ninth Street, Suite 316, Berkeley, CA
  21119. 94710 (415) 540-5441; FAX (415) 540-1938.
  21120.  
  21121.  
  21122. SilverWare Offers Async Library
  21123.  
  21124.  
  21125. SilverWare, Inc. has released the SilverComm C Async Library, an asynchronous
  21126. communication library for C programmers. The library comes with over 125
  21127. communication and 40 advanced functions, and includes free source code,
  21128. comprehensive demo and the Norton Guide Database. Documentation provides an
  21129. example for each function and includes data sheets for 8250, 16450 and 16550
  21130. UARTS. This royalty-free library links directly to your application and
  21131. supports all popular C compilers.
  21132. SilverComm C Async Library is $249. Contact SilverWare, Inc. at 3010 LBJ
  21133. Freeway, Suite 740, Dallas, TX 75234 (214) 247-0131; FAX (214) 406-9999.
  21134.  
  21135.  
  21136. Design/OA Now Works With X Windows
  21137.  
  21138.  
  21139. Meta Software Corporation has released a UNIX X-Window system version of
  21140. Design/OA, a custom CASE application available under X.
  21141.  
  21142. Design/OA is a graphics application development tool designed to build system
  21143. modeling tools. It can be used to develop graphically-based CASE, CAD/CAE,
  21144. object-oriented programming, or simulation applications. It can also be used
  21145. to create graphical front-ends to a database, code generator,
  21146. telecommunications network, or data processing system.
  21147. According to Meta Software's Chief Technical Officer Alan Epstein, the X
  21148. Window version is "X source code compatible and should run on all compatible
  21149. UNIX workstations with little or no modification. We plan to release optimized
  21150. versions for other UNIX workstations including IBM, DEC, and HP/Apollo over
  21151. the next several months."
  21152. Design/OA supports multiple windowing, and offers the developer full control
  21153. of the application's "look and feel" through customization of the graphical
  21154. interface, menus, dialogs, icons, commands and reports. It retails for
  21155. $15,000, and is distributed directly through Meta Software. Both the Apple
  21156. Macintosh and IBM PC versions sell for $7,500; volume discounts are available
  21157. on request, and special prices are offered to educational institutions.
  21158. Contact Meta Software at 150 CambridgePark Drive, Cambridge, MA 02140 (617)
  21159. 576-6920.
  21160.  
  21161.  
  21162. New Asynch Manager Adds File Transfer Protocols
  21163.  
  21164.  
  21165. Blaise Computing Inc. has released C Asynch Manager v3.0 and Asynch Plus v5.0,
  21166. upgrades to its communications toolkits for C and Pascal programmers.
  21167. These new versions add features in two main areas: modem control and file
  21168. transfer.
  21169. The new modem control routines let programs talk to multiple modems,
  21170. supporting the features and peculiarities of each simultaneously.
  21171. The new file transfer capabilities include 1K packets, CRC error checking,
  21172. true Y-Modem (multi-file transfers with file name and size and XMODEM
  21173. preserved), auto switching to incoming packet size and error detection method.
  21174. The file transfer routines have been designed to support background file
  21175. transfers, and multiple files may be sent or received simultaneously over
  21176. multiple ports.
  21177. C Asynch Manager v3.0 requires Microsoft C v4.0, v5.0, or v5.1 or Turbo C
  21178. v1.5, or v2.0. Asynch Plus v5.0 requires Turbo Pascal v4.0, v5.0, or v5.5 or
  21179. QuickPascal. The price of each package is $189.
  21180. For more information contact Blaise Computing, 2560 Ninth Street, Suite 316,
  21181. Berkeley, CA 94710 (415) 540-5441; FAX (415) 540-1938.
  21182.  
  21183.  
  21184. QNX Gets Window System
  21185.  
  21186.  
  21187. Quantum Software Systems Ltd. has released QNX Windows, a graphical user
  21188. interface environment for its QNX operating system.
  21189. QNX Windows' dialog manager handles all the basic interactions that take place
  21190. between the user and the system.
  21191. As an integral component of the operating system itself, QNX Windows is
  21192. server-based. QNX Windows can execute in parallel on the same node in the QNX
  21193. LAN or on remote nodes.
  21194. For more information, contact Quantum Software Systems, Ltd., 175 Terrence
  21195. Matthews Crescent, Kanata, Ont., Canada K2M 1W8 (613) 591-0931; FAX (613)
  21196. 591-3579.
  21197.  
  21198.  
  21199. BSO Offers 8051 Tool Set
  21200.  
  21201.  
  21202. BSO Inc. has released a tool set for the 8051, which is available to run on a
  21203. number of different host platforms. Prices start at $1700 for compiler and
  21204. assembler packages. For more information contact BSO at 411 Waverley Oaks
  21205. Road, Waltham, MA 02154-8414 (800) 458-8276 or (617) 894-7800.
  21206.  
  21207.  
  21208. Quad Offers SQL Tools
  21209.  
  21210.  
  21211. Quadbase Systems, Inc. has released Quadbase-SQL, a relational database
  21212. management system, and dQuery v3.0, an interactive query management system.
  21213. Quadbase-SQL is $795, with no royalties. dQuery v3.0 is $195. Both
  21214. Quadbase-SQL and dQuery v3.0 require MS-DOS v3.1 (or above), 640K of RAM and
  21215. hard disk. They run on any MS-DOS v3.1 compatible LAN system. For more
  21216. information contact Quadbase Systems, Inc., 790 Lucerne Dr., Suite 51,
  21217. Sunnyvale, CA 94086 (408) 738-6989.
  21218.  
  21219.  
  21220. Building Block Releases New QuickGeometry
  21221.  
  21222.  
  21223. Building Block Software has released QuickGeometry Library v1.02, a collection
  21224. of math subroutines for developing programs for CAD/CAM, parametric design, NC
  21225. programming, post processing, finite element analysis and GIS.
  21226. QuickGeometry Library v2 improves the documentation and adds 3D display. It is
  21227. $199 and includes C source code, object code for MS-DOS, documentation,
  21228. working example programs, one hour of telephone support, and a 30-day
  21229. money-back guarantee.
  21230. For more information contact Building Block Software, PO Box 1373, Somerville,
  21231. MA 02144 (617) 628-5217.
  21232.  
  21233.  
  21234. Aspirin Includes Code Generator And Utilities
  21235.  
  21236.  
  21237. Arrowhead Software has released Aspirin, a C development toolkit with
  21238. libraries to support forms, windows, time and date manipulation, text
  21239. manipulations, database management (ISAM), and menus.
  21240. The package also includes a code generator, which provides facilities for
  21241. adding, deleting, and modifying fields, text, lines and boxes. It also
  21242. provides complete control over placement, attributes, and the extended
  21243. character set.
  21244. The Programmer's Utility package contains programs to aid in the development
  21245. and enhancement of programs with features such as text search and replace, C
  21246. function finder, and print utilities. The special ClipCode disk, sent
  21247. quarterly, provides source code for extending and enhancing the basic library.
  21248. Aspirin is available from Arrowhead Software for the introductory price of
  21249. $250. All source code for the Aspirin libraries is included.
  21250. For more information, contact Arrowhead Software, 7500 W. Mississippi Suite
  21251. 201, Lakewood, CO 80226 (303) 922-1300.
  21252.  
  21253.  
  21254. Zortech Implements C + + Version 2.0
  21255.  
  21256.  
  21257.  
  21258. Zortech Inc. has announced its C + + v2.0 Developer's Edition for MS-DOS. The
  21259. C + + Developer's Edition, fully compatible with the AT&T 2.0 specification,
  21260. supports multiple inheritance and type safe linkage and has built-in support
  21261. for EMS.
  21262. The Developer's Edition includes a C + + source level debugger, source code
  21263. for the runtime library, and Zortech's C + + tools v2.0. Each of these
  21264. components may be purchased separately as well.
  21265. Zortech C + + v2.0 is compatible with Microsoft Windows and its portability to
  21266. other C environments (including Microsoft C) has been enhanced. Version 2.0
  21267. features a set of graphics classes and a TSR library that can make many
  21268. applications resident through a simple function call.
  21269. Zortech's C + + Developer's Edition sells for $450. The compiler itself can be
  21270. purchased separately for $199. The other components of the Developer's
  21271. Edition, including the new debugger, the runtime library source code, and
  21272. version 2.0 of C + + tools are available at $149 each. Updates to existing
  21273. users start at $40.
  21274. Zortech has also released its OS/2 compiler upgrade, priced at $149.
  21275. For more information contact Zortech Inc., 1165 Massachusetts Ave., Arlington,
  21276. MA 02174 (617) 646-6703.
  21277.  
  21278.  
  21279. MMC AD Offers Toolboxes
  21280.  
  21281.  
  21282. MMC AD has released C Programmer's Toolbox/PC v2.0 for PC compatibles, C
  21283. Programmer's Toolbox for Apple Macintosh Programmer's Workshop (MPW), and two
  21284. stand-alone programming tools for the Apple Macintosh, McCPrint v2.0 and
  21285. McClint v2.0.
  21286. The C Programmer's Toolbox/PC v2.0 is a set of 21 tools in two volumes. The
  21287. Toolbox works with any PC compatible system that has MS-DOS v2.1 or later. A
  21288. hard disk is highly recommended. The Toolbox is compatible with Microsoft C,
  21289. Quick C, Turbo C and other PC C compilers. Volumes I and II retail for $99.95
  21290. each or $175 for both. For registered toolbox owners, the new release is $30
  21291. per volume or $50 for both.
  21292. The C Programmer's Toolbox/MPW is a set of 20 tools that work with MPW v3.0 or
  21293. later. MPW C is not required. The Toolbox works with any C source code that is
  21294. being developed by or supported through MPW C v2.x or v3.x, Lightspeed C/Think
  21295. C, Aztec C, all PC C compilers, engineering workstation and UNIX C compilers.
  21296. The Toolbox retails for $295.
  21297. McCPrint v2.0 is a C source code beautification/reformatting system that
  21298. includes a source code formatting system, a multiple window editor and source
  21299. code highlighting system. McCPrint works with a Macintosh System v4.2 or later
  21300. (System v6.x is recommended) and at least 512 KB of memory. A hard disk is
  21301. recommended, but not required. McCPrint runs as a stand-alone application and
  21302. fully supports MultiFinder foreground and background processing. McCPrint is
  21303. compatible with all C compilers. McCPrint is $59.95. An update is available
  21304. for existing McCPrint customers for $25.
  21305. McCLint v2.0 is a C source code semantic checking system. McCLint works with a
  21306. Macintosh system v4.2 or later (system v6.x is recommended) and at least 1 MB
  21307. of memory. A hard disk and additional memory is highly recommended. McCLint
  21308. runs as a stand-alone application and fully supports MultiFinder foreground
  21309. and background processing. McCLint is $99.95. An update is available for
  21310. existing McCLint customers for $25.
  21311. For more information contact MMC AD Systems, Box 360845, Milpitas, CA 95035
  21312. (408) 263-0781.
  21313.  
  21314.  
  21315. Lattice Ships Free Amiga Updates
  21316.  
  21317.  
  21318. Lattice, Inc. has released v5.04 of its Lattice AmigaDOS C Compiler.
  21319. Version 5.04 of the Lattice C Compiler for AmigaDOS includes more than 50
  21320. enhancements to the compiler, libraries, codePRobe debugger, and utilities. A
  21321. READ.ME file on the update disk describes the changes and installation
  21322. procedure.
  21323. All registered users of the compiler received the upgrade automatically. For
  21324. more information contact Lattice, Inc. at 2500 South Highland Ave., Lombard,
  21325. IL 60148 (800) 444-4309.
  21326.  
  21327.  
  21328. MIPS RISC Gets API Standard
  21329.  
  21330.  
  21331. AT&T and MIPS have signed an agreement to build a UNIX System V v4.0
  21332. Application Binary Interface for the MIPS RISC microprocessor.
  21333. An Application Binary Interface (ABI) specification tells software vendors how
  21334. to write applications that will run in binary form -- like today's PC
  21335. applications -- on machines from any number of vendors that use the same
  21336. microprocessor architecture.
  21337. Under the agreement, the MIPS ABI will serve as the basis for trademarked UNIX
  21338. System V software for the MIPS architecture.
  21339. AT&T will make the MIPS ABI specifications available to the industry. AT&T
  21340. also said that the ABI specifications for the MIPS architecture will be
  21341. compatible with other AT&T ABI specifications.
  21342. For more information contact MIPS Computer Systems, Inc., 928 Arques Ave.,
  21343. Sunnyvale, CA 94086 (408) 991-7736 or AT&T, 60 Columbia Turnpike, Morristown,
  21344. NJ 07960 (201) 829-7212.
  21345.  
  21346.  
  21347. JYACC Offers Jterm Emulator Package
  21348.  
  21349.  
  21350. JYACC, Inc. has released Jterm, a terminal emulator package.
  21351. Jterm is equipped with a file transfer utility which offers ASCII, Xmodem,
  21352. Kermit and Kermit Server protocols, and JYACC's own protocol, Jtran. Jtran
  21353. incorporates a data compression system that reduces file transfer transmission
  21354. time by fifty percent.
  21355. The Jterm emulation provides application users and developers extensive screen
  21356. control and allows them to take full advantage of PC capabilities such as
  21357. color display, function keys and the PC graphics character set.
  21358. Jterm provides the standard features of a terminal emulation package (direct
  21359. and modem dialing, file transfer with errorchecking, initialization and script
  21360. files, etc.) and DEC VT100, VT220 and TTY emulation modes.
  21361. For more information contact JYACC at 116 John St., New York, NY 10038 (212)
  21362. 267-7722; FAX (212) 608-6753.
  21363.  
  21364.  
  21365. Marietta Updates c_ndx And c_wndw
  21366.  
  21367.  
  21368. Marietta Systems has released the c_ndx relational database library and the
  21369. c_wndw v2 library for Borland Turbo C, Lattice C, Microsoft C 5.x and Quick C.
  21370. c_wndw v2 includes the c_ndx relational database library for dBase files,
  21371. improvements in facility and performance, increased flexibility, and expanded
  21372. manuals.
  21373. The c_wndw and c_ndx libraries are available in object library form for
  21374. Borland Turbo C and Microsoft C compilers at a license fee of $95. Shipping
  21375. and handling is $4.50 for US and Canada, and $11 for overseas customers.
  21376. Source license agreements are available at $195 plus S&H. For more information
  21377. contact Marietta Systems, Inc., PO Box 71506, Marietta, GA 30007 (404)
  21378. 565-1560.
  21379.  
  21380.  
  21381.  
  21382.  
  21383.  
  21384.  
  21385.  
  21386.  
  21387.  
  21388.  
  21389.  
  21390.  
  21391.  
  21392.  
  21393.  
  21394.  
  21395.  
  21396.  
  21397.  
  21398.  
  21399.  
  21400.  
  21401.  
  21402.  
  21403.  
  21404.  
  21405.  
  21406.  
  21407.  
  21408.  
  21409.  
  21410.  
  21411.  
  21412.  
  21413.  
  21414.  
  21415.  
  21416.  
  21417.  
  21418.  
  21419.  
  21420.  
  21421.  
  21422.  
  21423.  
  21424.  
  21425.  
  21426.  
  21427.  
  21428.  
  21429.  
  21430.  
  21431.  
  21432.  
  21433.  
  21434.  
  21435.  
  21436.  
  21437.  
  21438.  
  21439.  
  21440.  
  21441.  
  21442.  
  21443.  
  21444.  
  21445.  
  21446.  
  21447.  
  21448.  
  21449.  
  21450. New Releases
  21451.  
  21452.  
  21453. CUG302 3-D Transforms
  21454.  
  21455.  
  21456. Written by Gus O'Donnell (CA) and submitted by Michael Yokoyama (HI), 3-D
  21457. Transforms is a library of functions used to create, manipulate and display
  21458. objects in three dimensions. The functions allow the programmer to create
  21459. representations of solid objects bound by polygons, to rotate, translate,
  21460. scale the objects in three dimensions, and to display the objects in color
  21461. with a given light source. The disk includes a brief description of each
  21462. function in the library, complete C source code, function libraries for Turbo
  21463. C, and a demonstration program which displays a cube, a tetrahedron, and
  21464. octahedron in three dimensions with each figure rotated about a different axis
  21465. (Figure 1). The program requires a Turbo C graphics library and BGI files.
  21466. Turbo C v1.5 or later is recommended.
  21467.  
  21468.  
  21469. CUG303 MC68K Disassembler
  21470.  
  21471.  
  21472. Written by John M. Collins (England) and submitted by Steven M. Ward (MA),
  21473. MC68K Disassembler runs on Motolora 68000 ports of UNIX System III and V. The
  21474. disassembled output can be assembled to generate the same object module as the
  21475. input. When disassembling stripped executable files, object modules and
  21476. libraries may be scanned, modules in the main input identified and the
  21477. appropriate names automatically inserted into the output. Also, an option is
  21478. available to convert most non-global names into local symbols, reducing the
  21479. number of symbols in the generated assembler file.
  21480. The disassembler copes reasonably with modules merged with the -r option to
  21481. ld, generating a warning message as to the number of modules involved. The
  21482. disk includes a users guide and complete C source code. Although the program
  21483. is MC68000 specific, it is easily adaptable to run in most any operating
  21484. system environment as a cross development tool.
  21485.  
  21486.  
  21487. CUG304 ROFF5
  21488.  
  21489.  
  21490. Ernest E. Bergmann (PA) has completed a major rewrite of his ROFF4 (CUG128 and
  21491. CUG145). The ROFF5, v2.00 technical text formatter has evolved from ROFF4 to
  21492. become somewhat more like UNIX's nroff and troff. ROFF5 now supports
  21493. conditional macros, page traps, roman numerals and line numbering. It is
  21494. intended for preparation of manuscripts on any dot matrix printer and can
  21495. handle equations and special symbols. Different ouput devices are supported
  21496. with device-specific ASCII files that inform ROFF5 of the special controls for
  21497. that device. Fractional line spacing for superscripts and subscripts are
  21498. supported even for printers that cannot reverse scroll. The "built-in"
  21499. commands follow the naming conventions of nroff and troff where appropriate;
  21500. however, in contrast to the UNIX formatters, ROFF5 supports register and macro
  21501. names of arbitrary length. The disk includes a complete set of C source code,
  21502. well-written documentation, and a number of test and demo files. The program
  21503. was written using Turbo C v2.0 for MS-DOS.
  21504.  
  21505.  
  21506. CUG305 HGA Mandelbrot Explorer and Card Games
  21507.  
  21508.  
  21509. Dan Schechter has submitted a Hercules monochrome Mandelbrot program, as well
  21510. as the card games, poker and blackjack. Unlike most Mandelbrot programs, which
  21511. require you to specify "color-value" information in advance, his programs,
  21512. EMANDEL and EJULIA (Figure 2) save all calculation data, allowing you to tweak
  21513. the picture by specifying color-value information afterwards.
  21514. POKER is five-card draw poker. The computer plays four hands independently
  21515. (the computer's four "players" do not consult with each other) and you play
  21516. one hand.
  21517. BLACKJACK is not quite real casino blackjack. It is just you against the
  21518. dealer. "Doubling down" is not supported. The screen display of both card
  21519. games is neatly organized using the Hercules graphics.
  21520. This disk includes C source codes as well as executables for MS-DOS. All the
  21521. programs are compiled using the Aztec C compiler.
  21522.  
  21523.  
  21524. CUG306 Thread and Synapsys
  21525.  
  21526.  
  21527. Gregory Colvin (CO) has contributed Thread and Synapsys. Thread is a
  21528. multitasking kernel based on lightweight threads. (See his story elsewhere in
  21529. this issue.) He uses the ANSI Standard C library functions, setjmp() and
  21530. longjmp() to implement multiple threads within a single C program. He has
  21531. tested the code with Microsoft C v5.0 on an IBM-AT, with MPW C v3.0 on a
  21532. Macintosh SE. On his AT machine, the kernel compiles to under 1K of code and
  21533. executes over 80,000 jumps per second.
  21534. Synapsys is a neural network simulation program which implements a very fast
  21535. backpropagation network by representing synapse layers as word arrays and
  21536. implementing all operations with integer arithmetic.
  21537. The disk includes C source code, benchmark and testing code for both programs.
  21538.  
  21539.  
  21540. Updates
  21541.  
  21542.  
  21543.  
  21544.  
  21545. CUG252, 253 C Tutor
  21546.  
  21547.  
  21548. Coronado Enterprises (12501 Coronado Ave NE, Albuquerque, NM 87122) has
  21549. released C Tutor v2.4. This new C Tutor has been modified to include many of
  21550. the proposed ANSI standard changes. CUG252 includes documentation and CUG253
  21551. includes source code. CUG257 and CUG258, C Tutor for Turbo C are not included
  21552. in the revision. We will retain them for a few months as a resource for
  21553. programmers working with older versions of Turbo C. Eventually convergence to
  21554. the ANSI standard should allow us to retire these volumes.
  21555.  
  21556.  
  21557. CUG263 c_wndw and c_ndx
  21558.  
  21559.  
  21560. This v2.02 release from Marietta Systems includes the "c_ndx" library that
  21561. supports relational database access to dBase files and B-tree indexes. This
  21562. shareware package includes a manual, sample programs, and small model library
  21563. for Turbo C and Quick C. The source code is available from the author (2917
  21564. Ashebrooke Dr, Marietta, GA 30068).
  21565.  
  21566.  
  21567. CUG265 cpio Installation Kit
  21568.  
  21569.  
  21570.  
  21571. Good news for AT&T 3B1 users. In the past, 3B1 users have been unable to read
  21572. CUG disks even though our physical disk format (48 tpi, 8 sectors/track, 512
  21573. bytes/sector) matches theirs. There seems to be some incompatibility between
  21574. their UNIX on 3B1 (it is okay on 3B2) and our SCO XENIX/386. T.W Kalebaugh
  21575. (KS) has created a loader and dump utility for AT&T 3B1 (UNIX PC, 7300 and
  21576. Convergent Technologies S-50). The updated disk includes his new subroutines
  21577. and makefiles.
  21578.  
  21579.  
  21580. CUG278 CXL Library
  21581.  
  21582.  
  21583. Mike Smedley has updated his shareware C function Library, CXL to v5.1. The
  21584. update includes new features such as a context-sensitive help system,
  21585. extensive mouse support, shadowed windows, multiple-field data entry forms,
  21586. enhanced menuing functions, extended keyboard support, and file encryption.
  21587. This disk includes a manual, demo programs, small model library for Microsoft
  21588. C & Quick C, Turbo C and Zortech C/C++. The source code is available from the
  21589. author (P.O. Box 33603, San Antonio, TX 78265). Additionaly, Kamran Bayegan
  21590. has contributed a "Screen and Form Designer" program which designs screens and
  21591. forms that are completely compatible with this library.
  21592.  
  21593.  
  21594. CUG297 Small Prolog
  21595.  
  21596.  
  21597. Henri de Feraudy (France) has updated his original Small Prolog. The updated
  21598. disk includes some minor bug fixes, a speed improvement involving prunify.c
  21599. and prhash.c, a better handling of type predicates such as integer, and three
  21600. new examples. A review of the earlier version appears elsewhere in this issue.
  21601. Figure 1
  21602. Figure 2
  21603.  
  21604.  
  21605.  
  21606.  
  21607.  
  21608.  
  21609.  
  21610.  
  21611.  
  21612.  
  21613.  
  21614.  
  21615.  
  21616.  
  21617.  
  21618.  
  21619.  
  21620.  
  21621.  
  21622.  
  21623.  
  21624.  
  21625.  
  21626.  
  21627.  
  21628.  
  21629.  
  21630.  
  21631.  
  21632.  
  21633.  
  21634.  
  21635.  
  21636.  
  21637.  
  21638.  
  21639.  
  21640.  
  21641.  
  21642.  
  21643.  
  21644.  
  21645.  
  21646.  
  21647.  
  21648.  
  21649. We Have Mail
  21650. Dear Mr. Ward,
  21651. I am delighted to hear that you will be publishing every month now. While it
  21652. may be possible to get too much of a good thing, one C Users Journal per month
  21653. is still a long way from too much (perhaps your staff has a different view).
  21654. There are two things in the November issue that cause me to write. First is
  21655. Jay Martin Anderson's generally excellent overview of the IEEE-488 interface
  21656. bus. Normally, using "HP-IB" and either "GPIB" or "IEEE-488" as synonyms
  21657. causes no great confusion. However, I think that Prof. Anderson's article may
  21658. be an exception to that rule. Let me provide the precise meaning of each term,
  21659. and then indicate why I believe that the equivocation of these terms has
  21660. presented a problem in his piece.
  21661. "IEEE-488" is the generic way of identifying the standards document, "IEEE
  21662. Standard Digital Interface for Programmable Instrumentation," produced by the
  21663. IEEE. The designation "GPIB" (General Purpose Interface Bus) is the generic
  21664. term for the interface bus. "HP-IB" (Hewlett-Packard Interface Bus) is a term
  21665. used by Hewlett Packard to designate both the IEEE-488 electronic standard and
  21666. HP's software protocol for using the interface.
  21667. The most serious error in the article that comes from equating HP-IB with
  21668. IEEE-488 is the assertion that "any instrument which claims adherence to the
  21669. IEEE-488 standard must be able to respond to a serial poll." (page 29). In
  21670. fact, the IEEE-488 standard specifies many allowable subsets of the full
  21671. interface. Included among these are nine "Allowable Subsets to T Interface
  21672. Function" (table 11 of the IEEE-488 standard.) "T" is the basic talker
  21673. function. (There is a precisely analogous TE -- extended talker -- with the
  21674. same nine subdivisions of that function). Of those allowable functions, five
  21675. do not support serial polling. However, one is the degenerate case of no
  21676. talker capability, TO. Obviously, any instrument that cannot talk cannot
  21677. answer a serial poll. So, after acknowledging that it is legal for an IEEE
  21678. device to not support talking at all, the more accurate picture is that half
  21679. of the talker options that "adher[e] to the IEEE-488 standard" do not support
  21680. serial polling. While it is true that HP-IB uses serial polling, there is no
  21681. such requirement from the IEEE.
  21682. I do not want to exaggerate the significance of Prof. Anderson having elided
  21683. the HP-IB and the IEEE-488 specifications into one. In general, his article is
  21684. an excellent introduction to the HP-IB. However, it is worth recognizing that
  21685. it is not an introduction to the GPIB as such.
  21686. The second thing in the November issue that I wanted to respond to was Jeff
  21687. Saraiva's request in "Q?A!" for programming examples using the Microsoft C
  21688. v5.1 compiler's graphics library. I am sending a copy of my FTGRAPH, which is
  21689. a tool kit of FFT functions. It uses the MSC 5.1 graphics library for its
  21690. screen output (MSC_GRPH.C is the source file). While it is not a particularly
  21691. extensive graphics application, it does illustrate determining the graphics
  21692. adapter at runtime, and scaling the output to the actual adapter's resolution.
  21693. It also shows how to use both text and graphics with the graphics library. You
  21694. may include it in the C Users Group library if you think that it is suitable.
  21695. You should note, however, that the front-end (FTGRAPH.C) is quite ragged. The
  21696. library routines have evolved over the last few years to meet my employer's
  21697. needs, but the front-end was written on my own time as a way of providing a
  21698. tool kit to accompany an article on FTs that I wrote for Intelligent
  21699. Instruments and Computers. I think the library is a reasonably polished,
  21700. professional product. The front-end is a good example of what you get for
  21701. nothing. I include a reprint of the article, in case Mr. Saraiva is not
  21702. familiar with the FT and what it can be used for. I hope it is not too trivial
  21703. a graphics application to be useful to him.
  21704. Sincerely,
  21705. Tom Clune
  21706. Eye Research Institute
  21707. 20 Staniford St.
  21708. Boston, MA 02114
  21709. Thanks for the HPIB/IEEE 488 clarification. We have passed your graphics
  21710. library on to Kenji for evaluation. We'll also pass a copy immediately to
  21711. Saraiva. I think "How to determine adapter type at runtime" would be a good
  21712. article by itself. Any authors? --rlw
  21713. Dear Robert,
  21714. I just finished reading the Jan 1990 issue of CUJ (again, an excellent job,
  21715. guys). I want to respond to one of your reader's (Dr. Whitaker of Boston, MA)
  21716. requests for texts on "grep", "awk", "sed" and "tr" as well as to one of the
  21717. articles which I found to be most interesting.
  21718. First the texts.
  21719. 1. AT&T UNIX Programmer's Manual Volume 4 titled Document Preparation, edited
  21720. by Steven V Earhart, a CBS College Publishing by Holt, Rinehart and Winston
  21721. (HRW), ISBN 0-03-011207-9
  21722. 2. AT&T UNIX Programmer's Manual Volume 5 titled Languages and Support Tools,
  21723. edited by Steven V. Earhart, a CBS College Publishing by Holt, Rinehart and
  21724. Winston (HRW), ISBN 0-03-011204-4
  21725. These two texts are probably the most complete descriptions of the utilities
  21726. in question and describe everything you ever wanted (and never wanted) to know
  21727. about them, complete with examples and option descriptions.
  21728. The next text I would recommend is UNIX Utilities by R. S. Tare published by
  21729. McGraw-Hill, ISBN 0-07-062884-X
  21730. This book is a programmer's reference and makes some assumptions about how
  21731. much the reader knows about programming in general. This book would probably
  21732. not be a good teaching guide but it's a great reference.
  21733. Lastly, I would recommend the following Bell Laboratories, technical
  21734. memoranda.
  21735. 1. SED -- A Non-interactive Text Editor, by Lee E. McMahon, dated August 15,
  21736. 1978.
  21737. 2. AWK -- A Pattern Scanning and Processing Language, by Alfred Aho, Brian
  21738. Kernighan and Peter Weinberger, dated September 1, 1978.
  21739. I realize that these two documents might be more difficult to get ahold of,
  21740. but they are excellent user guides and no more than 10 pages.
  21741. Next I would like to present an "addendum" to a very well written article
  21742. entitled UNIX 'termcap' Facility Improves Portability by Ronald Florence. I
  21743. realize that the article was about the 'termcap' facility but since he did
  21744. mention the 'terminfo' facility, I wanted to present some additional
  21745. information about it, to you. If after reading this letter you think that a
  21746. more "in-depth" article or tutorial about it may be of interest to your
  21747. readers, I would be more than happy to contribute.
  21748. First let me say that none of this information applies to any UNIX versions
  21749. prior to UNIX System v2 but I would strongly recommend upgrading to, at least,
  21750. UNIX System v3 as soon as possible. The added security measures and bug fixes
  21751. are well worth it! Well, back to the article.
  21752. Mr. Florence stated in his article that "The termcap database is substantially
  21753. easier to modify..." than the terminfo database. I must disagree with this
  21754. statement. On most (if not all) UNIX systems that use 'termcap', the database
  21755. can only be modified by the system administrator (or super user) and rightly
  21756. so. If you, yourself, are not the super user, experimental (trial and error)
  21757. modifications to terminal descriptions are impractical to say the least. With
  21758. 'terminfo', the user is free to experiment with a terminal description that
  21759. only he or she will use (at least until it's fully tested).
  21760. In order to write a 'terminfo' terminal description, you will need at least
  21761. the following: section 4 of the UNIX programmer's manual (TERMINFO(4)) and the
  21762. technical reference for the particular terminal you wish to build a
  21763. description for. Only with this information is it possible to write a terminal
  21764. description.
  21765. In order to use the "new" terminal description, it must be compiled using
  21766. TIC(1M) the terminfo compiler. The procedure is simple, once the terminal
  21767. description file is complete, just type
  21768. tic filename
  21769. and this will create subdirectories (one for each unique terminal name in the
  21770. first line, i.e., at386 makes directory "a", AT386 makes "A" and so on) and
  21771. the compiled file is placed in the subdirectory under the terminal name and
  21772. any appropriate links are made in the other subdirectories.
  21773. In order to make use of this file, the user must define and export the
  21774. environment variable TERMINFO equal to the directory under which the
  21775. subdirectories were created. The user must also define and export the TERM
  21776. variable equal to the appropriate terminal name.
  21777. For example, in the users ".profile" file have the following:
  21778. # (assuming "termdefs" contains the description file)
  21779. TERMINFO=${HOME}/termdefs
  21780. TERM= at386
  21781. export TERM TERMINFO
  21782. Programs using "curses" and "terminfo" routines will check for the TERMINFO
  21783. variable to be set first, before checking the standard terminal description
  21784. database.
  21785. Much of this information can be found in the various sections of the UNIX
  21786. manuals and there are also several books (and memoranda) on the subject. For
  21787. the convenience of your readers, I have enclosed a couple of sample listings
  21788. for 'terminfo' descriptions.
  21789. Listing 1 is the 'terminfo' description supplied by most UNIX System V/386
  21790. vendors for the 80386 based IBM PC/AT console. It should be noted that the
  21791. description supplied by most UNIX SysV/386 vendors is INCORRECT! The "xt"
  21792. boolean (destructive tabs) should be removed as it will cause problems with
  21793. programs like GNU Emacs and others.
  21794. Listing 2 is the same 'terminfo' description using the long C variable names
  21795. listed in <term.h>. This is a much clearer example of the terminal description
  21796. information.
  21797. I hope you and/or your readers will find this information of some use. If you
  21798. have any questions or wish to contact me, you can do so at the address or via
  21799. e-mail at uunet!rwbix!cci.
  21800. Sincerely,
  21801. Bob Barrett
  21802. Principal Consultant at CCI
  21803. 528 North Riverside Dr.
  21804. Neptune, NJ 07753
  21805. I have always found termcap and curses to be the most difficult-to-learn parts
  21806. of UNIX, mostly because the documentation is so scattered and patchy. In
  21807. addition to your references, Kochan and Wood's book "Topics In C Programming"
  21808. includes several little tidbits (like when to use clearok()) that I haven't
  21809. found elsewhere, and Rochkind's "Advanced C Programming for Displays" includes
  21810. good advice about using termcap directly and some interesting performance
  21811. comparisons between new and old termcap and curses.--rlw
  21812. Dear Mr. Ward,
  21813. I've started an interactive curses based program that calculates topological
  21814. chemical indexes as suggested by an article in the Scientific American
  21815. Magazine (Sept. 1986, p.43). A graphics editor for drawing organic structures
  21816. using commands loosely named after those in emacs is called. The editor also
  21817. uses a small library that includes standard subunits like benzene rings,
  21818. steroids, etc. Structures can be named and saved. The drawing can be modified,
  21819. renamed, and recalculated. Only the randic index calculator has been finished.
  21820. I have considered converting the program into a filter that would pipe the
  21821. index numbers to a statistical program to check for correlation with various
  21822. chemical or biological activities. I know that various systems are used to
  21823. translate standard chemical names into codes that are machine readable but I
  21824. don't know which one is the de facto standard.
  21825. My machine is a UNIX PC, PC 3700 (System v3.5 software) but I've avoided
  21826. menus, windows, and the mouse in favor of portability. I wrote the program in
  21827. standard K&R C.
  21828. I doubt that there is enough interest to add this package to the CUG standard
  21829. distribution. Since there are so many design considerations, I would like to
  21830. contact some chemists interested in this theoretical tool so that I could
  21831. implement it to be useful for their academic use.
  21832. Sincerely,
  21833. Phil Karn, SR
  21834. 230 Division Ave.
  21835.  
  21836. Lutherville, MD 21093
  21837. Dear Mr. Ward:
  21838. I am a recent subscriber to The C Users Journal and let me start by saying
  21839. that I think you have a great publication. Here are some topics which I would
  21840. enjoy reading about in future issues. Since I am an MS-DOS user, most of these
  21841. topics are oriented towards that environment.
  21842. Video and printer drivers. I would like for my programs to take advantage of
  21843. the hardware capabilities of different printers and video cards. Both
  21844. Microsoft Windows and Borland's BGI provide a method for doing this, but I
  21845. would prefer to use my own code. What I would really like to see discussed is
  21846. how to write device drivers which can be selected during the execution of a
  21847. program.
  21848. OCR. What are the current methods used to perform optical character
  21849. recongition. I realize that this is too big a topic for extensive coverage,
  21850. but an introductory tutorial would be very nice.
  21851. Speech Synthesis. What can be done to add speech to programs? I realize that
  21852. this is usually done with special hardware, but am curious as to what can be
  21853. done with just a standard PC. It seems that the commercial game programs keep
  21854. getting better and better sound using a standard computer.
  21855. Timing. How can I write programs which are independent of the clock speed of
  21856. the machine being run on. For some events, such as animation, the real time
  21857. clock does not give enough precision to control the timing. The commercial
  21858. games seem to have solved this problem as well.
  21859. Lynn Akers, Jr.
  21860. Akersoft, Inc.
  21861. 5600 Roswell Rd. Ste. 200B
  21862. Atlanta, GA 30342
  21863. Talk about timing! Surely you'll notice the speech recognition article in this
  21864. issue. Phyllis Lang wrote a story about "Improving Timing Resolution" which
  21865. appeared in our May 1989 issue. I'm sure that story would address your timing
  21866. needs. We have sold out of that issue, but can still supply a photo copy (for
  21867. a small fee). Just call and ask for Phyllis Lang's story from Vol. 7, Issue 5.
  21868. --rlw
  21869. Dear Ward Folks;
  21870. Where do you buy your drugs? $28 for your magazine! Not a chance, if it costs
  21871. so much to produce it why did you go to coated pages and a color cover? Leave
  21872. me the old style, charge me less and maybe we can work something out.
  21873. Best of luck (Ha!)
  21874. Tom Brusehaver
  21875. 1505 Ensign Dr. #C
  21876. Normal, IL 61761
  21877. P.S. No one asked me if I wanted the format to change. I would have said NO!
  21878. In fact, the coated paper we are now using is less expensive than the offset
  21879. stock we used to use. The change in price was designed to cover the additional
  21880. issues. If $24 for eight was reasonable, I fail to understand why $28 for
  21881. twelve isn't.
  21882. Frankly I don't think the price requires much defense. For $28 we deliver
  21883. roughly 1500 pages of technical coverage. Even discounting non-editorial space
  21884. you still get over 1000 pages of technical material. Have you priced any 300
  21885. page technical books lately? Have you bought a large pizza recently?
  21886.  
  21887. Listing 1
  21888. /* Lines ending with a '\' character are broken for readability. In
  21889. practice, this should all be on ONE line. */
  21890.  
  21891. AT386at386386AT386atat/386 console,
  21892. am, bw, eo, xon, xt,
  21893. colors#8, cols#80, lines#25, ncv#3, pairs#64,
  21894. acsc= ''a1fxgqh0jYk?lZm@nEooppqDrrsstCu4vAwBx3yyzz{{}}~~,
  21895. bel=^G, blink=\E[5m, bold=\E[1m, clear=\E[2J\E[H,
  21896. cr=\r, cub=\E[%p1%dD, cub1=\E[D, cud=\E[%p1%dB,
  21897. cud1=\E[B, cuf=\E[%p1%dC, cuf1=\E[C,
  21898. cup=\E[%i%p1%02d;%p2%02dH, cuu=\E[%p1%dA, cuu1=\E[A,
  21899. dch=\E[%p1%dP, dch1=\E[P, d1=\E [%p1%dM,dl1=\E[1M,
  21900. ed=\E[J, el=\E[K, flash= ^G, home=\E[H, ht=\t,
  21901. ich=\E[%p1%d@, ich1=\E[1@, il=\E[%p1%dL, il1=\E[1L,
  21902. ind=\E[S, indn=\E[%P1%dS, invis=\E[9m,
  21903. is2=\E0;10;39m, kbs=\b, kcbt=^], kclr=\E[2J,
  21904. kcub1=\E[D, kcud1=\E[B, kcuf1=\E[C, kcuu1=\E[A,
  21905. kdch1=\E[P, kend=\E[Y, kf1=\EOP, kf10=\EOY, kf11=\EOZ,
  21906. kf12=\EOA, kf2=\EOQ, kf3=\EOR, kf4=\EOS, kf5=\EOT,
  21907. kf6=\EOU, kf7=\EOV, kf8=\EOW, kf9=\EOX, khome=\E[H,
  21908. kich1=\E[@, knp=\E[U, kpp=\E[V, krmir=\EO, op=\E[0m,
  21909. rev=\E[7m, rin=\E[S, rmacs=\E[10m, rmso=\E[m,
  21910. rmul=\E[m,
  21911. setb=\E[%?%p1%{0}%=%t40m%e%p1%{1}%=%t44m%e%p1%{2}%=%t42m%e%p1 \
  21912. %{3}%=%t46m%e%p1%{4}%=%t41m%e%p1%{5}}%=%t45m%e%p1%{6}%=%t43m%e%p1 \
  21913. %{7}%=%t47m%;,
  21914. setf=\E[%?%p1%{0}%=%t30m%e%p1%{1}%=%t34m%e%p1%{2}%=%t32m%e%p1 \
  21915. %{3}%=%t36m%e%p1%{4}%=%t31m%e%p1%{5}%=%t35m%e%p1%{6}%=%t33m%e%p1 \
  21916. %{6}%=%t33m%e%p1%{7}%=%t37m%;,
  21917. sgr=\E[10m\E[0%?%p1%p3%%t;7%;%?%p2%t;4%;%?%p4%t;5%;%?%p6%t; \
  21918. 1%;%?%p9%t;12%;%?%p7%t;9%;m,
  21919. sgr0=\E[0;10m, smacs=\E[12m, smso=\E[7m, smul=\E[4m,
  21920.  
  21921.  
  21922. Listing 2
  21923. /* Note: Lines ending with a '\' character are broken for readability.
  21924.  
  21925. In practice, this should all be on ONE line.
  21926.  
  21927. Terminal type at386
  21928. AT86at386386AT386atat/386 console
  21929. flags
  21930. auto_left_margin, auto_right_margin, dest_tabs_magic_smso,
  21931. erase_overstrike, xon_xoff,
  21932.  
  21933. numbers
  21934. columns = 80, lines = 25, max_colors = 8, max_pairs = 64,
  21935. no_color_video = 3,
  21936.  
  21937. strings
  21938. acs_chars = 
  21939. '''a1fxggh0jYk?lZm@nEooppqDrrsstCu4vAwBx3yyzz{{}}~~',
  21940. bell = '^G', carriage_return = '\r', clear_screen = '\E[2J\E[H',
  21941. clr_eol = '\E[K', clr_eos = '\E[J',
  21942. cursor_address = '\E[%i%p1%02d;%p2%02dH', cursor_down = '\E[B',
  21943. cursor_home = '\E[H', cursor_left = '\E[D',
  21944. cursor_right = '\E[C', cursor_up = '\E[A',
  21945. delete_character = '\E[P', delete_line = '\E[1M',
  21946. enter_alt_charset_mode = '\E[12m', enter_blink_mode = '\E[5m',
  21947. enter_bold_mode = '\E[1m', enter_reverse_mode = '\E[7m',
  21948. enter_ secure_mode = '\E[9m', enter_standout_mode = '\E[7m',
  21949. enter_underline_mode = '\E[4m',
  21950. exit_alt_charset_mode = '\E[10m',
  21951. exit_attribute_mode = '\E[0;10m', exit_standout_mode ='\E[m',
  21952. exit_underline_mode = '\E[m', flash_screen = '^G',
  21953. init_2string = '\E[0;10;39m', insert_characcter = '\E[1@',
  21954. insert_line = '\E[1L', key_backspace = '\b', key_btab = '^]',
  21955. key_clear = '\E[2J', key_dc = '\E[P', key_down = '\E[B',
  21956. key_eic = '\EO', key_end = '\E[Y', key_f1 = '\EOP',
  21957. key_f10 = '\EOY', key_f11 = '\EOZ', key_f12 = '\EOA',
  21958. key_f2 = '\EOQ', key_f3 = '\EOR', key_f4 = '\EOS',
  21959. key_f5 = '\EOT', key_f6 = '\EOU', key_f7 = '\EOV',
  21960. key_f8 = '\EOW', key_f9 = '\EOX', key_home = '\E[H',
  21961. key_ic = '\E[@', key_left = '\E[D, key_npage = '\E[U',
  21962. key_ppage = '\E[V', key_right = '\E[C', key_up = '\E[A',
  21963. orig_pair = '\E[0m', parm_dch = '\E[%p1%dP',
  21964. parm_delete_line = '\E[%p1%dM', parm_down_cursor = '\E[%p1%dB',
  21965. parm_ich = '\E[%p1%d@', parm_index = '\E[%P1%dS',
  21966. parm_insert_line = '\E[%p1%dL', parm_left_cursor = '\E[%p1%dD',
  21967. parm_right_cursor = '\E[%p1%dC', parm_rindex = '\E[S',
  21968. parm_up_cursor = '\E[%p1%dA', scroll_forward = '\E[S',
  21969. set_attributes = '\E[10m\E[0%?%p1%p3%%t;7%;%?%p2%t;4%;%?%p4%t;5%; \
  21970. %?%p6%t;1%;%?%p9%t;12%;%?%p7%t;9%m',
  21971. set_background = '\E[%?%p1%{0}%=%t40m%e%p1%{1}%-%t44m%e%p1 \
  21972. %{2}%=%t42m%e%p1%{3}%=%t46m%e%p1%{4}%=%t41m%e%p1%{5}%=%t45m%e%p1 \
  21973. %{6}%=%t43m%e%p1%{7}%=%t47m%;',
  21974. set_foreground = '\E[%?%p1%{0}%=%t30m%e%p1%{1}%=%t34m%e%p1 \
  21975. %{2}%=%t32m%e%p1%{3}%=%t36m%e%p1%{4}%=%t31m%e%p1%{5}%=%t35m%e%p1 \
  21976. %{6}%=%t33m%e%p1%{6}%=%t33m%e%p1%{7}%=%t37m%;',
  21977.  
  21978. tab = '\t',
  21979. end of strings
  21980.  
  21981.  
  21982.  
  21983.  
  21984.  
  21985.  
  21986.  
  21987.  
  21988.  
  21989.  
  21990.  
  21991.  
  21992.  
  21993.  
  21994.  
  21995.  
  21996.  
  21997.  
  21998.  
  21999.  
  22000.  
  22001.  
  22002.  
  22003.  
  22004.  
  22005.  
  22006.  
  22007.  
  22008.  
  22009.  
  22010.  
  22011.  
  22012.  
  22013.  
  22014.  
  22015.  
  22016.  
  22017.  
  22018.  
  22019.  
  22020.  
  22021.  
  22022.  
  22023.  
  22024.  
  22025.  
  22026.  
  22027.  
  22028.  
  22029.  
  22030.  
  22031.  
  22032.  
  22033.  
  22034.  
  22035.  
  22036.  
  22037.  
  22038.  
  22039.  
  22040.  
  22041.  
  22042.  
  22043.  
  22044.  
  22045.  
  22046.  
  22047.  
  22048. Discrete Event Simulation In C For Real-Time Systems
  22049.  
  22050.  
  22051. Steve Halladay and Steve Johnson
  22052.  
  22053.  
  22054. This article is not available in electronic form.
  22055.  
  22056.  
  22057.  
  22058.  
  22059.  
  22060.  
  22061.  
  22062.  
  22063.  
  22064.  
  22065.  
  22066.  
  22067.  
  22068.  
  22069.  
  22070.  
  22071.  
  22072.  
  22073.  
  22074.  
  22075.  
  22076.  
  22077.  
  22078.  
  22079.  
  22080.  
  22081.  
  22082.  
  22083.  
  22084.  
  22085.  
  22086.  
  22087.  
  22088.  
  22089.  
  22090.  
  22091.  
  22092.  
  22093.  
  22094.  
  22095.  
  22096.  
  22097.  
  22098.  
  22099.  
  22100.  
  22101.  
  22102.  
  22103.  
  22104.  
  22105.  
  22106.  
  22107.  
  22108.  
  22109. External Tools For Debugging C
  22110.  
  22111.  
  22112. Bob Whitten
  22113.  
  22114.  
  22115. Bob Whitten is a senior software engineer for X O Technologies, Inc.,
  22116. Valencia, CA, a manufacturer of turbine flow meters, transmitters for flow
  22117. meters, and flow totalizers and controllers. A programmer for 10 years, Bob
  22118. has been involved in many embedded systems, especially "system" code. He can
  22119. be reached at (805) 257-5542.
  22120.  
  22121.  
  22122. Though my favorite debugging environment is Turbo Debug, most of my projects
  22123. are not MS-DOS-based, running instead on a microcontroller tied directly and
  22124. intimately to the surrounding hardware. Usually, as soon as the prototype
  22125. hardware is available, an effort is made to get something running on it, to
  22126. see that the hardware works and to give us and management a good feeling that
  22127. the project is going well. After all, if the hardware works, then the project
  22128. is half-done, right? (This is where the software team lets out a loud
  22129. groan...) When that first something is running, does it work? Does it do
  22130. everything expected? More importantly, can you test the incremental software
  22131. builds as they are produced?
  22132. Sometimes the hardware doesn't work, or not as specified. Or the software team
  22133. interpreted the spec one way, and the hardware team went the other way. (Of
  22134. course, the argument goes that this should have all come out during the
  22135. technical walk-throughs, but since everybody thought they understood it,
  22136. nobody mentioned it.) Usually, an LSI interface chip is involved and the
  22137. documentation on it seemed clear, but later it turns out not to work the way
  22138. everyone thought.
  22139. Other times the software doesn't work, usually because the software team isn't
  22140. talking together enough (or in the case of a one-person job, the software
  22141. engineer isn't talking to himself enough). Embedded programs can be tricky to
  22142. write and even trickier to debug. I've been writing (and debugging) programs
  22143. of this sort for a while now, and in this article I'd like to share what I've
  22144. learned of how to use "external" tools in debugging. Three main tools -- an
  22145. Oscilloscope (and its kid brother, the logic probe), a Logic Analyzer, and an
  22146. In-Circuit Emulator provide very different levels of help, and each has its
  22147. place.
  22148.  
  22149.  
  22150. Using An Oscilloscope
  22151.  
  22152.  
  22153. An oscilloscope displays, or "traces", electrical signals from one or more
  22154. input channels on a cathode-ray tube, showing how these signals change during
  22155. a given time interval. 'Scopes have lots of knobs and switches, so they are a
  22156. practical tool if you already know how to use them or have a good working
  22157. relationship with someone who does. Attaching 'scope probes to hardware,
  22158. especially prototype hardware, can threaten your job security, so I usually
  22159. try to find someone else to do it. The oscilloscope can be useful mostly
  22160. because it has a "trigger" circuit, which can be set to initiate a trace
  22161. either when a signal goes high or when it goes low. The trigger once or
  22162. repeatedly. Repeatedly is the normal setting since the image traced on the
  22163. screen fades quickly; a signal that is repeated often will appear brighter.
  22164. Sometimes the challenge of using a 'scope is making the program cycle on a
  22165. regular enough basis to get a readable trace.
  22166. The 'scope's screen is calibrated in centimeters, with voltage measurements on
  22167. the y-axis, and time on the x-axis. You can select both the voltage range and
  22168. the timing range. For digital circuits, the voltage range should be set to
  22169. conveniently display zero to five volts.
  22170. Since the information displayed on the 'scope is limited to a couple of
  22171. channels (two bits), it seems almost useless. It's amazing what a simple tool
  22172. can do in the hands of skilled person, however, and the 'scope is no
  22173. exception. I've been fortunate to work with people that seem to make the
  22174. 'scope sing a ballad.
  22175. For example, if you're programming a microcontroller, sometimes it's enough to
  22176. know whether or not the code reached a certain point. Since there is usually
  22177. some output bit somewhere that is not used or does not cause any problems if
  22178. set (like a Light-Emitting Diode), the code can include "milestones", where
  22179. these outputs are set to indicate that the code got there.
  22180. for (sum = 0, i=0; i < 2048; i++)
  22181. sum += *(PROM + i)
  22182. if (sum != 0)
  22183. while (1)
  22184. ; /* hang forever */
  22185. outbyte( LED_PORT, 0x01);
  22186. /* turn on the OK light */
  22187. Now, arguably, the 'scope isn't needed here since the light will either go on
  22188. or not. But what if the light goes on, but gets reset so rapidly that it never
  22189. appears to light? What if the LED is inserted backwards, so it doesn't light?
  22190. Just putting the 'scope probe on the output pin and looking for a change will
  22191. begin to diagnose the problem.
  22192. In addition to simple "does it get there" debugging, the 'scope is a great way
  22193. to perform timing measurements. For example, a task that should complete
  22194. within 30 ms could set an I/O port at its beginning and clear the I/O bit at
  22195. completion. This will generate a pulse that can be traced on the 'scope. The
  22196. time to execute the task and the time between executions, can then be easily
  22197. read off the 'scope, based on the graduations on the CRT face.
  22198. Using two channels, the turn-around time for communications message processing
  22199. can be easily measured by attaching the transmit line to one channel and the
  22200. receive line to the other channel. You can even decode the message from the
  22201. 'scope trace if you know the communications protocol well enough. (This is
  22202. lots of fun with NRZI standards like HDLC.)
  22203. A good 'scope is considered a minimum requirement in most shops where hardware
  22204. is being designed, but a 'scope can be overkill for other tasks. If you just
  22205. need to do some "did the signal go high" testing, a logic probe might be
  22206. adequate. The logic probe senses digital logic levels and has an LED for a
  22207. signal high, another for signal low, another for a "pulsing" signal (slowed to
  22208. human speeds), and yet another, labeled "memory", to show that a signal went
  22209. high and then low (a single pulse). These are cheap (less than $50), simple,
  22210. small, and don't have a lot of knobs.
  22211.  
  22212.  
  22213. Using A Logic Analyzer
  22214.  
  22215.  
  22216. A logic analyzer (LA) is like a collection of logic probes, in that it looks
  22217. at logic levels at many locations, either high or low, but unlike a logic
  22218. probe, an LA also allows those levels to be "clocked" into memory, usually
  22219. based on the microcontroller clock signal. In most applications the LA must
  22220. have at least as many input lines as there are address lines on the processor
  22221. -- more is better. The state of the lines is remembered on the basis of the
  22222. clock input, which can be set to clock on either the high-going or the
  22223. low-going edge. A careful study of the handbook for the particular processor
  22224. is often needed to set clocks and clock edges correctly, and sometimes just
  22225. experimenting till it "works" is the only way.
  22226. Since microcontrollers may go through a million or so instructions in a
  22227. second, just saving every instruction in the LA's limited memory is not
  22228. feasible. To focus on an area of interest, the LA has its own kind of trigger
  22229. mechanism, which can be as simple as waiting for some or all of the input
  22230. lines to match user-set values, e.g., a given address. The analyzer may be set
  22231. to start collecting frames into memory after the trigger is hit, or it may
  22232. collect frames until the trigger, known as a "pre-trigger". In pre-trigger
  22233. mode, if you set the "trigger" to the PANIC code, the LA will capture the
  22234. addresses of the instructions executed immediately before the PANIC. Most LAs
  22235. provide additional, very complex trigger schemes, to allow the user to catch a
  22236. bug that occurs only in unusual circumstances.
  22237. While using an LA is a definite improvement over a 'scope, it has its own
  22238. challenges. For starters, the LA doesn't understand C. It reports what it sees
  22239. in machine language (i.e., ones and zeros, converted to hexadecimal), unless
  22240. you've paid extra for a "personality module" that can display these codes in
  22241. assembly language. Thus, unless you were born with sixteen fingers, you'd
  22242. better have a hex calculator close at hand.
  22243. To use an LA to debug your C code, make your compiler produce listings with
  22244. intermixed assembly language, and learn enough assembly language to understand
  22245. what the compiler produced. Be sure you've turned off all optimizations --
  22246. otherwise you'll find your lines of code moved around or folded together. If
  22247. you use a linker, as you usually must, you will have to add the link map
  22248. offsets to the addresses in each module's listing to produce the addresses
  22249. seen by the LA. Sometimes, you can force the linker to align modules on 256
  22250. (100 hex) byte boundaries, making the hex arithmetic easier to figure in your
  22251. head.
  22252. Logic analyzers are not ICEs (in-circuit emulators); an LA can only "see" the
  22253. electrical signals on the microcontroller's bus. The LA can't "see" the
  22254. activity of important circuits (communications, Analog-Digital conversion,
  22255. timers, DMA), located within the microcontroller. Also, the LA doesn't allow
  22256. you to stop and examine things and then continue. You can mitigate this
  22257. limitation somewhat, at least during debug, by having your program copy
  22258. important internal state information to an external memory location (causing
  22259. the internal register data to appear on the external data bus).
  22260.  
  22261.  
  22262. The Art Of Debugging With A Logic Analyzer
  22263.  
  22264.  
  22265. As I remarked earlier, LAs typically have complex triggering mechanisms.
  22266. Usually, simply triggering on a given address is sufficient, but when the
  22267. really tough, once-every-hour bug comes along, the fancy triggering capability
  22268. is invaluable. This is because the bug happens long before it is detected. If
  22269. the trigger can be set to the place where the error is detected (for example,
  22270. the hardware is set to a "fail-safe" state), sometimes there is enough
  22271. "pre-trigger" memory to find out why the code got to this place.
  22272. When that is not enough, the trickery has to start. Some analyzers will allow
  22273. selective collection into memory, effectively expanding the memory by
  22274. excluding un-interesting sections of code. Or, if there are only two paths
  22275. that can bring the code to this one point, you can configure the trigger on an
  22276. "OR" case: "trigger if either of these addresses is seen." Sometimes a certain
  22277. section of code will execute correctly three times and fail consistently the
  22278. fourth time. The trigger can sometimes be set to trigger on the "Nth"
  22279. occurrence of an address.
  22280. As an aside, the LA can help find bugs that a traditional debugger like Turbo
  22281. Debug cannot, because the LA is non-invasive. The timing of the code and the
  22282. contents of memory are not affected by the logic analyzer -- both are changed
  22283. when a debugging program is loaded. Though it's usually easier to follow
  22284. unoptimized code, in some cases you may be forced to debug the optimized
  22285. version. When a bug is reported from the field, it may not manifest itself the
  22286. same way unless the exact code from the field is used, loaded at exactly the
  22287. same address.
  22288. Multi-level triggering is required when the suspicious code works most of the
  22289. time. For example, a trigger might be set to trigger if the following sequence
  22290. occurs: State 1 is reached, then State 2 is reached; if State 3 occurs before
  22291. State 4, then trigger, otherwise, start over looking for State 1. This
  22292. retiggering feature finds bugs of the type where the execution thread wanders
  22293. off into code that is run commonly, but is not correct in a certain context.
  22294. An LA can also trigger on data accesses. It can be triggered on either a read
  22295. or write, and even on the data at a certain address being accessed. This can
  22296. help in those maddening situations where a data structure is getting "bashed"
  22297. somewhere, but you haven't the foggiest where in the inch-thick listing that
  22298. might be. The fancy triggering can come in quite handy in these cases. Let's
  22299. say that the structure is legitimately changed in only one piece of code. The
  22300. retriggering mechanism works well here. The Set-up would be something like
  22301. this (this set-up is based on the Nicolet analyzer that I'm most familiar
  22302. with):
  22303. S1 -- a write request to the given data address
  22304. S2 -- the start of the code that is allowed to change this address
  22305. S3 -- the end of the code allowed to change this.
  22306. 1. Collect frames until S1 occurs, then done. If S2 occurs first, go to step
  22307. 2.
  22308. 2. Wait for an S3 to occur, then go to step 1.
  22309.  
  22310. The analyzer can be set to trigger on "sequence done" or "memory full".
  22311. "Sequence done" would be a good choice here (the pre-trigger memory will have
  22312. the addresses of code leading up to the fault). This sequence should only be
  22313. "done" if some other code writes to address S1. If S1 is written to during
  22314. initialization, a step before these may be in order:
  22315. 0. Wait for the address of the end of the initialization code, S4.
  22316. When a problem seems impossible to trigger on, I always get out the
  22317. instruction book for the analyzer again, and hope to find something I missed
  22318. the first dozen times through. I also do a "reality-check" if I've hooked the
  22319. analyzer up and strange results appear. A reality-check is just a trace that
  22320. triggers on the "trace memory full" condition after a restart. This trace
  22321. should show the addresses and data from the first few instructions, and gives
  22322. confidence that the analyzer is clocking correctly. It may also show how much
  22323. switch bounce is in the reset button, by starting over and over again several
  22324. times.
  22325. The most important part about using an analyzer is that setting "good" trigger
  22326. sequences is an art that will be acquired over time.
  22327.  
  22328.  
  22329. Using An ICE
  22330.  
  22331.  
  22332. An In-Circuit Emulator (ICE) is different from a logic analyzer in that it
  22333. replaces the microcontroller and allows the user a high degree of control over
  22334. the execution of the processor. Because of this control, it is much more like
  22335. the "Turbo Debug" environment. The user can single-step through the program,
  22336. set breakpoints (much like in a regular debug program), and set watchpoints
  22337. (executing until a variable is changed) that are checked in real time (not in
  22338. slow-motion like the debug programs). At a break, you can examine the internal
  22339. registers and the memory locations and the I/O ports.
  22340. Many ICEs also include a trace option that allows the emulator to do the
  22341. functions of an LA. This includes collecting a trace of where the execution
  22342. has been, and fancy multi-level triggering. The ICE also supplies a few
  22343. digital input lines for the user to connect as he pleases, to monitor the
  22344. prototype hardware.
  22345. The ICE manufacturer also may have made a deal with the C compiler companies
  22346. to allow source level debugging of the code, including single-line stepping,
  22347. and setting a break based on a line number, and examination of variables by
  22348. name. This makes the C programmer even more at home, and reduces the learning
  22349. time significantly.
  22350. Since the ICE allows the execution to be stopped, the hardware's "watch-dog"
  22351. timer must be disabled, if one exists. Also, disable code checksum tests
  22352. during testing, since the ICE can change the contents of the program. Also,
  22353. bear in mind that the C source single-step mode is line-oriented, so keep each
  22354. line simple.
  22355. ICE is also good for patching "dumb mistakes" on the fly. For example, what if
  22356. you wrote: "if ( a = = b )" but you meant "if (a > b)"? Making that one small
  22357. change could mean 20 minutes work if you have to go back to your desk, edit,
  22358. recompile, relink, reload, etc. With the ICE, you can "patch" the code and
  22359. continue.
  22360.  
  22361.  
  22362. Conclusion
  22363.  
  22364.  
  22365. The tools described in this article can be useful in various circumstances. A
  22366. 'scope is sometimes my first line of attack because it does certain tasks
  22367. better than the others (like measuring timing). An emulator with integrated
  22368. logic analyzer seems like the most powerful tool, but sometimes a logic
  22369. analyzer has more triggering levels, or more input lines, or something that is
  22370. required for a particular problem. Also, while a logic analyzer isn't tied to
  22371. any one microcontroller, an emulator generally is (though you can purchase
  22372. "personality modules" for other processors).
  22373. Be flexible. Yet don't tell your boss that you can do it all with just a
  22374. 'scope, either. I believe that software schedules get off track worst during
  22375. the debugging phase. Nobody wants to plan to make mistakes. Don't forget to
  22376. allow time to learn any new tools or methods that you'll have to learn.
  22377. In all of debugging, try to become "wholistic". Accept information through
  22378. whatever means it comes, not just by staring blankly into the screen on the
  22379. 'scope, logic analyzer, or emulator. If your product has LEDs, make sure they
  22380. blink in the ways you expect them to. Listen to the clicks and clacks of
  22381. external hardware, or to the change in tone of the power supply when the load
  22382. changes. If you feel heat radiating from the hardware and you don't think it
  22383. should, check it out -- just do so carefully; I've burned my fingers more than
  22384. once removing PROMs that I installed backwards. If you smell smoke, make sure
  22385. it's not your hardware.
  22386. The choice of what tools to use can be very difficult. While the ICE seems the
  22387. best choice, it can also be the most expensive, since you may need to buy a
  22388. different one for the next project. Maybe the project is so simple that the
  22389. code can be checked out on a PC, with minimal testing on actual hardware. Good
  22390. debugging tools will never make up for bad programming, and many projects were
  22391. completed without any fancy tools. The best tool to use is the best tool
  22392. available for the task at hand. But who hasn't used a screwdriver handle to
  22393. tap something into place, or a table knife to remove a screw? The craftsman
  22394. can take what tools he has, and make them do his bidding.
  22395.  
  22396.  
  22397.  
  22398.  
  22399.  
  22400.  
  22401.  
  22402.  
  22403.  
  22404.  
  22405.  
  22406.  
  22407.  
  22408.  
  22409.  
  22410.  
  22411.  
  22412.  
  22413.  
  22414.  
  22415.  
  22416.  
  22417.  
  22418.  
  22419.  
  22420.  
  22421.  
  22422.  
  22423.  
  22424.  
  22425.  
  22426.  
  22427.  
  22428.  
  22429.  
  22430.  
  22431.  
  22432.  
  22433.  
  22434.  
  22435. Forked Interrupt Systems
  22436.  
  22437.  
  22438. Marc L. Allen
  22439.  
  22440.  
  22441. Marc L. Allen is a senior design engineer with Hamilton Test Systems, Inc., a
  22442. subsidiary of United Technologies Corporation, where he designs point-of-sale
  22443. systems and equipment. He has a B.S. in computer engineering from the
  22444. University of Arizona. He may be contacted at Hamilton Test Systems, Inc.,
  22445. 2202 N. Forbes Blvd., Tucson, AZ 85745.
  22446.  
  22447.  
  22448. I recently designed a system controller for a PC-based point of sale credit
  22449. card authorization system. This controller is capable of handling up to four
  22450. subordinate terminals and several miscellaneous communications and storage
  22451. devices
  22452. This application handles interrupts generated by keystrokes from subordinate
  22453. terminals, communication activity, disk I/O, and an internal timer. These
  22454. interrupts must be processed as quickly as possible while guaranteeing that
  22455. every interrupt is processed.
  22456. This system generates enough interrupt activity that I couldn't run with
  22457. interrupts disabled for fear of missing one, and in certain cases would need
  22458. to service an incoming interrupt before I had finished dealing with a previous
  22459. interrupt from the same device. To address these needs I settled on a forked
  22460. interrupt system running in protected mode and developed using Intel's IC-286
  22461. compiler under MS-DOS.
  22462. A forked interrupt system utilizes a fork queue to serialize interrupts while
  22463. minimizing the amount of time they are disabled. To do this, device drivers
  22464. are broken up into two parts. The first handles the immediacy of the
  22465. interrupt. Since interrupts are disabled during this portion of the driver, it
  22466. should perform only the minimum work required. This normally includes
  22467. acknowledging the device, clearing the interrupting condition, and (for input
  22468. interrupts) reading the input data. Finally, this interrupt-disabled portion
  22469. of the driver places the interrupt in the fork queue to be completed by the
  22470. second, interrupt-enabled, portion.
  22471. The second part of the driver is activated by the fork queue task and performs
  22472. the remaining interrupt processing. For communication devices, this portion
  22473. might store incoming data or extract and send outgoing data, perform checksum
  22474. or CRC calculations, and handle hardware handshaking details. For a timer
  22475. interrupt, the interrupt-enabled portion would handle the effect of the timer
  22476. event on the system. Listing 1 contains the two portions of a clock driver
  22477. which uses this technique. The clock interrupts occur at some
  22478. system-configurable interval and are used for task time-slicing and the
  22479. handling of timer events on the tasks waiting for them.
  22480. The interrupt-disabled portion of the clock driver, timer_int() (Listing 1),
  22481. is one of the simplest interrupt-disabled portions in the system. The timer
  22482. interrupt is cleared and then a utility routine is called to place the second
  22483. half of the driver (the interrupt-enabled portion, alarm()) in the fork queue.
  22484. fork_driver() effectively ends the interrupt-disabled portion of the clock
  22485. driver by transferring control to the fork queue task. When this transfer
  22486. occurs, the driver is suspended and is not resumed until another clock
  22487. interrupt occurs. At this point, the driver completes the call to
  22488. fork_driver() and continues to the top of the external while loop to handle
  22489. the current interrupt.
  22490. The meat of the driver is contained in alarm() (Listing 1). This
  22491. interrupt-enabled code first informs the system that a significant event (a
  22492. timeslice event) has occured, increments a system tick counter, and processes
  22493. any expired timers on the timer tick list. With interrupts enabled, other
  22494. interrupts may occur and be placed in the fork queue while this driver is in
  22495. operation. In fact, since the clock driver by design has no commonality
  22496. between its two portion, a second clock interrupt can be placed on the fork
  22497. queue while the present one is being handled. Naturally, if a driver can't
  22498. keep up with its own device, it's eventually going to have some serious
  22499. problems. But with a conservative queue size, the driver could get behind its
  22500. device during a sudden burst of activity and still catch up during the
  22501. following idle period. This can easily happen if many devices interrupt at the
  22502. same time. Remember that each interrupt will suspend the current driver until
  22503. the new interrupt can be placed into the fork queue.
  22504. The call to fork_driver() in Listing 1 is not strictly correct. fork_driver()
  22505. actually takes an additional long (four-byte) argument, allowing the
  22506. interrupt-disabled portion to pass any necessary data to the interrupt-enabled
  22507. portion. Although the choice of a long argument was appropriate for my system,
  22508. any size is acceptable. This argument is passed to the interrupt-enabled
  22509. portion as its first parameter. In practice, this parameter may be a character
  22510. received over a communications line, some kind of device identifier, or a
  22511. device status. Those who like to play games with parameters can use the long
  22512. argument to pass two integer or character values or even a structure
  22513. containing four characters. This is not ANSI standard and certainly is not
  22514. portable C; however, it does make certain operations much simpler. As the
  22515. clock driver has no need for any data, dummy is used as a place holder.
  22516. The final parameter passed to the routine is a pointer to the driver's acting
  22517. 80286 Task State Segment (TSS), a structure which contains all the driver
  22518. specific information required by the system. I use the term "acting" because
  22519. this TSS is not the original TSS for that driver. The original is reserved for
  22520. the driver's interrupt-disabled portion. Otherwise, the orignal TSS might be
  22521. active when the next device interrupt occurs forcing a general protection
  22522. fault while trying to activate a busy task.
  22523. Listing 2 shows how fork_driver() operates. Notice that if the system was
  22524. running a normal task, the fork queue task would startup to handle the lastest
  22525. fork entry. If the fork queue task was already running, this entry will be
  22526. taken care of in due course, and if the system was executing a system service
  22527. call, that call would be allowed to finish. The system scheduler will start
  22528. the fork queue task at the completion of the system service. My system also
  22529. contains a fork_continue() routine. It allows a driver to place an entry in
  22530. the fork queue but returns control to the driver. fork_continue() is only used
  22531. if the driver has more than one routine to fork. The last fork operation a
  22532. driver performs should be through fork_driver().
  22533. The physical queue entry contains elements to store the address of the
  22534. driver's original TSS, the address of the interrupt-enabled routine, the long
  22535. parameter, and a link to the next element in the queue. I store the address of
  22536. the original TSS so that the fork queue task can set up an environment
  22537. identical to that of the driver before activating its interrupt-enabled
  22538. portion. This allows a driver to switch to the activating task's Local
  22539. Descriptor Table (LDT) and maintain the LDT association through the interrupt.
  22540. The initial LDT switch would be performed when a system task initiates an I/O
  22541. to the driver. Note that while the clock driver does not support direct I/O
  22542. from a system task, it is activated by a number of system service calls
  22543. regarding timeslicing and system timers. The interrupt-enabled portion does
  22544. change LDTs to gain access to different tasks' parameter blocks which may be
  22545. in local data areas.
  22546. Once started, the fork queue task (fork_execute(), Listing 3) will execute all
  22547. queue entries, including those added during queue execution.
  22548. For each entry in the queue, the fork queue task creates an exact duplicate of
  22549. the entry's original TSS with the following exceptions:
  22550. The entry is set to run with interrupts enabled.
  22551. The stack is switched to a special fork queue stack to avoid any interactions
  22552. between the two portions of the driver.
  22553. The current execution address is set to point to the fork_start() routine.
  22554. fork_start() (Listing 4) is used to front-end the entry's execution. It
  22555. provides a stack environment for the entry to return. No special routines need
  22556. to be called by the entry routine to exit the queue.
  22557. After building a copy of the TSS, the fork queue task performs a task switch
  22558. to the new copy. The new task starts running at fork_start() and calls the
  22559. entry routine, passing the four-byte parameter and the address of the TSS
  22560. copy. When the entry routine returns, fork_start() task switches back to the
  22561. fork queue task, which continues with the next queue entry.
  22562. Although this implementation of a fork queue works well for my application, it
  22563. has some limitations. While the fork queue increases the number of interrupts
  22564. that can be handled during a burst of acctivity, the extra overhead also
  22565. increases the interrupt latency (the time from when an interrupt occurs until
  22566. its processing is completed). Additionally, entry routines are not allowed to
  22567. utilize system services in the normal fashion. To perform system services, I
  22568. needed to place hooks allowing the entry routines to directly call internal
  22569. functions that normal tasks can access only through the system service calls.
  22570.  
  22571.  
  22572. Future Directions
  22573.  
  22574.  
  22575. Presently, to run a routine at a very high priority I must have the calling
  22576. task raise its priority, call the routine, and then lower its priority on
  22577. return. Placing such routines on the fork queue would be much simpler. Because
  22578. such a task would be a normal task routine, as opposed to a driver routine, it
  22579. should have access to system services.
  22580. You could add system services capability to the fork queue by creating a real
  22581. task for the fork queue. Presently, the fork queue task is an internal system
  22582. task without all the information needed to handle system services. It isn't
  22583. included in the system task table and isn't scheduled in the normal manner.
  22584. Even if a fork queue entry could use system services, certain ones should be
  22585. avoided or even ignored. Any service that requires the queue to block or wait
  22586. would defeat the purpose of the fork queue.
  22587. Another possible extension to the forked interrupt system is a prioritized
  22588. fork queue. Some devices may be considered more important than others. For
  22589. instance, an imminent power failure interrupt should take higher precedence
  22590. than a clock interrupt.
  22591.  
  22592.  
  22593. Conclusion
  22594.  
  22595.  
  22596. The forked interrupt system has shown itself to be a good way to serialize
  22597. interrupts. Drivers are easier to write since reentrancy is not required. The
  22598. fork queue allows these non-reentrant drivers to operate in an environment
  22599. where interrupts are mostly enabled, allowing a faster burst rate of
  22600. interrupts to be handled in a timely fashion.
  22601.  
  22602. Listing 1
  22603. /*
  22604.  
  22605. timer_int() -- Timer interrupt routine
  22606.  
  22607. This routine handle the incoming timer interrupt. The
  22608. interrupt is acknowledged and cleared. Then the alarm()
  22609. routine is forked to handle the rest of the timer stuff.
  22610.  
  22611. */
  22612.  
  22613. void timer_int()
  22614. {
  22615. while (1)
  22616.  
  22617. {
  22618. /* Clear timer interrupt here. */
  22619. outp(0x20, 0x20);
  22620.  
  22621. /* Fork the processing routine. */
  22622. fork_driver(alarm);
  22623. }
  22624. }
  22625.  
  22626. /*
  22627.  
  22628. alarm() -- Timer Alarm routine
  22629.  
  22630. This routine is the fork routine of the system timer
  22631. device.
  22632.  
  22633. It will alert any tasks that have expiring system
  22634. timers this tick.
  22635.  
  22636. */
  22637.  
  22638. void alarm(dummy, tcb)
  22639. unsigned long dummy;
  22640. TSS *tcb;
  22641. {
  22642.  
  22643. /* Time slice -- Significant event */
  22644.  
  22645. significant_event = 1;
  22646.  
  22647. /* One more tick... */
  22648.  
  22649. ++timer_interrupts;
  22650.  
  22651. /* For each item on the queue (which is in least to
  22652. most time to wait order), see if the top of the
  22653. queue is ready to alert.
  22654.  
  22655. Alerting consists of setting the target task's event
  22656. flags and delivering any required Asynchronous
  22657. Traps (ASTs). */
  22658.  
  22659. while (timer_waiting)
  22660. {
  22661. /* Change to proper LDT of next task. */
  22662. set_ldt(timer_waiting_ldt, tcb);
  22663.  
  22664. /* Check to see if timer has expired for this task. */
  22665. if ((long) (timer_interrupts -
  22666. ((P_TIMER *) timer_waiting -> pblock) ->
  22667. wakeup) >= 0)
  22668. {
  22669. * Timer has expired. Set appropriate event. */
  22670. set_event(timer_waiting -> my_handle.t,
  22671. timer_waiting -> ef_cluster,
  22672. timer_waiting -> ef_mask);
  22673.  
  22674. /* Deliver AST as required. */
  22675. deliver_ast(timer_waiting -> my_handle.t,
  22676.  
  22677. timer_waiting -> ast_addr);
  22678.  
  22679. /* Remove from timer list and continue. */
  22680. timer_waiting_ldt = timer_waiting -> link_ldt;
  22681. timer_waiting = timer_waiting -> link;
  22682. }
  22683. else
  22684. /* Timer hasn't expired. Don't check any more
  22685. since the tasks are sorted in ascending order. */
  22686. break;
  22687. }
  22688. }
  22689.  
  22690.  
  22691. Listing 2
  22692. /*
  22693.  
  22694. fork_driver(routine, param)
  22695. void (*routine)();
  22696. unsigned long param;
  22697.  
  22698. This routine places the passed routine onto the fork
  22699. queue. The driver is allowed to pass up to four bytes
  22700. to the target routine.
  22701.  
  22702. The driver's action is considered complete, and it will
  22703. not be reentered until another interrupt occurs.
  22704.  
  22705. */
  22706.  
  22707. void fork_driver(routine, param)
  22708. void (*routine)();
  22709. unsigned long param;
  22710. {
  22711. FORK_PARAM *current_fork;
  22712. DSS *current_dcb;
  22713. unsigned chat current_name[NAME_SIZE + 1];
  22714.  
  22715. /* Get address of driver's TSS. */
  22716. current_dcb = get_tss(NULL);
  22717.  
  22718. /* Make sure we are allowed to fork this device again. */
  22719. if (current_dcb -> current_fork_count >= current_dcb ->
  22720. max_fork_count && current_dcb -> max_fork_count)
  22721. {
  22722. /* Can't fork another entry on this device. Throw
  22723. away interrupt. (Serious Problem Here!) */
  22724. if (in_executive fork_in_process)
  22725. resume_last();
  22726. else
  22727. resume(scheduler_task);
  22728. return;
  22729. }
  22730.  
  22731. /* Get next entry. */
  22732. if (!(current_fork = fork_free))
  22733. {
  22734. /* Out of fork space. Throw away interrupt.
  22735. (Serious Problem Here!) */
  22736.  
  22737. if (in_executive fork_in_process)
  22738. resume_last();
  22739. else
  22740. resume(scheduler_task);
  22741. return;
  22742. }
  22743.  
  22744. /* Get the next free fork queue entry link */
  22745. fork_free = fork_free -> link;
  22746.  
  22747. /* Fill the entry with the address of the driver's TSS,
  22748. the interrupt-enabled routine address, the passed
  22749. parameter, and clear the link. */
  22750.  
  22751. current_fork -> tcb = current_dcb;
  22752. current_fork -> routine = routine;
  22753. current_fork -> param1 = param;
  22754. current_fork -> link = NULL;
  22755.  
  22756. /* Add one to the count of fork entries for this
  22757. device. */
  22758.  
  22759. ++current_dcb -> current_fork_count;
  22760.  
  22761. /* Link it onto the end of the fork queue */
  22762. if (fork_queue)
  22763. fork_queue_tail = fork_queue_tail -> link = current_fork;
  22764.  
  22765. else
  22766. fork_queue = fork_queue_tail = current_fork;
  22767.  
  22768. /* If in a system service call, or currently executing
  22769. the fork, resume what we were last doing. Otherwise
  22770. else start up the fork queue. */
  22771.  
  22772. if (in_executive fork_in_process)
  22773. resume_last();
  22774. else
  22775. resume(fork_queue_task);
  22776. }
  22777.  
  22778.  
  22779. Listing 3
  22780. /*
  22781.  
  22782. fork_execute() -- Execute the fork queue
  22783.  
  22784. This task executes all elements on the fork queue. While
  22785. the queue is executing, other elements can be place onto
  22786. the queue.
  22787.  
  22788. The queue operates by setting up a special TSS to be a
  22789. duplicate of the queued driver's TSS, except for the current
  22790. CS:IP and the stack. This allows all forked routines to
  22791. execute using the exact same environment it would have if
  22792. the task (or driver) had called the routine directly.
  22793.  
  22794. Actually, one enviromental difference may occur. All
  22795. routines executing on the fork will be run with
  22796.  
  22797. interrupts enabled.
  22798.  
  22799. In addition, the forked routine will be called in such a
  22800. manner that all it needs to do is issue a return to exit
  22801. the routine. The queue will handle the rest. Any return
  22802. value issued by the forked routine will be ignored. */
  22803.  
  22804. void fork_execute()
  22805. {
  22806. DWORD fork_addr;
  22807. OFFSET fork_sp;
  22808. SELECTOR fork_ss;
  22809. FORK_PARAM *owner;
  22810. unsigned char current_name[NAME_SIZE + 1];
  22811.  
  22812. current_name[NAME_SIZE] = '\0';
  22813.  
  22814. /* Initialize some constants, such as the base stack, and
  22815. the queue startup routine to use. */
  22816.  
  22817. fork_ss = fdummy_tcb.ss;
  22818. fork_sp = fdummy_tcb.sp;
  22819. fork_addr.whole = (unsigned long) fork_start;
  22820.  
  22821. /* Loop to continue task at each invokation */
  22822. while (1)
  22823. {
  22824.  
  22825. /* Tell the world that we are running the queue */
  22826. fork_in_process = 1;
  22827.  
  22828. /* As long as we have something to do.... */
  22829. while (fork_queue)
  22830. {
  22831. /* Get the next element */
  22832. owner = fork_queue;
  22833. fork_queue = fork_queue -> link;
  22834.  
  22835. /* Set up the information for fork_start() */
  22836. current_routine = owner -> routine;
  22837. current_param = owner -> param1;
  22838.  
  22839. /* Set up the TSS to give the target routine access
  22840. to the owning task's LDT. Also, set up for
  22841. interrupts enabled. */
  22842.  
  22843. movemem(owner-> tcb, &fdummy_tcb, 44);
  22844.  
  22845. fdummy_tcb.cs = fork_addr.high;
  22846. fdummy_tcb.ip = fork_addr.low;
  22847. fdummy_tcb.ss = fork_ss;
  22848. fdummy_tcb.sp = fork_sp;
  22849. fdummy_tcb.flag_word = F_IE;
  22850.  
  22851. /* Execute task (Task switch) */
  22852. fdummy_task();
  22853.  
  22854. /* One less fork fork entry for this driver. */
  22855. ((DSS *) owner -> tcb) -> current fork count--;
  22856.  
  22857.  
  22858. /* Place used entry back on free list */
  22859. owner -> link = fork_free;
  22860. fork_free = owner;
  22861. fork_count- -;
  22862. }
  22863.  
  22864. /* Clear fork flag and reschedule */
  22865. fork_in_process = 0;
  22866. resume_cl (scheduler_task);
  22867. }
  22868. }
  22869.  
  22870.  
  22871. Listing 4
  22872. /*
  22873.  
  22874. fork_start() -- Start the forked routine
  22875.  
  22876. This routine is used to call the forked routine. It
  22877. passes the four bytes to the target routine and then
  22878. resumes the previous task, which should always be the
  22879. fork queue. */
  22880.  
  22881. void fork_start()
  22882. {
  22883. /* Call the routine, passing the long parameter and the
  22884. address of the working TSS. */
  22885.  
  22886. (*current_routine)(current_param, &fdummy_tcb);
  22887.  
  22888. /* Task switch back to the fork queue task. */
  22889. resume_last();
  22890. }
  22891.  
  22892.  
  22893.  
  22894.  
  22895.  
  22896.  
  22897.  
  22898.  
  22899.  
  22900.  
  22901.  
  22902.  
  22903.  
  22904.  
  22905.  
  22906.  
  22907.  
  22908.  
  22909.  
  22910.  
  22911.  
  22912.  
  22913.  
  22914.  
  22915.  
  22916.  
  22917.  
  22918.  
  22919.  
  22920. Building A Better Boolean With C++
  22921.  
  22922.  
  22923. Ron Burk
  22924.  
  22925.  
  22926. Ron Burk has a B.S.E.E. from the University of Kansas and has been a
  22927. programmer for the past 10 years. He is currently president of Burk Labs, a
  22928. small software consulting firm.
  22929.  
  22930.  
  22931. C++ continues to evolve as a set of incremental changes and additions to C. As
  22932. a C programmer, you can begin to learn and use C++ in exactly the same way by
  22933. making incremental changes to your programming style and to the language
  22934. features that you use. This article provides a simplified introduction to C++
  22935. data types by altering a C data type a step at a time to construct a better,
  22936. C++ data type.
  22937. C++ offers many features that can be used when constructing new data types,
  22938. but this article will focus on just two: data hiding and user-defined
  22939. conversions. These two language features give you the ability to define C++
  22940. data types that have many of the same privileges as built-in data types such
  22941. as int or float.
  22942.  
  22943.  
  22944. What Do You Want From A Data Type?
  22945.  
  22946.  
  22947. Different languages take different approaches to data types. On one end of the
  22948. spectrum are typeless languages; for example, a BASIC interpreter may have a
  22949. single string data type and automatically convert between numeric and string
  22950. formats when required. On the other end of the spectrum are languages with
  22951. strict type checking; Pascal, for instance, detects and disallows any attempt
  22952. to use a variable that is not of the correct data type. C falls somewhere
  22953. between these two extremes. It contains multiple data types (and allows the
  22954. user to create more), but it also provides automatic conversions between
  22955. certain data types.
  22956. C++ is stricter about type checking than C. For example, the following code
  22957. fragment is legal C, but not legal C++:
  22958. main() {
  22959. void f();
  22960.  
  22961. f(45);
  22962. }
  22963. In C, you don't have to specify the number and type of arguments that a
  22964. function requires; in C++ you must.
  22965. Even a simple data type such as Boolean can be implemented in a number of
  22966. different ways. The best implementation depends upon your circumstances. For
  22967. example, if you are making a data type for a project that three programmers
  22968. will work on for four months, you will probably favor an implementation that
  22969. is easy to use and simple to construct. On the other hand, if you are making a
  22970. data type for a five-year project that will involve 100 programmers, you may
  22971. be willing to give up some ease of use in exchange for stricter type checking
  22972. so the compiler can catch more programmer mistakes.
  22973. It is in the larger coding projects, involving more than one person, that C++
  22974. holds a clear advantage over C. Why invest time carefully designing a Boolean
  22975. data type if you never write programs more than a few pages long? As the
  22976. type-constructing tools provided by C++ are contrasted with those of C, you
  22977. will see how you can use C++ to tailor a type definition to your own unique
  22978. needs.
  22979.  
  22980.  
  22981. Designing a Boolean Type
  22982.  
  22983.  
  22984. Although C does not contain a built-in Boolean type, the concept arises twice
  22985. in the language. First, the result of a relational expression is defined in C
  22986. to be of type int and equal to either one or zero. Second, C control
  22987. statements consider any non-zero expression to be true. Therefore, it could be
  22988. desirable for a Boolean type to have analagous properties. First, a Boolean
  22989. variable should always be equal to either zero or one. Second, you should be
  22990. able to assign any integer to a Boolean variable and the result should be one
  22991. if the integer was non-zero; the goal is to construct a Boolean type which is
  22992. compatible with the C type int. The following code, for example, should
  22993. perform correctly:
  22994. bool func(){
  22995. bool more;
  22996. while (more=fread (/*args*/)){
  22997. if(/*some condition*/)
  22998. return more;
  22999. }
  23000. }
  23001. The standard library function fread() reads data items from a file and returns
  23002. the number of items successfully read. In this example, the caller is only
  23003. interested in whether the number of items read successfully is zero
  23004. (end-of-file) or non-zero. The code above can only work if the type bool is
  23005. compatible with the normal built-in scalar types. Otherwise, assigning an int
  23006. to a bool would require gyrations like this:
  23007. while(more=(bool)fread(/*args*/)){
  23008. /* or worse: */
  23009. while (more=(fread (/*args*/) !=0)) {
  23010. Of course, if you accidentally assigned an int to a bool in some program, the
  23011. compiler would not complain. Choosing to make a Boolean type that is
  23012. compatible with scalars exchanges some compiler error detection for ease of
  23013. use.
  23014.  
  23015.  
  23016. typedef Does Not Create Types
  23017.  
  23018.  
  23019. Here is a possible implementation of a Boolean type in C:
  23020. /* bool.h */
  23021. typedef int bool;
  23022. One problem with this implementation is that typedef does not create a new,
  23023. distinct type; it only creates a synonym for an existing type. In a big
  23024. project, you might have more than one data type that is an int- compatible
  23025. scalar, but they may have no relationship to the bool data type.
  23026. Unfortunately, if they are created with typedefs as shown above, they will all
  23027. be synonyms for int and the compiler will let you mix and match them without
  23028. complaining.
  23029. Although typedef does not create a new data type, struct does. Here is an
  23030. alternative implementation of bool in C:
  23031. /* bool.h */
  23032. typedef struct{int val;} bool;
  23033. This introduces a new type called bool which is incompatible with other data
  23034. types. The compiler will object if you try to assign a variable of a different
  23035. data type to a variable of type bool. Unfortunately, the compiler will also
  23036. object if you try to assign an integer to your bool variable:
  23037.  
  23038. bool a = 1; /* error */
  23039. bool b.val = 1; /* ok,
  23040. but painful to type */
  23041.  
  23042.  
  23043. User Conversions
  23044.  
  23045.  
  23046. Although a C struct creates a new data type, it does not have any facility for
  23047. telling the compiler what other data types should be compatible with the new
  23048. one. In this case, you want to be able to specify that an int can be converted
  23049. into a bool, and a bool can be converted into an int. You could augment your C
  23050. Boolean type with type conversion functions. The result might look like this:
  23051. /* bool.h */
  23052. typedef struct{int val;} bool;
  23053. bool booli(int i);
  23054. int ibool(bool b);
  23055. The definition of the conversion functions would be placed in another file:
  23056. /* bool.c */
  23057. #include <bool.h>
  23058. bool booli(int i) {
  23059. bool ret;
  23060. ret.val = i;
  23061. return ret;
  23062. }
  23063. int ibool (bool b) {return b.val;}
  23064. Finally, you have a new data type, bool, which can be converted to and from
  23065. integers. This C implementation has several problems, however.
  23066. First, this bool implementation is clumsy to use; the syntax is less than
  23067. elegant. A typical use of this implementation might be:
  23068. bool b;
  23069. b = booli(x > 5);
  23070. printf("b = %d\n", ibool(b));
  23071. Second, there is a problem with the structure member val. Each variable of
  23072. type bool should always be either zero or one. So long as the programmer uses
  23073. the conversion functions, all is well. Unfortunately, there is nothing to
  23074. prevent the data type user from making mistakes such as this one:
  23075. int l = 45;
  23076. bool b;
  23077. /* set b to 1 */
  23078. b.val = l;
  23079. In this case, the Boolean has been set equal to 45 because the letter "l"
  23080. looks like the numeral "1".
  23081. Finally, the conversion functions create extra time and space overhead in the
  23082. generated code. A function call is relatively cheap in C, but it should not be
  23083. necessary for such simple conversions as these.
  23084.  
  23085.  
  23086. Converting To C++
  23087.  
  23088.  
  23089. You can repair these deficiencies by taking advantage of C++ language
  23090. features. The first change is to remove the typedef:
  23091. /* bool.h */
  23092. struct bool {int val;};
  23093. bool booli(int i);
  23094. int ibool (bool b);
  23095. In C++, unlike C, a struct declaration causes the structure tag name to become
  23096. a new data type; in other words, the following code compiles in C++ but it
  23097. doesn't in C:
  23098. #include <bool.h>
  23099. /* legal C & C++ */
  23100. struct bool b;
  23101. /* legal C++, not C */
  23102. bool b;
  23103. C++ also has a keyword, called private, that you can use to prevent the data
  23104. type user from accidentally setting val to something other than zero or one.
  23105. Consider the following header file:
  23106. /* bool.h */
  23107. struct bool {
  23108. private:
  23109. int val;
  23110. };
  23111. bool booli(int i);
  23112. int ibool(bool b);
  23113. The private keyword tells the compiler that the structure members that follow
  23114. it cannot be accessed from outside this data type. Thus, the following code
  23115. becomes illegal:
  23116.  
  23117. include <bool.>h
  23118. ...
  23119. bool a;
  23120. a.val = 5;
  23121. /* error: val is private */
  23122. Of course, we've painted ourselves into a corner now booli() and ibool() won't
  23123. compile because they need to access val. Private structure members aren't much
  23124. use without a syntax for allowing certain functions to access them. The
  23125. easiest way to do that is with the friend keyword, like this:
  23126. /* bool.h */
  23127. struct bool {
  23128. friend bool booli(int i);
  23129. friend int ibool(bool b);
  23130. private:
  23131. int val;
  23132. };
  23133. This makes booli and ibool friend member functions of the structure bool. Now
  23134. the conversion functions will compile, since the compiler knows they are
  23135. allowed to access the private members of bool. Since you control the functions
  23136. that can modify a bool, you can guarantee that it only takes on the values
  23137. zero and one.
  23138. You can make the connection between the conversion functions and the data type
  23139. more explicit by making the conversion functions member functions of the
  23140. structure. A member function of a structure has the same privileges as a
  23141. friend member function, but it has a different invocation syntax and can only
  23142. be used to operate on the data type with which it was declared. Changing the
  23143. two access functions into member functions results in the following header
  23144. file:
  23145. /* bool.h */
  23146. struct bool {
  23147. void booli(int i);
  23148. int ibool ();
  23149. private:
  23150. int val;
  23151. };
  23152. Two things have changed: the friend keyword is removed, and bool is no longer
  23153. passed to, or returned by, the member functions. Here is an example of legal
  23154. invocations of the two functions:
  23155. #include "bool.h"
  23156. bool b;
  23157. b.booli(45);
  23158. int i = b.ibool();
  23159. As you can see, the syntax for calling member functions in a structure is
  23160. analogous to the syntax for selecting data members in a structure. A member
  23161. function is implicitly passed a pointer to the variable it was invoked with;
  23162. that is why ibool() no longer needs an explicit bool argument and booli() no
  23163. longer needs to explicitly return a bool value.
  23164. The definition of the two member functions must also change if they are not
  23165. friend functions. The corresponding changes in bool.c are:
  23166. /* bool.c */
  23167. #include <bool .h>
  23168. void bool::booli(int i) {
  23169. val = i;
  23170. }
  23171. int bool::ibool(){ return val; }
  23172. The first change results from the fact that the complete name of a C++ member
  23173. function is typename::function, which distinguishes it from any non-member
  23174. functions. This syntax allows you to use the same member function name in
  23175. different data types without any conflict. The second change is that the
  23176. functions can refer to the data members of the structure (in this case, val)
  23177. as though they were local variables. This is possible because member functions
  23178. cannot be invoked without some variable of the correct data type.
  23179. Of course, the user of data type bool still has to type things like b.ibool(),
  23180. just to reference a Boolean; however, you now have hidden the implementation
  23181. of the data type. If you ever discover a bool that has been set to something
  23182. other than zero or one, you know the bug must be in the member functions. If
  23183. you want to change val to be a char instead of an int, you can be confident
  23184. that only the member functions will be affected by the change.
  23185.  
  23186.  
  23187. Data Type Initialization
  23188.  
  23189.  
  23190. Actually, there is a problem in the protection for bool variables; if the user
  23191. doesn't initialize a bool variable, then it may contain garbage instead of a
  23192. one or a zero. You can eliminate this loophole by telling the compiler that
  23193. each variable of data type bool must be initialized when it is declared. This
  23194. is done by declaring a special member function called a constructor. A
  23195. constructor looks like an ordinary member function except that it has the same
  23196. name as the data type and it has no return type. In this case, you can simply
  23197. change the name booli() to bool() as follows:
  23198. /* bool.h */
  23199. struct bool {
  23200. bool (int i);
  23201. int ibool ();
  23202. private:
  23203. int val;
  23204. };
  23205. Creating a constructor for a data type has three main implications. First, the
  23206. compiler will no longer allow a variable of that data type to be declared
  23207. without being initialized. Second, if the constructor takes a single argument
  23208. as this one does, the compiler will use the constructor whenever it sees a
  23209. cast from the argument data type into the data type the constructor is a
  23210. member of. Finally, if the constructor takes a single argument, it defines an
  23211. implicit conversion from that argument's data type into the data type the
  23212. constructor is a member of. These implications deserve further explanation.
  23213. Defining a constructor guarantees you won't have any uninitialized variables
  23214. of a particular data type. In other words, with the newest header file, the
  23215. following code won't compile:
  23216. #include "bool.h"
  23217. bool b; /* error */
  23218. The compiler will complain that the variable b must be initialized. The
  23219. constructor also introduces a new syntax for initializing the variables of its
  23220. data type:
  23221. #include "bool.h"
  23222. bool b(9);
  23223. In the statement shown, the function bool::bool() gets called to convert the
  23224. integer 9 into a Boolean 1.
  23225. Defining a constructor with a single argument also enables the cast operator
  23226. for that data type. In this case, the constructor will be called whenever you
  23227. cast an integer into a Boolean as in the following example:
  23228.  
  23229. #include "bool.h"
  23230. bool b(1);
  23231. int i = 38;
  23232. ...
  23233. b = (bool) i;
  23234. In addition to the C-style type cast, C++ allows a function-style type cast.
  23235. For example, in C++ you can say:
  23236. int i;
  23237. long l;
  23238. l = (long)i; /* legal C & C++ */
  23239. l = long(i); /* legal C++ not C */
  23240. The function-style syntax is simply easier to read. This expands the number of
  23241. ways you can initialize a bool to three:
  23242. #include "bool .h"
  23243. bool b(9);
  23244. bool c = (bool)9;
  23245. bool d = bool(9);
  23246. In all three cases, the function bool::bool() gets called to perform the
  23247. conversion.
  23248. Finally, a constructor with one argument defines an implicit conversion. Just
  23249. as you can assign an int to a long because of C's built-in implicit
  23250. conversion, you can now assign an int to a bool, because an implicit
  23251. conversion has been defined for it. In other words, you don't have to use the
  23252. cast operator and the following code fragment is legal C++.
  23253. #include "bool .h"
  23254. bool b(1);
  23255. int i;
  23256. b = i; /* OK */
  23257. Now you have a Boolean that can be assigned an integer value which gets
  23258. converted by the function you've defined.
  23259. There is no way to define a constructor function that takes a single argument
  23260. without getting all three of these effects. This is a fact to ponder when you
  23261. define a constructor for a data type. You can't tell the compiler to require
  23262. the user to use an explicit cast to convert an integer into a Boolean. You
  23263. can't tell the compiler to allow a Boolean to be initialized with an integer,
  23264. but not assigned an integer. Sometimes, this forces you to avoid using a
  23265. constructor and return to the explicit function notation used previously.
  23266. If you want all of your Booleans to be initialized, but don't want to have to
  23267. type all those initializers, you can take advantage of a C++ feature called
  23268. default arguments. Here is how it works:
  23269. /* bool.h */
  23270. stuct bool {
  23271. bool(int i=0);
  23272. int ibool ();
  23273. private:
  23274. int val;
  23275. };
  23276. Now, whenever the compiler would normally call bool::bool() but does not have
  23277. an argument for it, it will use a value of zero. Thus, both of the following
  23278. statements cause bool::bool() to be invoked with an argument of zero:
  23279. #include "bool .h"
  23280. bool b; /* initialize to zero */
  23281. bool b = bool();
  23282.  
  23283.  
  23284. Overloading The Cast Operator
  23285.  
  23286.  
  23287. Telling the compiler how to implicitly convert ints into bools is only half
  23288. the job of making the two data types compatible. The remaining task is to
  23289. replace bool::ibool() with a conversion function that tells the compiler how
  23290. to implicitly convert bools into ints. If int were a user-defined type instead
  23291. of a built-in type, you could define within it a constructor that takes a
  23292. single bool argument and that would do the trick. Since that is not possible,
  23293. C++ gives you another way to define implicit conversions: you can redefine how
  23294. the cast operator operates on your data type. (In fact, you can redefine how
  23295. most any operator operates on your data type).
  23296. Once again, the type conversion requires a member function, but this time it
  23297. is an operator member function. This revised header replaces ibool() with a
  23298. type conversion function:
  23299. /* bool.h */
  23300. struct bool {
  23301. bool (int i);
  23302. operator int();
  23303. private:
  23304. int val;
  23305. };
  23306. The revised specification of bool tells the compiler you have defined a
  23307. function that it can use whenever casting a bool into an int. The revised
  23308. implementation looks like this:
  23309. /* bool.c */
  23310. #include <bool.h>
  23311. bool::booli(int i) {
  23312. val = (i != 0);
  23313. }
  23314. bool::operator int(){ return
  23315. val; }
  23316. Having a function named bool::operator int() is a little disconcerting, but
  23317. the code is otherwise the same.
  23318. Just as with constructor functions, the conversion function can be used either
  23319. implicitly or explicitly. The following code, for example, is legal:
  23320.  
  23321. #include "bool.h"
  23322. bool b=1;
  23323. int i;
  23324. /*...*/
  23325. i = (int)b;
  23326. i = int(b);
  23327. i = b;
  23328. In all three assignments, the function bool::operator int() is called to
  23329. transform the bool into an int. There is no way to define a type conversion
  23330. that must be used explicitly. Just as with constructors, when implicit
  23331. conversions are undesirable, you must resort to something like the explicit
  23332. member function call defined previously using bool::ibool().
  23333. The bool data type is nearly finished now. The original example of the desired
  23334. usage of the data type is completely legal now:
  23335. bool func (){
  23336. bool more;
  23337. while (more=fread (/*args*/)){
  23338. if(/*some condition*/)
  23339. return more;
  23340. }
  23341. }
  23342. You can freely mix bools and ints while still controlling initializations of,
  23343. and assignments to, bools and still hiding the actual representation of a
  23344. Boolean so that you can change it to short or char without impacting the data
  23345. type user's code.
  23346.  
  23347.  
  23348. Efficiency
  23349.  
  23350.  
  23351. The final deficiency to remove from the bool definition is the function call
  23352. overhead. In defining complicated data types in which the conversion functions
  23353. contain a lot of code, the function call overhead would not be significant. In
  23354. this example, however, the function call overhead is probably larger than the
  23355. entire body of the conversion functions. You can remove that overhead quite
  23356. easily by placing the function definition right in the data type
  23357. specification. Doing that and adding some useful constants results in the
  23358. following header file:
  23359. /* bool.h */
  23360. #define FALSE 0
  23361. #define TRUE 1
  23362. struct bool {
  23363. bool(int i){val = (i!=O);}
  23364. operator int(){return val;}
  23365. private:
  23366. int val;
  23367. };
  23368. This style of function definition makes the functions inline, which means the
  23369. compiler will try to generate inline code for them each time they are used,
  23370. rather than call them as functions.
  23371. There is actually an inline keyword in C++ which you can place in front of any
  23372. function you would like to be inline. The simpler the function is, the more
  23373. likely the compiler can generate inline code for it. When the compiler cannot
  23374. inline the code, it simply generates a normal function call. Functions that
  23375. are defined inside data type specifications are automatically inline
  23376. functions, so there was no need for the keyword in the above specification.
  23377. Now the bool data type is as efficient as anything you could implement in C to
  23378. do the same thing. Each variable of type bool is guaranteed to be either zero
  23379. or one at all times and you are free to change how the Boolean is stored and
  23380. how it is converted to and from integers without affecting any other code.
  23381. Unfortunately our current specification still looks a bit like a C programmer
  23382. coded it; here is how a more fluent C++ programmer might write it:
  23383. // bool .h
  23384. class bool {
  23385. int val;
  23386. public:
  23387. bool(int i){val = (i!=0);}
  23388. operator int (){return val ;}
  23389. };
  23390. const bool FALSE=0,TRUE=1
  23391. For single-line comments, it's often easier to use the C++ comment convention
  23392. //. The keyword class is just like the keyword struct except that all of its
  23393. members are private by default, whereas in a struct all the members are public
  23394. by default. C++ has both a private keyword and a public keyword and they
  23395. complement each other. Finally, the #define constants are replaced with
  23396. constants that have the type bool rather than type int. Now the specification
  23397. looks more like "real" C++.
  23398.  
  23399.  
  23400. Making Incompatible Types
  23401.  
  23402.  
  23403. As mentioned, a large project might contain more than one data type that is
  23404. compatible with ints. For example, a game simulation might contain a data type
  23405. called playercount (a non-negative count of the number of players within a
  23406. particular grid). It could be desirable for a data type like this to be
  23407. compatible with ints just as bool is, but would code like this also be legal?
  23408. #include "bool .h"
  23409. #include "player.h"
  23410. playercount p;
  23411. bool b;
  23412. p = b;
  23413. The answer, thankfully, is "no". Even if the compiler has been told how to
  23414. implicitly convert a playercount into an int, and how to implicitly convert an
  23415. int into a bool, the rule is that the compiler never uses more than one
  23416. user-defined implicit conversion on a single value. Without this rule, the
  23417. number of possibly unwanted implicit conversions would mushroom as you added
  23418. each data type with a user-defined conversion.
  23419. If you really want a second user-defined conversion to be applied, you can
  23420. code it explicitly:
  23421. #include "bool .h"
  23422. #include "player.h"
  23423.  
  23424. playercount p;
  23425. bool b;
  23426. p = playercount (b);
  23427.  
  23428.  
  23429. Conclusion
  23430.  
  23431.  
  23432. C++ makes it easy to construct data types that isolate implementation details.
  23433. Additionally, user conversions give you a degree of control over how your data
  23434. types interact with other data types. Like many features of C++, it is
  23435. possible to get into trouble defining new data types. In particular, the
  23436. danger of user-defined conversions is that the compiler will perform silent
  23437. conversions you did not intend. This is a very real problem when you construct
  23438. multiple data types for a large project, each with its own conversion
  23439. functions. However, these potential problems are outweighed by the advantages
  23440. of being able to design a type system that suits the individual needs of your
  23441. project.
  23442.  
  23443.  
  23444.  
  23445.  
  23446.  
  23447.  
  23448.  
  23449.  
  23450.  
  23451.  
  23452.  
  23453.  
  23454.  
  23455.  
  23456.  
  23457.  
  23458.  
  23459.  
  23460.  
  23461.  
  23462.  
  23463.  
  23464.  
  23465.  
  23466.  
  23467.  
  23468.  
  23469.  
  23470.  
  23471.  
  23472.  
  23473.  
  23474.  
  23475.  
  23476.  
  23477.  
  23478.  
  23479.  
  23480.  
  23481.  
  23482.  
  23483.  
  23484.  
  23485.  
  23486.  
  23487.  
  23488.  
  23489.  
  23490.  
  23491.  
  23492.  
  23493.  
  23494.  
  23495.  
  23496. A Practical C File I/O Tutorial: A Mini-Database Program
  23497. Leor Zolman wrote "BDS C", the first C compiler designed exclusively for
  23498. personal computers. Since then he has designed and taught programming
  23499. workshops and has also been involved in personal growth workshops as both
  23500. participant and staff member. He still doesn't hold any degrees. His latest
  23501. incarnation is as a CUJ staff member.
  23502.  
  23503.  
  23504.  
  23505.  
  23506. Series Introduction
  23507.  
  23508.  
  23509. If you're a recent convert to C from any other high-level language and you've
  23510. tried to write programs that do any serious file input/output, then chances
  23511. are you've experienced more than a little bit of frustration. The C standard
  23512. library, in keeping with the general philosophy of the C language, provides
  23513. tools powerful enough for doing anything you want, provided you know how to
  23514. correctly combine those tools. In the case of file I/O, about the only
  23515. operations supported in a "trivial" manner are:
  23516. reading and writing bytes
  23517. reading and writing single lines of text
  23518. For reading and writing any other flavor of data structure to or from the
  23519. disk, a certain level of "C sophistication" is required. Often, the task
  23520. quickly moves beyond "How do I read and write this data?" toward the more
  23521. general problem, "What is the most appropriate way to represent this data in
  23522. order to facilitate efficient means of reading and writing it to disk?"
  23523. In this series of tutorial articles, I will develop from scratch a complete
  23524. special-purpose "mini-database" system in order to illustrate the process of
  23525. designing file based C applications. The resulting system will be functional
  23526. but intentionally inadequte for any particular task.
  23527. This first installment will consist of an operational description of the
  23528. database, broken into the following areas:
  23529. data structures
  23530. functional description
  23531. user interface (the menu system)
  23532. The second installment will present the database record editing and management
  23533. mechanism.
  23534. Later installments will present several different approaches to storing the
  23535. data on disk and will discuss the relative merits of each approach. The first
  23536. version will store all data to disk as user-readable text and will use
  23537. statically-allocated arrays in memory.
  23538. The second version will store all data to disk in binary format for rapid
  23539. transfer. I'll also develop two memory allocation systems for the binary
  23540. version: static array allocation (same as for the textual disk format) and
  23541. dynamic array allocation (to optimize the use of system memory).
  23542.  
  23543.  
  23544. Mini-Database Data Record Structure
  23545.  
  23546.  
  23547. This will not be a "general-purpose" database system, but rather a program
  23548. built to handle only one specific record format: a personnel record as in
  23549. Table 1.
  23550. The definition of the structure tag for this record, named record, is shown in
  23551. the header file (Listing 1, lines 30-37).
  23552. The system will be able to handle only one active database at a time. We'll
  23553. use dynamic memory allocation to obtain storage for the data records, so that
  23554. data memory is allocated only when necessary. For the first version of the
  23555. system, the list of data record pointers will be kept in a
  23556. statically-allocated (i.e., fixed-length) array. The definition of this array
  23557. is shown on line 49 of the header file. The name of the array is recs, and its
  23558. type is
  23559. array (of MAX_RECS elements) of
  23560. pointers to structures of type
  23561. record
  23562. The programmer must explicitly size a fixed-length array. In my code the size
  23563. is MAX_RECS. Thus, the total amount of fixed memory needed for storing the
  23564. records of the database is MAX_RECS times the size of a single record pointer.
  23565. (In later versions, I'll even show how to dynamically allocate the storage for
  23566. the recs array itself. To facilitate this modification the symbol RECS is
  23567. introduced (Listing 1, line 50) as an alias for recs.)
  23568. Lines 42-46 (of Listing 1) illustrate a necessary complication when writing
  23569. multiple-source-file programs: global data must be defined in one module and
  23570. one module only. If the data is to be known in any other modules of the
  23571. program, it must be declared in those other modules. Definitions actually
  23572. reserve storage for the specified data, while declarations only serve to
  23573. inform the compiler about the nature of data defined elsewhere. This
  23574. simplistic rule of thumb will usually differentiate between definitions and
  23575. declarations appearing in header files: 
  23576. If the extern modifier is used, you're probably looking at a declaration;
  23577. otherwise, you're probably looking at a definition.
  23578. To conform with the ANSI Standard, each data item should be defined only once
  23579. among all the source modules of a program. At first it might seem one need
  23580. only insert an extern keyword in front of all but one declaration, making it
  23581. the definition. Unfortunately, this is not easily done. Typical
  23582. multiple-module programs use lots of shared data; do we really want to
  23583. maintain separate lists of declarations in separate modules, some having the
  23584. extern keyword and some not? Of course not; we'd rather have all the data
  23585. included within a single .h file. But if the declarations/definitions must be
  23586. written differently in separate files, can we really use a single header file?
  23587. Yes. Lines 42-46 illustrate a symbolic constant to control whether the extern
  23588. keyword is generated for the critical declarations. If MAIN_MODULE exists,
  23589. then we are compiling the main module of the program and the symbolic constant
  23590. EXTERN is defined to nothing (so the items in lines 49, 52 and 53 are
  23591. defined). Otherwise EXTERN is defined to extern and the lines are treated as
  23592. declarations of external data. To force definitions to be created as the main
  23593. module is compiled, we #define MAIN_MODULE (see Listing 2, line 24) before the
  23594. inclusion of the header file. The other modules of the program do not contain
  23595. such a definition.
  23596. (Note: The difference between definitions and declarations has been rendered
  23597. fuzzy by variations among C compilers over the years. Microsoft, perhaps to
  23598. eliminate the need for exactly the sort of mechanism just presented, decided
  23599. to make its linker allow multiple definitions of the same piece of data among
  23600. source files of a program (although multiple initializations were still
  23601. flagged as errors.) While this does simplify development in some cases, it
  23602. renders C programs relying on this "feature" non-portable. Turbo C 2.0, under
  23603. which this database program was developed, makes you "do it right", even if
  23604. doing so requires a little bit more thought.)
  23605.  
  23606.  
  23607. Other Global Data
  23608.  
  23609.  
  23610. The system maintains a minimal amount of global data to describe the currently
  23611. open database's state. The variable n_recs tells how many records are
  23612. currently held in memory. The variable max_recs contains the maximum number of
  23613. records that can be represented. For the fixed-length array version, max_recs
  23614. is simply assigned the value of the symbolic constant MAX_RECS (Listing 2,
  23615. line 72).
  23616.  
  23617.  
  23618. A Simple Menu System
  23619.  
  23620.  
  23621. A simple line-oriented menu system serves as the user interface. The menu
  23622. function do_menu is shown in MDBUTIL.C (Listing 3, lines 21-39.) The menu
  23623. consists of a list of pointers to structures of type menu_item (Listing 1,
  23624. lines 55-58), where each menu_item consists of an integer action code and a
  23625. string description of the action. do_menu simply numbers and lists out each
  23626. description (up to but not including the first entry with action_code of 0),
  23627. asks the user to pick one of the choices, and returns the action_code value
  23628. associated with the selected item. Note that the action_code values need not
  23629. correspond to the choice numbers displayed by the function.
  23630.  
  23631.  
  23632. The Main Menu Options
  23633.  
  23634.  
  23635. The database operations are divided into two menus. The first menu (Listing 2,
  23636. lines 37-48) contains the options for controlling database selection, disk I/O
  23637. and program termination. The second menu, within the MDBEDIT.C module (shown
  23638. in a future article) controls all the options associated with editing the data
  23639. records of the currently active database.
  23640. The main menu controls the top-level system functions. A variable, db_active,
  23641. tells whether a database is currently open, and thus whether certain
  23642. operations are appropriate. For example, we don't want to allow the user to
  23643. open a new database if another is currently open. The main menu options are as
  23644. follows:
  23645.  
  23646.  
  23647.  
  23648. CREATE:
  23649.  
  23650.  
  23651. Initialize a new database. Ask the user for a name for the database (this will
  23652. also be the name of the file used to store the database on disk) and check to
  23653. make sure another file does not already exist by that name. If the name is OK,
  23654. then initialize max_recs, n_recs and db_active.
  23655.  
  23656.  
  23657. EDIT:
  23658.  
  23659.  
  23660. Call the edit_db() function to edit the records of the database.
  23661.  
  23662.  
  23663. OPEN:
  23664.  
  23665.  
  23666. Load a previously stored database from disk (via the read_db function), then
  23667. go immediately into editing that database by calling edit db. read_db()
  23668. allocates the appropriate amount of memory for the database records, assigns
  23669. the pointers to elements in the RECS array, and returns the number of records
  23670. loaded. We announce the number of records before calling edit_db().
  23671.  
  23672.  
  23673. BAKUP.
  23674.  
  23675.  
  23676. This menu entry is included to encourage backup facilities in your
  23677. applications. The backup function, backup_rib(), is just a dummy.
  23678.  
  23679.  
  23680. CLOSE:
  23681.  
  23682.  
  23683. Terminate operations on the current database, write it to disk and free up all
  23684. associated storage.
  23685.  
  23686.  
  23687. SAVE:
  23688.  
  23689.  
  23690. Write the database to disk, preventing loss of work "so far" in case of a
  23691. system crash. Do not close the database.
  23692.  
  23693.  
  23694. ABANDON:
  23695.  
  23696.  
  23697. Close the database without saving it to disk: free up all storage.
  23698.  
  23699.  
  23700. QUIT:
  23701.  
  23702.  
  23703. Exit the program.
  23704.  
  23705.  
  23706. Utility Functions
  23707.  
  23708.  
  23709. Listing 3 shows the source module MDBUTIL.C, containing utility functions used
  23710. throughout the program. In addition to the do_menu() function (already
  23711. described), this module includes error(), alloc_rec() and free_up().
  23712. The error() function is a general-purpose fatal exit. It prints a message and
  23713. exits.
  23714. The alloc_rec() function is not used by any of the code in this month's
  23715. listing, but is basic to the operation of the program. alloc_rec() is called
  23716. to obtain memory from the system to store a single record of database data.
  23717. The malloc() function is called to actually obtain the block of storage.
  23718. alloc_rec returns either NULL, signaling that the system has no more storage
  23719. to spare, or a valid memory pointer obtained from malloc().
  23720. The free_up() function returns all storage (obtained through calls to
  23721. alloc_rec) back to the system. In this system storage is always freed up for
  23722. the entire database at one time (when the current database file is closed or
  23723. abandoned.) Freeing that storage is simply a matter of walking through all the
  23724. records of the database and calling the free() function for each pointer.
  23725. Next month: Editing the database records.
  23726. Table 1
  23727. Name: Type: Value:
  23728.  
  23729.  
  23730. active char 1 if record is active, 0 if deleted
  23731. last char[25] Last name
  23732. first char[15] First name
  23733. id long ID number
  23734. age int Age
  23735. gender char 'M' or 'F'
  23736. salary float Annual salary
  23737.  
  23738. Listing 1
  23739. 1: /*
  23740. 2: * MDB.H (Static-Array-Only Version)
  23741. 3: *
  23742. 4: * Program: Mini-Database
  23743. 5: * Written by: Leor Zolman
  23744. 6: * Module: Program Header File
  23745. 7: */
  23746. 8:
  23747. 9: #define TRUE 1
  23748. 10: #define FALSE 0
  23749. 11:
  23750. 12: /*
  23751. 13: * Prototypes:
  23752. 14: */
  23753. 15:
  23754. 16: int do_menu(struct menu_item *mnu, char *title);
  23755. 17: void write_db(char *filename);
  23756. 18: int read_db(char *filename);
  23757. 19: void edit_db();
  23758. 20: void fix_db();
  23759. 21: void backup_db();
  23760. 22: void error(char *msg);
  23761. 23: struct record *alloc_rec(void);
  23762. 24: void free_up();
  23763. 25:
  23764. 26: /*
  23765. 27: * Data Definitions:
  23766. 28: */
  23767. 29:
  23768. 30: struct record { /* Database record definition */
  23769. 31: char active; /* TRUE if Active, else FALSE */
  23770. 32: char last[25], first[15]; /* Name */
  23771. 33: long id; /* ID Number */
  23772. 34: int age; /* Age */
  23773. 35: char gender; /* M or F */
  23774. 36: float salary; /* Annual Salary */
  23775. 37: };
  23776. 38:
  23777. 39: #define MAX_RECS 1000 /* Maximum number of records */
  23778. 40:
  23779. 41:
  23780. 42: #ifdef MAIN_MODULE /* Make sure data is only */
  23781. 43: #define EXTERN /* DEFINED in the main module, */
  23782. 44: #else /* and declared as EXTERNAL in */
  23783. 45: #define EXTERN extern /* the other modules. */
  23784. 46: #endif
  23785. 47:
  23786. 48:
  23787. 49: EXTERN struct record *recs[MAX_RECS]; /* Array of ptrs to */
  23788.  
  23789. 50: #define RECS recs /* structs of type record */
  23790. 51:
  23791. 52: EXTERN int n_recs; /* # of records in current db */
  23792. 53: EXTERN int max_recs; /* Max # of recs allowed */
  23793. 54:
  23794. 55: struct menu_item { /* Menu definition record */
  23795. 56: int action_code; /* Menu item code */
  23796. 57: char *descrip; /* Menu item text */
  23797. 58: };
  23798. 59:
  23799.  
  23800.  
  23801. Listing 2
  23802. 1: /*
  23803. 2: * MDBMAIN.C (Static Array Only Version)
  23804. 3: *
  23805. 4: * Program: Mini-Database
  23806. 5: * Written by: Leor Zolman
  23807. 6: * Module: Main Program Module
  23808. 7: *
  23809. 8: * Program Description:
  23810. 9: * This system is an "introductory showcase" of
  23811. 10: * C programming techniques for File I/O-related
  23812. 11: * applications. Areas of focus include:
  23813. 12: * Static and Dynamic Array Allocation
  23814. 13: * Text-based and Binary-based Disk Data Storage
  23815. 14: * Elementary user-interface and error-handling
  23816. 15: *
  23817. 16: * Compile & Link (Turbo C):
  23818. 17: * tcc mdbmain.c mdbedit.c mdbutil.c
  23819. 18: * {mdbftxt.c or mdbfbin.c}
  23820. 19: */
  23821. 20:
  23822. 21: #include <stdio.h>
  23823. 22: #include <stdlib.h>
  23824. 23:
  23825. 24: #define MAIN_MODULE 1 /* force data definitions */
  23826. 25: #include "mdb.h"
  23827. 26:
  23828. 27:
  23829. 28: #define CREATE 1 /* Main menu action codes */
  23830. 29: #define OPEN 2
  23831. 30: #define EDIT 3
  23832. 31: #define SAVE 4
  23833. 32: #define BAKUP 5
  23834. 33: #define CLOSE 6
  23835. 34: #define ABANDON 7
  23836. 35: #define QUIT 8
  23837. 36:
  23838. 37: static struct menu_item main_menu[] =
  23839. 38: {
  23840. 39: {CREATE, "Create New Database"},
  23841. 40: {OPEN, "Select Existing Database to Work With"},
  23842. 41: {EDIT, "Edit Database Records"},
  23843. 42: {SAVE, "Write Database to Disk"},
  23844. 43: {BAKUP, "Backup Database to Floppies"},
  23845. 44: {CLOSE, "Close the Database"},
  23846. 45: {ABANDON, "Abandon Changes to the Current Database"},
  23847. 46: {QUIT, "Quit"},
  23848.  
  23849. 47: {NULL} /* End of list */
  23850. 48: };
  23851. 49:
  23852. 50:
  23853. 51: main(int argc, char **argv)
  23854. 52: {
  23855. 53: char db_name[150];
  23856. 54: int db_active = FALSE; /* No Database open */
  23857. 55: FILE *fp;
  23858. 56:
  23859. 57: while (1)
  23860. 58: {
  23861. 59: switch(do_menu(main_menu, "Main Menu"))
  23862. 60: {
  23863. 61: case CREATE:
  23864. 62: if (db_active)
  23865. 63: goto still_open;
  23866. 64: printf("Name for new Database? ");
  23867. 65: gets(db_name);
  23868. 66: if ((fp = fopen(db_name,"r")) != NULL)
  23869. 67: {
  23870. 68: printf("That filename already exists.\n");
  23871. 69: fclose(fp);
  23872. 70: break;
  23873. 71: }
  23874. 72: max_recs = MAX_RECS;
  23875. 73: db_active = TRUE;
  23876. 74: n_recs = 0;
  23877. 75: printf("Entering EDIT mode:\n");
  23878. 76: /* After creating, fall through to EDIT */
  23879. 77:
  23880. 78: case EDIT:
  23881. 79: if (!db_active)
  23882. 80: goto inactive;
  23883. 81: edit_db(db_name); /* Edit recs in memory */
  23884. 82: break;
  23885. 83:
  23886. 84: case OPEN:
  23887. 85: if (db_active)
  23888. 86: {
  23889. 87: still_open: printf("Current Database still open.\n");
  23890. 88: break;
  23891. 89: }
  23892. 90: printf("Database Name? ");
  23893. 91: gets(db_name);
  23894. 92: if ((n_recs = read_db(db_name)) != NULL)
  23895. 93: {
  23896. 94: printf("\nLoaded %d Record(s).\n",
  23897. 95: n_recs);
  23898. 96: db_active = TRUE;
  23899. 97: }
  23900. 98:
  23901. 99: edit_db(db_name);
  23902. 100: break;
  23903. 101:
  23904. 102: case BAKUP:
  23905. 103: if (!db_active)
  23906. 104: goto inactive;
  23907. 105: backup_db(); /* Perform backup */
  23908.  
  23909. 106: break;
  23910. 107:
  23911. 108: case CLOSE:
  23912. 109: if (!db_active)
  23913. 110: goto inactive;
  23914. 111: write_db(db_name); /* write to disk */
  23915. 112: free_up();
  23916. 113: db_active = FALSE;
  23917. 114: break;
  23918. 115:
  23919. 116: case SAVE:
  23920. 117: if (!db_active)
  23921. 118: goto inactive;
  23922. 119: write_db(db_name); /* write to disk */
  23923. 120: break;
  23924. 121:
  23925. 122: case ABANDON:
  23926. 123: if (!db_active)
  23927. 124: {
  23928. 125: inactive: printf("Please select a Database!\n");
  23929. 126: break;
  23930. 127: }
  23931. 128: free_up();
  23932. 129: db_active = FALSE;
  23933. 130: break;
  23934. 131:
  23935. 132: case QUIT:
  23936. 133: if (db_active)
  23937. 134: {
  23938. 135: write_db(db_name); /* write to disk */
  23939. 136: free_up();
  23940. 137: }
  23941. 138: exit(0);
  23942. 139: }
  23943. 140: }
  23944. 141: }
  23945. 142:
  23946. 143: /*
  23947. 144: * Function: backup_db
  23948. 145: * Purpose: Backup current Database to floppies
  23949. 146: * Parameters: None
  23950. 147: * Return Value: None
  23951. 148: */
  23952. 149:
  23953. 150: void backup_db() /* Backup module */
  23954. 151: {}
  23955.  
  23956.  
  23957. Listing 3
  23958. 1: /*
  23959. 2: * MDBUTIL.C
  23960. 3: *
  23961. 4: * Program: Mini-Database
  23962. 5: * Written by: Leor Zolman
  23963. 6: * Module: Utility functions
  23964. 7: */
  23965. 8:
  23966. 9: #include <stdio.h>
  23967. 10: #include <stdlib.h>
  23968.  
  23969. 11: #include "mdb.h"
  23970. 12:
  23971. 13:
  23972. 14: /*
  23973. 15: * Function: do_menu
  23974. 16: * Purpose: Simple line-oriented menu handler
  23975. 17: * Parameters: None
  23976. 18: * Return Value: None
  23977. 19: */
  23978. 20:
  23979. 21: int do_menu(struct menu_item *mnu, char *title)
  23980. 22: {
  23981. 23: int i, j;
  23982. 24: char buf[150];
  23983. 25:
  23984. 26: printf("\n%s -- Options:\n", title);
  23985. 27: for (i = 0; mnu[i].action_code != NULL; i++)
  23986. 28: printf("%2d) %s\n", i+1, mnu[i].descrip);
  23987. 29:
  23988. 30: while (1)
  23989. 31: {
  23990. 32: printf("\nYour choice? "};
  23991. 33: j = atoi(gets(buf));
  23992. 34: if (j >= 1 && j <= i)
  23993. 35: break;
  23994. 36: printf("Please select from options 1-%d: ", i+1);
  23995. 37: }
  23996. 38:
  23997. 39: return mnu[j - 1].action_code;
  23998. 40: }
  23999. 41:
  24000. 42:
  24001. 43: /*
  24002. 44: * Function: error
  24003. 45: * Purpose: Report error end terminate program
  24004. 46: * Parameters: Message to display
  24005. 47: * Return Value: None
  24006. 48: */
  24007. 49:
  24008. 50: void error(char *msg)
  24009. 51: {
  24010. 52: printf ("Fatal Condition: %s\n", msg);
  24011. 53: exit(-1);
  24012. 54: }
  24013. 55:
  24014. 56:
  24015. 57: /*
  24016. 58: * Function: alloc_rec
  24017. 59: * Purpose: Allocate memory for a Database record,
  24018. 60: * checking for an allocation error
  24019. 61: * Parameters: None
  24020. 62: * Return Value: Pointer to memory, or NULL on error
  24021. 63: */
  24022. 64:
  24023. 65: struct record *alloc_rec(void)
  24024. 66: {
  24025. 67: struct record *temp;
  24026. 68:
  24027. 69: if ((temp = malloc(sizeof(struct record))) == NULL)
  24028.  
  24029. 70: return NULL;
  24030. 71: else
  24031. 72: return temp;
  24032. 73: }
  24033. 74:
  24034. 75:
  24035. 76: /*
  24036. 77: * Function: free_up
  24037. 78: * Purpose: De_allocate all records in current Database
  24038. 79: * Parameters: None
  24039. 80: * Return Value: None
  24040. 81: */
  24041. 82:
  24042. 83: void free_up()
  24043. 84: {
  24044. 85: int i;
  24045. 86:
  24046. 87: for (i = 0; i < n_recs; i++)
  24047. 88: free(RECS[i]);
  24048. 89: }
  24049. 90:
  24050. 91:
  24051.  
  24052.  
  24053.  
  24054.  
  24055.  
  24056.  
  24057.  
  24058.  
  24059.  
  24060.  
  24061.  
  24062.  
  24063.  
  24064.  
  24065.  
  24066.  
  24067.  
  24068.  
  24069.  
  24070.  
  24071.  
  24072.  
  24073.  
  24074.  
  24075.  
  24076.  
  24077.  
  24078.  
  24079.  
  24080.  
  24081.  
  24082.  
  24083.  
  24084.  
  24085.  
  24086.  
  24087.  
  24088.  
  24089.  
  24090.  
  24091.  
  24092. Using Files As Semaphores
  24093.  
  24094.  
  24095. Lyle Frost
  24096.  
  24097.  
  24098. Lyle Frost is the owner of Citadel, a consulting and software development
  24099. firm. He can be contacted at 241 E. Eleventh St., Brookville, IN 47012 or on
  24100. the Citadel BBS at (317) 647-2403.
  24101.  
  24102.  
  24103. Multitasking operating systems allow a single application to execute as a
  24104. group of concurrent processes. These processes must usually share access to
  24105. common resources, such as data files or shared memory. In the case of a
  24106. multi-user database system, for example, each user would access the same set
  24107. of data files through separate processes. Concurrent access to a shared
  24108. resource requires some synchronization to prevent the processes from
  24109. interfering with one another. The semaphore is one of the primary constructs
  24110. for achieving this synchronization. Implementing semaphores is in general a
  24111. difficult task. However, files may serve as a very simple implementation.
  24112.  
  24113.  
  24114. Mutual Exclusion
  24115.  
  24116.  
  24117. A program segment that accesses a shared resource is called a critical
  24118. section. While in a critical section, a process must prevent other processes
  24119. from entering critical sections requiring the same resource. This type of
  24120. synchronization is called mutual exclusion.
  24121. For example, consider two concurrent processes A and B which simultaneously
  24122. modify the same record. Two distinct accesses are needed to modify a record;
  24123. the original record must first be read from its storage area, then after being
  24124. modified, must be written back. Without mutual exclusion, the timing of the
  24125. individual accesses by process A relative to those by process B is
  24126. unpredictable. If, for instance, the write by process A occurred during the
  24127. interval between the read and write by process B, the modification made by
  24128. process A would be lost (Figure 1a). Only if the two critical sections
  24129. happened not to overlap would the correct result be obtained (Figure 1c).
  24130. Mutual exclusion ensures that conflicting critical sections do not overlap.
  24131. The basic principle of mutual exclusion is not complicated: before entering a
  24132. critical section, a process must somehow allocate the required resource for
  24133. its exclusive use. If the process fails to obtain the resource because it is
  24134. currently allocated by another process, execution of the critical section must
  24135. be postponed until the resource can be acquired. The critical section may be
  24136. executed only after successfully allocating the resource, which must be freed
  24137. at the conclusion of the critical section. Figure 2 shows two processes
  24138. attempting to modify the same record simultaneously, but using mutual
  24139. exclusion.
  24140. Since an allocated resource impedes the execution of other processes needing
  24141. the same resource, critical sections should execute as quickly as possible,
  24142. and contain only the code necessary to complete the operations on the
  24143. resource. For instance, a critical section should not contain code to read
  24144. user input (unless, of course, the resource is a keyboard).
  24145.  
  24146.  
  24147. Semaphores
  24148.  
  24149.  
  24150. Semaphores are used to enforce mutually exclusive access to shared resources.
  24151. A semaphore is simply a flag that indicates to other processes that a specific
  24152. resource has been allocated. A process lowers a semaphore to indicate that a
  24153. resource is in use, then raises the semaphore when it has finished with the
  24154. resource. Successfully lowering a semaphore and entering the critical section
  24155. is also referred to as "passing the semaphore". The terminology derives from
  24156. the device used on railroads to show when a section of track is occupied. The
  24157. railroad semaphore is historically a mechanical arm that lowered a flag when
  24158. the section of track it marked was in use, then raised it when the track
  24159. became free.
  24160. The following pseudocode outlines the semaphore lower and raise operations.
  24161. Traditionally, a value of 0 is used for a lowered semaphore and a value of 1
  24162. for a raised semaphore.
  24163. semlower(semaphore s)
  24164. {
  24165. if s == 1 /* if semaphore is raised, */
  24166. then s = 0 /* lower semaphore */
  24167. else
  24168. s not available
  24169. }
  24170.  
  24171. semraise(semaphore s)
  24172. {
  24173. s = 1 /* raise semaphore */
  24174. }
  24175. At first glance, these two routines may seem trivial to implement -- and they
  24176. would be, except that semlower itself has a critical section; it must first
  24177. perform a test operation to check if s is raised, followed by a set operation
  24178. to lower s. Suppose two processes simultaneously attempted to lower the same
  24179. semaphore. If the timing was such that the test and set operations of the two
  24180. processes interleaved, each process would believe it had lowered the
  24181. semaphore. The semaphore is not in itself a solution to the root problem of
  24182. implementing mutual exclusion. Introducing the semaphore merely confines the
  24183. general concurrency problem to a single critical section. Once mutual
  24184. exclusion has been effected for the critical section in semlower, semaphores
  24185. can then provide mutual exclusion for all other critical sections.
  24186. There are two fundamental requirements for a semaphore implementation:
  24187. The semaphore must be visible to all processes which manipulate it.
  24188. Mutual exclusion must be effected for the critical section in semlower.
  24189. Assuming that a shared memory facility is available, the first requirement can
  24190. be met without great difficulty. The second, however, is a problem. While
  24191. mutual exclusion algorithms have been devised, they are all quite complex.
  24192. However, by using files as semaphores, the complex algorithms, as well as the
  24193. need for shared memory capabilities, can be avoided.
  24194. Files clearly fulfill the visibility requirement, but how they provide mutual
  24195. exclusion is not so obvious.
  24196. Access to the file system is controlled by the operating system and requires
  24197. special functions referred to as system calls. In UNIX, for example, open,
  24198. close, and unlink are file-related system calls. Though invoked exactly like a
  24199. regular function, a system call causes code within the operating system to be
  24200. executed. (Note that while the stdio library functions which access the file
  24201. system are regular functions, they must be written using system calls -- fopen
  24202. would call open, remove would call unlink, etc.)
  24203. When called to delete a file, the operating system must first check that the
  24204. file exists, then delete it. This is a test and set sequence and as such,
  24205. requires mutual exclusion. Since the operating system has complete control
  24206. over process scheduling, it can easily force a test and set to execute
  24207. consecutively. Because the operating system ensures this mutual exclusion,
  24208. files can be used as semaphores. Deleting and creating a file correspond to
  24209. lowering and raising a semaphore, respectively.
  24210. Listing 1 and Listing 2 show the salient parts of the source code for an
  24211. implementation of semaphores using files. Listing 1 (semaphor.h) should be
  24212. included by programs using these routines so that all semaphore files needed
  24213. by an application may be created as a "set" in a dedicated directory. The
  24214. semaphore set control structure semset_t defined in semaphor.h contains the
  24215. two values defining a semaphore set: the name of the directory containing the
  24216. semaphore files and the number of semaphores in the set.
  24217. A semaphore set is opened using the semopen function.
  24218. semset_t *semopen(char *semdir,
  24219. int flags, int semc);
  24220. semdir is the name of the directory containing the semaphore files. flags
  24221. values are constructed by bitwise OR-ing command and permission flags. If the
  24222. SEM_CREAT flag is set, the set will be created if it does not exist. If
  24223. SEM_EXCL is also set, semopen will fail (returning -1) if the semaphore set
  24224. already exists. The operating system dependent permission flags determine
  24225. access to the semaphore set and are usually defined in <sys/stat.h> as macros
  24226. of the form S_I*. If the semaphore set must be created, it will include semc
  24227. semaphores; otherwise, semc is ignored. The macro semcount counts the number
  24228. of semaphores in an open set. semopen returns a semaphore set pointer which is
  24229. used by other semaphore functions.
  24230. An individual semaphore can be lowered using the semlower function.
  24231. int semlower(semset_t *ssp, isemno);
  24232. ssp is a semaphore set pointer obtained from a previous call to semopen, and
  24233. semno is the number of the semaphore in that set to be lowered. If the
  24234. semaphore is already lowered, semlower fails and sets errno to EAGAIN. The
  24235. following code fragment illustrates the use of semlower.
  24236. for (n = 0; n < MAXTRIES; n++){
  24237. if (semlower(ssp, semno) == -1) {
  24238. if (errno == EAGAIN) {
  24239.  
  24240. /*semaphore already
  24241. lowered */
  24242. continue;
  24243. } else { /* error */
  24244. break;
  24245. }
  24246. }else { /* semaphore successfully lowered */
  24247. break;
  24248. }
  24249. }
  24250. A semaphore is raised using the semraise function.
  24251. int semraise(semset_t *ssp, int
  24252. semno);
  24253. ssp and semno are the same as for semlower. The source code for semlower and
  24254. semraise is shown in Listing 2.
  24255. After finishing with a semaphore set, it should be closed using semclose.
  24256. int semclose(semset_t *ssp);
  24257. All semaphores lowered by a process should be raised before calling semclose.
  24258. Finally,
  24259. int semremove(char *semdir);
  24260. removes all the semaphore files from directory semdir then removes the
  24261. directory.
  24262.  
  24263.  
  24264. Shared Locking
  24265.  
  24266.  
  24267. By definition, a semaphore allocates a resource for exclusive use by a single
  24268. process. But in many applications there are two types of critical sections:
  24269. those sections requiring exclusive access, and
  24270. those which may share access with each other, but not with critical sections
  24271. of the first type.
  24272. Introducing the second type of critical section creates what is usually
  24273. referred to as the Readers and Writers Problem. A critical section where data
  24274. will be written to a file usually requires exclusive access to the file, but
  24275. critical sections which will only read data may share access with each other.
  24276. (In database terminology exclusive locks are also called write locks, and
  24277. shared locks are also called read locks.)
  24278. Even though semaphores allocate only for exclusive access, they can also be
  24279. used to implement read and write locking. Each resource lock requires two
  24280. semaphores and a shared integer variable. The first (write) semaphore prevents
  24281. any other process from write locking the resource. The second (read) semaphore
  24282. locks not the resource, but the shared variable. The shared variable is the
  24283. read count. It contains the number of processes which have the resource read
  24284. locked.
  24285. If wsem is the write semaphore, rsem is the read semaphore, and rc is the read
  24286. count, then the algorithm to read lock a resource is:
  24287. semlower(rsem) /* allocate read count */
  24288. if rc == 0 /* if no other readers, */
  24289. semlower(wsem) /* allocate resource */
  24290. rc++ /* increment read count */
  24291. semraise(rsem) /* free read count */
  24292. The shared variable containing the number of readers is first allocated. The
  24293. first reader lowers the write semaphore to prevent the resource from being
  24294. write locked. Incrementing the read count informs the next reader that the
  24295. write semaphore is already lowered.
  24296. The algorithm for releasing a read lock is shown below.
  24297. semlower(rsem) /* allocate readcount */
  24298. rc- /* decrement readcount */
  24299. if rc == 0 /* if no other readers, */
  24300. semraise(wsem) /* free resource */
  24301. semraise(rsem) /* free readcount */
  24302. When the last reader leaves its critical section, the write semaphore is
  24303. lowered to allow the resource to be write locked. A write lock is acquired and
  24304. released by lowering and raising the write semaphore, respectively. If any
  24305. processes have the resource read locked or write locked, the write semaphore
  24306. will already be lowered and the attempted write lock will fail.
  24307. Listing 3 and Listing 4 show a portion of the implementation of r/w locking
  24308. using files. The read counts require the same visibility as the semaphores,
  24309. and so files are also used for read counts. The routines developed above are
  24310. used to manipulate the semaphores (two for each r/w semaphore). r/w semaphores
  24311. are also grouped into sets; the read count files (one for each r/w semaphore)
  24312. and the directory containing the semaphores (twice the number of r/w
  24313. semaphores) would be isolated within a single directory.
  24314. The header rwsem.h, Listing 3, defines the r/w semaphore set control structure
  24315. rwsset_t. rwsdir contains the files used by the semset_t, and rwsc is the
  24316. number of r/w semaphores. ssp points to a semaphore set used for the write and
  24317. read semaphores. lockheld points to an array of lock types containing the type
  24318. of lock held by the calling process for each r/w semaphore. (The lock type
  24319. must be remembered because the procedure to remove a read lock is different
  24320. from that for removing a write lock.)
  24321. The r/w semaphore functions
  24322. rwsset_t *rwsopen(rwsset_t *rwsp, int flags, int rwsc);
  24323. int rwscount(rwsset_t *rwsp);
  24324. int rwsclose(rwsset_t *rwsp);
  24325. int rwsremove(char *rwsdir);
  24326. are exactly analogous to their semaphore counterparts. The single function
  24327. rwslock controls locking, in place of semlower and semraise.
  24328. int rwslock(rwsset_t *rwsp, int rwsno, int ltype);
  24329. The first two parameters are the same as for semlower and semraise. The last
  24330. specifies the type of lock.
  24331. RWS_UNLCK unlock
  24332. RWS_RDLCK read (shared) lock
  24333. RWS_WRLCK write (exclusive) lock
  24334. If ltype is RWS_RDLCK and the indicated r/w semaphore is already in a write
  24335. lock state, or if ltype is RWS_WRLCK and the indicated r/w semaphore is
  24336. already in a read lock state, rwslock will fail (return -1) and set errno to
  24337. EAGAIN. As for semlower, a busy wait loop would be used for rwslock when ltype
  24338. is RWS_RDLCK or RWS_WRLCK. The source for rwslock is shown in Listing 4.
  24339.  
  24340.  
  24341.  
  24342. Conclusion
  24343.  
  24344.  
  24345. The control of concurrency has been a prominent topic for many years; E. W.
  24346. Dijkstra's paper introducing semaphores was first published in 1965. But in
  24347. spite of its relatively long history, it may be new to many microcomputer
  24348. programmers who are suddenly acquiring multitasking capabilities for the first
  24349. time. Understanding the implications of concurrency and the techniques for
  24350. concurrency control will be necessary for the programmer to fully utilize the
  24351. new multitasking systems now available for microcomputers, particularly those
  24352. that are also multi-user.
  24353. References
  24354. Calingaert, P. Operating System Elements. Englewood Cliffs, NJ: Prentice-Hall,
  24355. 1982.
  24356. Deitel, H. An Introduction to Operating Systems. Reading, MA: Addison-Wesley,
  24357. 1984.
  24358. Figure 1 Concurrent Access Without Mutual Exclusion
  24359. Figure 2 Concurrent Access With Mutual Exclusion
  24360.  
  24361. Listing 1
  24362. /* semaphore.h */
  24363.  
  24364. #ifndef SEMAPHOR_H /* prevent multiple includes */
  24365. #define SEMAPHOR_H
  24366.  
  24367. #include <limits.h>
  24368. #ifndef PATH_MAX
  24369. #define PATH_MAX (256) /* max # of characters in a path name */
  24370. #endif
  24371.  
  24372. /* constants */
  24373. #define SEMOPEN_MAX (60) /* max # semaphore sets open at once */
  24374.  
  24375. /* type definitions */
  24376. typedef struct { /* semaphore set control structure */
  24377. char semdir[PATH_MAX + 1]; /* semaphore directory path name */
  24378. int semc; /* semaphore count */
  24379. } semset_t;
  24380.  
  24381. /* function declarations */
  24382. int semclose(semset_t *ssp);
  24383. #define semcount(ssp) ((ssp)->semc)
  24384. int semlower(semset_t *ssp, int semno);
  24385. semset_t * semopen(char *semdir, int flags, int semc);
  24386. int semraise(semset_t *ssp, int semno);
  24387. int semremove(char *semdir);
  24388.  
  24389. /* semopen command flags */
  24390. #define SEM_CREAT (01000) /* create and open */
  24391. #define SEM_EXCL (02000) /* exclusive open */
  24392.  
  24393. /* error codes */
  24394. #define SEMEOS (0) /* start of error code domain */
  24395. #define SEMEMFILE (SEMEOS - 1) /* too many semaphore sets open */
  24396. #define SEMPANIC (SEMEOS - 2) /* internal semaphore error */
  24397.  
  24398. #endif /* #ifndef SEMAPHOR_H */
  24399.  
  24400.  
  24401. Listing 2
  24402. /* semaphore.c */
  24403.  
  24404. /* Supported operating systems: UNIX, MS-DOS */
  24405. #define UNIX (1)
  24406. #define MSDOS (2)
  24407. #define HOST UNIX
  24408.  
  24409.  
  24410. #include <errno.h>
  24411. #include <limits.h>
  24412. #include <stdio.h>
  24413. #include <string.h>
  24414. #if HOST == UNIX
  24415. #define PATHDLM ('/') /* path name delimiter */
  24416. #include <fcntl.h> /* open() macro definitions */
  24417. #include "syscalkr.h" /* system call declarations */
  24418. #include <sys/stat.h> /* file mode macros */
  24419. #elif HOST == MSDOS
  24420. #define PATHDLM ('\\') /* path name delimiter */
  24421. #include <fcntl.h> /* open() macro definitions */
  24422. #include <io.h> /* close(), open() declarations */
  24423. #include <sys/types.h>
  24424. #include <sys/stat.h> /* file mode macros */
  24425. #endif
  24426. #include "semaphor.h"
  24427.  
  24428. /* semaphore set table definition */
  24429. static semset_t sst[SEMOPEN_MAX];
  24430.  
  24431. /* semlower: lower semaphore */
  24432. int semlower(semset_t *ssp, int semno)
  24433. {
  24434. char path[PATH_MAX + 1];
  24435.  
  24436. /* remove semaphore file */
  24437. sprintf(path, "%s%cs%d", ssp->semdir, (int)PATHDLM, semno);
  24438. if (unlink(path) == -1) {
  24439. if (errno == ENOENT) errno = EAGAIN;
  24440. return -1;
  24441. }
  24442.  
  24443. errno = 0;
  24444. return 0;
  24445. }
  24446.  
  24447. /* semraise: raise semaphore */
  24448. int semraise(semset_t *ssp, in, semno)
  24449. {
  24450. char path[PATH_MAX + 1];
  24451. int fd = 0;
  24452.  
  24453. /* create semaphore file */
  24454. sprintf(path, "%s%cs%d", ssp->semdir, (int)PATHDLM, semno);
  24455. #if HOST == UNIX
  24456. fd = open(path, O_WRONLY O_CREAT, O);
  24457. #elif HOST == MSDOS
  24458. fd = open(path, O_WRONLY O_CREAT, S_IREAD S_IWRITE);
  24459. #endif
  24460. if (fd == -1) {
  24461. return -1;
  24462. }
  24463. if (close(fd) == -1) {
  24464. return -1;
  24465. }
  24466.  
  24467. errno = 0;
  24468. return 0;
  24469.  
  24470. }
  24471.  
  24472.  
  24473. Listing 3
  24474. /* rwsem.h */
  24475. #ifndef RWSEM_H /* prevent multiple includes */
  24476. #define RWSEM_H
  24477.  
  24478. #include <limits.h>
  24479. #include "semaphor.h"
  24480.  
  24481. /* constants */
  24482. #define RWSOPEN_MAX SEMOPEN_MAX /* max # rwsem sets open at once */
  24483.  
  24484. /* type definitions */
  24485. typedef struct { /* rwsem set control structure */
  24486. char rwsdir[PATH_MAX + 1]; /* directory */
  24487. int rwsc; /* r/w semaphore count */
  24488. semset_t *ssp; /* semaphore set */
  24489. short *lockheld; /* locks held by calling process */
  24490. } rwsset_t;
  24491.  
  24492. /* function declarations */
  24493. int rwsclose(rwsset_t *rwsp);
  24494. #define rwscount(rwsp) ((rwsp)->rwsc)
  24495. int rwslock(rwsset_t *rwsp, int rwsno, int ltype);
  24496. rwsset_t * rwsopen(char *rwsdir, int flags, int rwsc);
  24497. int rwsremove(char *rwsdir);
  24498.  
  24499. /* rwsopen command flags */
  24500. #define RWS_CREAT (01000) /* create and open */
  24501. #define RWS_EXCL (02000) /* exclusive open */
  24502.  
  24503. /* lock types */
  24504. #define RWS_UNLCK (0) /* unlock */
  24505. #define RWS_RDLCK (1) /* read lock */
  24506. #define RWS_WRLCK (2) /* write lock */
  24507.  
  24508. /* error codes */
  24509. #define RWSEOS (-20) /* start of error code domain */
  24510. #define RWSEMFILE (RWSEOS - 1) /* too many rwsem sets open */
  24511. #define RWSPANIC (RWSEOS - 2) /* internal rwsem error */
  24512.  
  24513. #endif /* #ifndef RWSEM_H */
  24514.  
  24515.  
  24516. Listing 4
  24517. #include <errno.h> /*rwslock.c */
  24518. #include <limits.h>
  24519. #include <stdio.h>
  24520. #include <string.h>
  24521. #define PATHDLM ('/') /* path name delimiter */
  24522. #include "rwsem.h"
  24523.  
  24524. /* function declarations */
  24525. int getcnt(char *file, int *cntp);
  24526. int putcnt(char *file, int cnt);
  24527.  
  24528. /* read/write semaphore set table definition */
  24529.  
  24530. static rwsset_t rwst[RWSOPEN_MAX];
  24531.  
  24532. /* rwslock: read/write semaphore lock */
  24533. int rwslock(rwsset_t *rwsp, int rwsno, int ltype)
  24534. {
  24535. int wsem = 0; /* write semaphore */
  24536. int rsem = 0; /* read semaphore */
  24537. int rc = 0; /* readcount */
  24538. char rcpath[PATH_MAX + 1]; /* readcount file path name */
  24539.  
  24540. /* identify write and read semaphores and read-count file */
  24541. wsem = rwsno * 2;
  24542. rsem = wsem + 1;
  24543. sprintf(rcpath, "%s%cr%d", rwsp->rwsdir, (int)PATHDLM, rwsno);
  24544.  
  24545. switch (ltype) {
  24546. case RWS_UNLCK: /* unlock */
  24547. switch (rwsp->lockheld[rwsno]) {
  24548. case RWS_UNLCK: /* unlock */
  24549. break; /* case RWS_UNLCK: */
  24550. case RWS_RDLCK: /* read lock */
  24551. if (semlower(rwsp->ssp, rsem) == -1) { /* allocate readcount */
  24552. return -1;
  24553. }
  24554. getcnt(rcpath, &rc); /* get readcount */
  24555. rc--; /* decrement readcount */
  24556. if (rc == 0) { /* if no other readers, */
  24557. if (semraise(rwsp->ssp, wsem) == -1) { /* free resource */
  24558. semraise(rwsp->ssp, rsem);
  24559. return -1;
  24560. }
  24561. }
  24562. putcnt(rcpath, rc); /* store new readcount */
  24563. if (semraise(rwsp->ssp, rsem) == -1) { /* free readcount */
  24564. return -1;
  24565. }
  24566. break; /* case RWS_RDLCK: */
  24567. case RWS_WRLCK: /* write lock */
  24568. if (semraise(rwsp->ssp, wsem) == -1) {
  24569. return -1;
  24570. }
  24571. break; /* case RWS_WRLCK: */
  24572. default:
  24573. errno = RWSPANIC;
  24574. return -1;
  24575. break; /* default: */
  24576. };
  24577. break; /* case RWS_UNLCK: */
  24578. case RWS_RDLCK: /* read lock */
  24579. if (rwsp->lockheld[rwsno] == RWS_RDLCK) {
  24580. errno = 0;
  24581. return 0;
  24582. }
  24583. if (semlower(rwsp->ssp, rsem) == -1) { /* allocate readcount */
  24584. return -1;
  24585. }
  24586. getcnt(rcpath, &rc); /* get readcount */
  24587. rc++; /* increment readcount */
  24588. if (rc == 1) { /* if no other readers, */
  24589.  
  24590. if (semlower(rwsp->ssp, wsem) == -1) { /* allocate resource */
  24591. semraise(rwsp->ssp, rsem);
  24592. return -1;
  24593. }
  24594. }
  24595. putcnt(rcpath, rc); /* store new readcount */
  24596. if (semraise(rwsp->ssp, rsem) == -1) { /* free readcount */
  24597. if (rc == 1) semraise(rwsp->ssp, wsem);
  24598. return -1;
  24599. }
  24600. break; /* case RWS_RDLCK: */
  24601. case RWS_WRLCK: /* write lock */
  24602. if (semlower(rwsp->ssp, wsem) == -1) { /* allocate resource */
  24603. return -1;
  24604. }
  24605. break; /* case RWS_WRLCK: */
  24606. default:
  24607. errno = EINVAL;
  24608. return -1;
  24609. break; /* default: */
  24610. };
  24611.  
  24612. /* save type of lock held */
  24613. rwsp->lockheld[rwsno] = ltype;
  24614.  
  24615. errno = 0;
  24616. return 0;
  24617. }
  24618.  
  24619.  
  24620.  
  24621.  
  24622.  
  24623.  
  24624.  
  24625.  
  24626.  
  24627.  
  24628.  
  24629.  
  24630.  
  24631.  
  24632.  
  24633.  
  24634.  
  24635.  
  24636.  
  24637.  
  24638.  
  24639.  
  24640.  
  24641.  
  24642.  
  24643.  
  24644.  
  24645.  
  24646.  
  24647.  
  24648.  
  24649.  
  24650.  
  24651.  
  24652.  
  24653. Fast Memory Allocation Scheme
  24654.  
  24655.  
  24656. Steve Weller
  24657.  
  24658.  
  24659. Steven Weller is president of Windsor Systems, specializing in OS-9,
  24660. system-level and real-time software, and computer graphics. An electronics
  24661. engineer from England, Steve has been in software for nine years and has
  24662. particular interest in parallel computer applications, modern computer
  24663. languages, operating systems, and the management of technology. He may be
  24664. contacted at 2407 Lime Kiln Lane, Louisville, KY 40222 (502) 425-9560.
  24665.  
  24666.  
  24667. In applications requiring the dynamic allocation of a large number of small
  24668. objects, the overhead associated with general-purpose allocation schemes can
  24669. be large: between 20 and 200 percent of the actual stored data. To minimize
  24670. this problem I use a layered allocation system in which standard system calls
  24671. allocate relatively large blocks of memory to a simpler memory management
  24672. subsystem.
  24673. All of the smaller objects belonging to a single data structure (e.g., a tree
  24674. or linked list) are then "borrowed" (using a low overhead allocation scheme)
  24675. from one (or a set) of these layer blocks. Unlike generalized allocation
  24676. routines, the "borrowing" system doesn't attach allocation information to any
  24677. of the borrowed objects, potentially reducing memory overhead. Moreover,
  24678. because the entire data structure is freed as a unit, I avoid the overhead of
  24679. attempting to coalesce adjacent, freed objects (except for the underlying
  24680. large blocks).
  24681.  
  24682.  
  24683. Why Not malloc()?
  24684.  
  24685.  
  24686. malloc() and free() are the most commonly used standard C function calls for
  24687. memory allocation and deallocation. They are general and easy to use, but
  24688. inefficient for small amounts of memory, both in terms of storage overhead and
  24689. speed.
  24690. malloc() collects the requested amount of memory, allocates it, and returns a
  24691. pointer to the allocated area. On my machine malloc() adds an overhead of
  24692. eight bytes to every piece of memory allocated. malloc() is also not
  24693. particularly fast since it must manipulate the links it maintains between
  24694. allocated blocks each time memory is allocated.
  24695. free() deallocates the memory block whose address is passed by undoing
  24696. malloc()'s links and adding the block to the list of free blocks, merging
  24697. adjacent blocks if possible. Using free() to deallocate a large number of
  24698. small blocks is very inefficient.
  24699.  
  24700.  
  24701. Borrowing Memory
  24702.  
  24703.  
  24704. Memory borrowing, as I call it, allows the user to obtain memory in small
  24705. pieces, but return it all in one go. A call to iniz_borrow() sets up the
  24706. system:
  24707. if ((id=iniz_borrow(2000))==0)
  24708. error("Can't get memory\n");
  24709. iniz_borrow() accepts a number which represents the block size to be allocated
  24710. from the system when memory is required, in this case 2000 bytes. The routine
  24711. allocates either one block and returns a memory ID, or returns zero indicating
  24712. that an error has occurred.
  24713. All subsequent allocations and deallocations use the unique memory ID number.
  24714. Any number of memory IDs may be created, each with its own allocation size,
  24715. but all the memory associated with one ID must be returned at the same time.
  24716. Normally each memory ID is associated with a separate large data structure.
  24717. Each time memory is needed for a small object within one of these structures,
  24718. borrow() is called:
  24719. if ((new=borrow(id,size))==0)
  24720. error("Can't get memory\n");
  24721. borrow() allocates memory from the block defined by the ID and returns a
  24722. pointer to it (here assigned to new), or a zero on error. Additional blocks
  24723. are acquired from the system if necessary.
  24724. Two functions free "borrowed" memory; both return all memory allocated with
  24725. one ID. return_borrow() returns all but the first block to the system, leaving
  24726. the memory ID valid and reusable. deiniz_borrow() returns all memory to the
  24727. system, making the memory ID invalid and unusable.
  24728.  
  24729.  
  24730. The Borrow Functions
  24731.  
  24732.  
  24733. Listing 1 contains the header information and the initialization routine.
  24734. iniz_borrow() allocates a block of memory and places the allocation
  24735. information in the MemBlock structure at the start of the block. The routine
  24736. returns the block's address as the memory ID. As more blocks of memory are
  24737. required, they will be linked to the first.
  24738. The allocate() routine shown in Listing 1 can be any allocation routine you
  24739. have, probably sbrk() or malloc(). Your routine must, however, return a zero
  24740. on failure.
  24741. Listing 2 shows the borrow() routine itself. The requested amount of memory is
  24742. rounded up to an even number of bytes, keeping the allocated memory addresses
  24743. on even byte boundaries. This restriction can be dropped if not required, or
  24744. changed to
  24745. need=(need+3)&~3
  24746. or
  24747. need=(need+7)&~7
  24748. to ensure that even word or even long word alignment is maintained.
  24749. Next the amount of memory requested is compared with the amount remaining in
  24750. the current block. If the remaining memory is insufficient, another block is
  24751. allocated and linked to the current block. borrow() updates the MemBlock
  24752. structure in the first allocated block to identify the newly allocated block
  24753. as the current block, and adjusts the offset to allow for a pointer at the
  24754. start of the new block. It is not necessary for any block other than the first
  24755. to contain the whole MemBlock structure.
  24756. The offset mb_offs identifies the amount of memory that has been allocated in
  24757. the current block. To satisfy a memory request, the address of the allocated
  24758. memory is computed (by adding the current block pointer mb_pres to the
  24759. offset), the offset is incremented (by the amount of memory allocated), and
  24760. the original memory address returned.
  24761. Listing 3 shows the deallocation routines. deiniz_borrow() returns all the
  24762. blocks allocated by the system by running down the allocated list, calling
  24763. deallocate() as it goes. deallocate() can be any deallocation routine
  24764. complementary to the allocation routine used in iniz_borrow(). It must, as
  24765. before, return a zero on failure.
  24766. return_borrow() is similar to deiniz_borrow() except that it does not return
  24767. the first block, and hence keeps the memory ID valid. The MemBlock structure
  24768. at the start of the first block is reset to show an empty first block -- the
  24769. same state that it was left in by iniz_borrow().
  24770.  
  24771.  
  24772. Block Size
  24773.  
  24774.  
  24775. Using a large block size results in fewer allocations and deallocations from
  24776. system memory, and hence greater speed, but at the expense of greater memory
  24777. overhead. If the block size is only a few times greater than the memory being
  24778. allocated by borrow(), then large amounts at the end of each block will remain
  24779. unused.
  24780.  
  24781.  
  24782.  
  24783. Conclusion
  24784.  
  24785.  
  24786. This simple memory allocation system takes advantage of the way that many
  24787. applications allocate and deallocate memory. It can be tailored to different
  24788. data structures by grouping memory allocation for each type of structure under
  24789. separate memory IDs, each with a different block size. The simple allocation
  24790. mechanism produces a fast and efficient system.
  24791.  
  24792. Listing 1
  24793. /* Header for memory blocks */
  24794. typedef struct MEMBLOCK {
  24795. struct MEMBLOCK *mb_next, /* Pointer to next block */
  24796. *mb_pres; /* Present block */
  24797. int mb_size, /* Size of blocks */
  24798. mb_offs; /* Present offset in block */
  24799. } MemBlock;
  24800.  
  24801. unsigned int iniz_borrow(), deiniz_borrow(), return_borrow();
  24802. char *borrow();
  24803.  
  24804. /* ------------------------------------------------------- */
  24805.  
  24806. /* Initialise memory */
  24807.  
  24808. /* Returns the memory ID or zero on error */
  24809.  
  24810. unsigned int iniz_borrow(block)
  24811. register int block; /* Allocation block size */
  24812. {
  24813. register MemBlock *p; /* Pointer to block */
  24814.  
  24815. /* Get first block */
  24816. if((int)(p=(MemBlock *)allocate(block))==0)
  24817. return(0);
  24818. p->mb_next=NULL; /* No next block */
  24819. p->mb_pres=p; /* This is the present block */
  24820. p->mb_size=block; /* Record the block size */
  24821. p->mb_offs=sizeof(MemBlock); /* Start past this info */
  24822.  
  24823. return((unsigned int)p);
  24824. }
  24825.  
  24826.  
  24827. Listing 2
  24828. /* Borrow Memory */
  24829.  
  24830. /* Returns a pointer to the allocated memory, or NULL */
  24831.  
  24832. char *borrow(id,need)
  24833. register MemBlock *id; /* Pointer to first block */
  24834. register int need; /* Requested memory size */
  24835. {
  24836. register MemBlock *p=id->mb_pres; /* Present block pointer */
  24837. register int oldoffs; /* Old offset */
  24838.  
  24839. /* Round need up to word multiple */
  24840. need+=need&1;
  24841.  
  24842. /* Deal with more memory required */
  24843. if(id->mb_offs+need>id->mb_size) { /* Too large to fit ? */
  24844. register MemBlock *q; /* Get another */
  24845. if((q=(MemBlock *)allocate(id->mb_size))==0)
  24846.  
  24847. return(NULL);
  24848. p->mb_next=q; /* Link to new block */
  24849. q->mb_next=NULL; /* Mark end of list */
  24850. id->mb_pres=q; /* New block is present one */
  24851. id->mb_offs=sizeof(MemBlock *); /* Reset offset */
  24852. p=q; /* Present block */
  24853. }
  24854. oldoffs=id->mb_offs; /* Record present offset */
  24855. id->mb_offs+=need; /* Move offset */
  24856. return((char *)((int)p+oldoffs)); /* Return address of memory */
  24857. }
  24858.  
  24859.  
  24860. Listing 3
  24861. /* Return all memory allocated to this ID */
  24862.  
  24863. /* NULL is returned on error */
  24864.  
  24865. unsigned int deiniz_borrow(id)
  24866. register MemBlock *id;
  24867. {
  24868. register MemBlock *nextone=id, /* Pointer to next block */
  24869. *thisone; /* Pointer to pres block */
  24870.  
  24871. while(thisone=nextone) { /* While blocks to return */
  24872. nextone=thisone->mb_next; /* Point to next block */
  24873. if(deallocate(thisone)==0) /* Return this one */
  24874. return(NULL);
  24875. }
  24876.  
  24877. return(id); /* Return non-zero */
  24878. }
  24879.  
  24880. /* --------------------------------------------------------- */
  24881.  
  24882. /* Return all memory but the first block */
  24883.  
  24884. /* NULL is returned on error */
  24885.  
  24886. unsigned int return_borrow(id)
  24887. register MemBlock *id;
  24888. {
  24889. register MemBlock *nextone, /* Pointer to next block */
  24890. *thisone; /* Pointer to pres block */
  24891. /* Return all but first */
  24892. if(nextone=id->mb_next) /* If anything to return */
  24893. while(thisone=nextone) { /* While blocks to return */
  24894. nextone=thisone->mb_next; /* Point to next block */
  24895. if(deallocate(thisone)==0) /* Return this one */
  24896. return(NULL);
  24897. }
  24898.  
  24899. /* Reset infomation in the first block */
  24900. id->mb_next=NULL; /* No next block */
  24901. id->mb_pres=id; /* This is the present one */
  24902. id->mb_offs=sizeof(MemBlock); /* Reset offset */
  24903.  
  24904. return(id); /* Return non-zero */
  24905. }
  24906.  
  24907.  
  24908.  
  24909.  
  24910.  
  24911.  
  24912.  
  24913.  
  24914.  
  24915.  
  24916.  
  24917.  
  24918.  
  24919.  
  24920.  
  24921.  
  24922.  
  24923.  
  24924.  
  24925.  
  24926.  
  24927.  
  24928.  
  24929.  
  24930.  
  24931.  
  24932.  
  24933.  
  24934.  
  24935.  
  24936.  
  24937.  
  24938.  
  24939.  
  24940.  
  24941.  
  24942.  
  24943.  
  24944.  
  24945.  
  24946.  
  24947.  
  24948.  
  24949.  
  24950.  
  24951.  
  24952.  
  24953.  
  24954.  
  24955.  
  24956.  
  24957.  
  24958.  
  24959.  
  24960.  
  24961.  
  24962.  
  24963.  
  24964.  
  24965.  
  24966.  
  24967.  
  24968.  
  24969.  
  24970. A Survey Of CUG C Compilers
  24971.  
  24972.  
  24973. Victor Volkman
  24974.  
  24975.  
  24976. Victor R. Volkman received a BS in computer science from Michigan
  24977. Technological University in 1986. Mr. Volkman is a frequent contributor to The
  24978. C Users Journal and the C Gazette. He is currently employed as Software
  24979. Engineer at Cimage Corporation of Ann Arbor, MI. He can be reached at the HAL
  24980. 9000 BBS, (313) 663-4173, 1200/2400/9600 baud.
  24981.  
  24982.  
  24983. Compiler construction is alternately the most rewarding and most frustrating
  24984. area of software development. The C Users' Group offers public domain C
  24985. compilers with source code for both those who study and those who use
  24986. compilers. These packages have been independently developed by programmers who
  24987. were often the first to implement the C language on their target machines.
  24988. Some of these compilers share the ability to compile their own source to build
  24989. new versions of themselves. All of them share their authors' vision of taking
  24990. the C language to new frontiers.
  24991.  
  24992.  
  24993. A Small History Of The Small C Compiler
  24994.  
  24995.  
  24996. Since Ron Cain's introduction of the Small C compiler into the public domain
  24997. nearly a decade ago, its implementations have spread like wildfire to nearly
  24998. every popular microprocesor. The C User's Group is fortunate to be able to
  24999. offer public domain compilers which have been ported to the Z-80, 8080, 6800,
  25000. 6809, 8086, and 68000 (see Figure 1).
  25001. Ron Cain's Small C Compiler v1.0, which debuted in the May 1980 issue of Dr.
  25002. Dobb's Journal, was originally a very small subset of the C language. Small C
  25003. has been a self-compiler since its first implementation. This means that
  25004. performance improvements in code generation and parsing can be immediately
  25005. incorporated back into the compiler itself. Small C is a one-pass compiler
  25006. which generates assembly language from a C input file. The subset of data
  25007. types which the original Small C recognized consisted only of characters,
  25008. integers, and one-dimensional arrays of either type. Additionally, the only
  25009. control statements were while and if. Small C was also restricted to bitwise
  25010. logical (&, ) operators since boolean (&&, ) operators were not supported.
  25011. In 1982, James E. Hendrix assumed trusteeship of Small C. Hendrix published
  25012. numerous upgrades through Dr. Dobb's Journal culminating in the release of
  25013. Small C v2.1 for CP/M in 1984. New features added along the way include code
  25014. optimization, data initializing, conditional compiling, extern storage, for,
  25015. while, switch/case, and goto statements, and a plethora of operators. To
  25016. complete the system, James E. Hendrix and Ernest Payne developed a CP/M
  25017. compatible version of the UNIX C standard I/O library. The internal design of
  25018. Small C v2.1 was the subject of Hendrix's The Small C Handbook.
  25019. The first published 8086 PC-DOS implementation of Small C v2.1 appeared in
  25020. 1985. Along the way, code optimization techniques were refined even more. The
  25021. present incarnation from Hendrix, Small C v2.2, is available for 8086 PC-DOS
  25022. only. Small C v2.2 was released simultaneously with Hendrix's definitive
  25023. reference work A Small C Compiler: Language, Usage, Theory, and Design in
  25024. 1988.
  25025.  
  25026.  
  25027. CUG C Compilers Based On Small C
  25028.  
  25029.  
  25030. Many of the C compilers available from CUG are based on some derivative of the
  25031. Cain or Hendrix implementation of Small C. The exceptions to this rule are the
  25032. 68000 C Compiler (disk #204) which has no lineage with Small C and the DECUS C
  25033. Preprocessor (disk #243) which is not a full compiler. Some of the CUG C
  25034. compilers based on Cain's Small C v1.1, include many of the enhancements
  25035. published in Dr. Dobb's Journal over the years. This puts them approximately
  25036. at the level of Hendrix Small C v2.0 discussed earlier. These enhanced Small C
  25037. compilers are available as disk CUG104 Z-80/8080 (CP/M 80), CUG163 8086
  25038. (PC-DOS), and CUG221 6809 (FLEX OS).
  25039. An attribute which most of the CUG C compilers share is a noticeable lack of
  25040. external documentation. All disks have less than a dozen pages of
  25041. documentation with the exception of Small C w/Floats (CUG156) which includes
  25042. 30 pages. Fortunately, their common heritage means their implementations
  25043. remain similar to the well-documented Cain and Hendrix designs. Specifically,
  25044. the Doctor Dobb's Journal issues from 1980 to 1982 (see bibliography) are the
  25045. best source for Small C versions before 2.0. Alternately, Hendrix's Small C
  25046. Handbook (now out of print) details these early versions. You might need to
  25047. check your local university library for these publications. Unfortunately,
  25048. Hendrix latest book A Small C Compiler will be less relevant to older versions
  25049. due to recent internal code redesigns.
  25050. The CUG C compilers based on Small C, regardless of version, also share
  25051. certain limitations of language features. In particular, struct, union, long,
  25052. float, and double data types are not supported. The exception to this rule is
  25053. of course Small C w/Floats (CUG156) which includes a 48-bit non-standard
  25054. float. Additionally, arrays are limited to one-dimension and pointer arrays
  25055. are specifically prohibited. These compilers also assume that ints and
  25056. pointers are equivalent. This means the size of code and data pointers must
  25057. also be the same. Small C-based compilers do not allow nested include files
  25058. nor parameterized macro substitutions (as used in stdio.h). Also, the full set
  25059. of C operators is often not present.
  25060. In general, the run-time libraries contain a good assortment of standard I/O,
  25061. string, and keyboard-polling functions. Higher-level functions such as
  25062. sprintf() are not always present. The libraries have very primitive linear
  25063. memory allocation with alloc() and free(). Blocks of allocated memory must be
  25064. freed in reverse order of allocation.
  25065. The overall ratings were based on my perception of the documentation,
  25066. completeness, and usability of the implementation.
  25067.  
  25068.  
  25069. CUG104: Small C For Z-80/8080 (CP/M 80)
  25070.  
  25071.  
  25072. This implementation of Small C for the Z-80/8080 was done by Mike Bernson of
  25073. Ann Arbor, MI. This Small C is not self-compiling and requires a special
  25074. assembler and linker which are included only in CP/M 80 executable form. The
  25075. compiler was developed with BDS C v1.41.
  25076. Mike Bernson has made several improvements to RC Small C v1.1 including most
  25077. of the features of JH Small C v2.1 except goto/label and the ternary operator.
  25078. The Standard C I/O library is included in both assembly language and object
  25079. code format. Only three pages of documentation are provided, consisting of two
  25080. pages of grammar and a one page listing of file contents.
  25081.  
  25082.  
  25083. CUG132: Small C For 6809 (Radio Shack Color Computer w/OS9)
  25084.  
  25085.  
  25086. Small C for the 6809 (Color Computer) was implemented by A.J. Griggs. This
  25087. version is close to RC Small C v1.0 since it lacks switch/case, for, and
  25088. goto/label statements among other things. This Small C is not self-compiling
  25089. and requires BDS C v1.41 or later to compile. This package requires a 6809
  25090. assembler and linker which are not included. Small C for 6809 is designed as a
  25091. cross-compiler which produces 6809 code while running under a 8080/Z-80
  25092. environment. After compilation, you would use the supplied serial-port driver
  25093. to download the object code in Motorola S HEX format to the target 6809
  25094. machine.
  25095. This C compiler cannot be self-compiled because it has hardware dependencies
  25096. on the byte order of 16-bit words. Specifically, the 6809 has the low and high
  25097. bytes stored in the reverse order of 8080 machines. The compiler assumes a
  25098. certain order in some cases and thus cannot compile itself.
  25099. This disk includes a serial driver, graphics library, and sample graphics
  25100. game. The graphics library supports real-time animation in the player-missle
  25101. arcade style. Graphics objects are managed in a list which stores their screen
  25102. position and x/y velocity. During animation, the routines automatically flag
  25103. collision of objects on the screen. The management of graphic objects is
  25104. similar to the use of sprites on Commodore C64 and C128 machines.
  25105. Also on this diskette are a total of eight pages of documentation, six on the
  25106. 6809 port and two on use of the graphics library.
  25107.  
  25108.  
  25109. CUG146: Small C For 6800 (FLEX OS)
  25110.  
  25111.  
  25112. This implementation of Small C for 6800 (FLEX OS) was completed by Serge
  25113. Stepanoff of Livermore, CA. This version is close to RC Small C v1.0 since it
  25114. lacks switch/case, for, and goto/label statements among other things. An
  25115. additional restriction is that identifiers are limited to six significant
  25116. characters. This Small C is not self-compiling and requires BDS C v1.41 or
  25117. later to compile.
  25118. This package does not include a complete Standard C I/O library. A nonstandard
  25119. printf() is used which requires that the number of arguments be passed as the
  25120. last parameter.
  25121. Small C for 6800 (FLEX OS) does not compile to assembly or machine language,
  25122. but rather to a pseudo-code. A small pseudo-code interpreter, less than 2K,
  25123. actually executes the user's pseudocode. To run this pseudo-code in a
  25124. different environment requires only the rewrite of the interpreter and the
  25125. runtime library for the target machine. However, the source code for the
  25126. interpreter is not included on the distribution diskette.
  25127. The diskette contains 11 pages of documentation, the first five pages are
  25128. devoted to how to use the compiler and the remainder to the run-time library.
  25129.  
  25130.  
  25131. CUG156: Small C w/Floats (CP/M)
  25132.  
  25133.  
  25134.  
  25135. Small C w/Floats (CP/M) was implemented by James R. Van Zandt of Nashua, NH.
  25136. This package was originally available as disk #224 from the Sig/M-Amateur
  25137. Computer Group of Iselin, New Jersey. This version is close to RC Small C v1.0
  25138. since it lacks switch/case, for, and goto/label statements. Additionally, the
  25139. following operators are not supported: logical or ( ), logical and (&&),
  25140. logical not (!), bitwise-not (~), and the assignment operators (+=, -=, et.
  25141. al.).
  25142. This disk includes the executable compiler and is self-compiling. The compiler
  25143. reads C source and produces Z-80 assembly language. The two major speed
  25144. enhancements relative to Ron Cain's original compiler are a hash coded symbol
  25145. table and 1K disk buffers. Additionally, the compiler will resolve symbols
  25146. uniquely up to the first 16 characters. This disk also includes the ZMAC macro
  25147. assembler and ZLINK linker in executable form only.
  25148. Small C w/Floats supports the following usage of floating point:
  25149. double d; 48 bit floating point
  25150. double *d; pointer to double
  25151. double d(); function returning double
  25152. double d[5]; array of doubles
  25153. Storage classes, structures, multidimensional arrays, unions, and more complex
  25154. types like double **d are not included.
  25155. The layout of doubles does not conform to IEEE standard. These routines will
  25156. execute only on a Z-80. They use the alternate registers and some of the
  25157. undocumented instructions of that processor.
  25158. Small C w/Floats includes a full complement of transcendental functions for
  25159. type double (Listing 1).
  25160. If the "profile and trace" (-P) option of the compiler is used, each call to
  25161. err() results in a walkback trace of function calls. In addition, an execution
  25162. profile is displayed on the console at program termination (call to exit()).
  25163. The profile consists of a list of the functions and the number of times (up to
  25164. 999999) each was called. This is sometimes useful for debugging (to spot
  25165. functions that are never called), but is most valuable for program execution
  25166. time optimization.
  25167. With 30 pages of documentation, Small C w/Floats is the best documented of any
  25168. compiler available from CUG. The documentation covers compiler usage and
  25169. internal, floating point routines, Standard C I/O library, ZMAC macro
  25170. assembler, and the ZLINK linker.
  25171.  
  25172.  
  25173. CUG163: Small C For 8086 (PC-DOS)
  25174.  
  25175.  
  25176. This implementation of Small C for 8086 (PC-DOS) was completed by Daniel R.
  25177. Hicks of Rochester, MN. Small C for 8086 (PC-DOS) is distributed on two
  25178. diskettes, the first contains the run-time library source and the second
  25179. contains the compiler source and executable. This package was originally
  25180. available as disk #152 from the Personal Computer Club of Toronto, Canada.
  25181. This is a self-compiler, but does require your own assembler and linker. This
  25182. port of Small C is based on JH Small C v2.0 so that it does support
  25183. switch/case, for, goto/label statements. Hicks standard C I/O library provides
  25184. very good compatibility with its UNIX counterpart.
  25185. Hicks implementation imposes the following additional restrictions: lower-case
  25186. and upper-case symbols are synonymous, both local declarations within a block
  25187. and goto statements may not be used simultaneously, and the sizeof() operator
  25188. is not supported.
  25189. Parameters are pushed in order of occurrence: The first parameter in a list is
  25190. the first one pushed and therefore the deepest one in the stack. This is
  25191. opposite the order of many C compilers, and it prevents some C library
  25192. functions (such as printf) from being able to determine the parameter count by
  25193. examining just the first or second parameter. For this reason, the compiler,
  25194. prior to a CALL, loads register DL with the parameter count, thus allowing
  25195. functions such as printf to be implemented.
  25196. Included on the diskette are nine pages of detailed documentation on the
  25197. capabilities and limitations of the compiler.
  25198.  
  25199.  
  25200. CUG170: Miscellany V (Caprock C, version N for IBM-PC)
  25201.  
  25202.  
  25203. Caprock Small C for 8086 (PC-DOS) was implemented by Caprock Systems, Inc. of
  25204. Arlington, TX. This disk was originally available as disk #315 from PC
  25205. Software Interest Group (PC-SIG) of Sunnyvale, CA. This compiler is supplied
  25206. in source form only, an executable version is not included. Additionally, the
  25207. standard C I/O library is missing from this distribution. This version is
  25208. close to RC Small C v1.0 since it lacks switch/case, for, and goto/label
  25209. statements.
  25210. When compiled under Microsoft C 5.1, this file produced four errors and 53
  25211. warnings. All of these problems were the result of the assumption that
  25212. integers are interchangeable with pointers.
  25213. No documentation is included with this compiler.
  25214. True to its name, the Miscellany V disk offers over 20 files of C functions.
  25215. Some of the other offerings on this disk include Life and Towers of Hanoi
  25216. games, a binary to Intel HEX format converter, and several keyboard utilities.
  25217.  
  25218.  
  25219. CUG204: 68000 C Compiler (UNIX System V)
  25220.  
  25221.  
  25222. The 68000 C Compiler (PC-DOS) was completed by Matthew Brandt of Norcross, GA.
  25223. This compiler is intended as an instructive tool for personal use. Any use for
  25224. profit without the written consent of the author is prohibited. As stated
  25225. earlier, this is the only C compiler offered by CUG which is not derived from
  25226. RC or JH Small C. This is an optimizing C compiler which generates assembly
  25227. language for the Motorola 68000 processor. This system also requires a 68000
  25228. assembler and linker which the user must supply. It has successfully compiled
  25229. itself on UNIX System V running on a Motorola VME-10. Since this code was
  25230. written for a machine with long integers it may exhibit some irregularity when
  25231. dealing with long integers on the IBM-PC.
  25232. This compiler vies with Small C w/Floats (CUG #156) for the best
  25233. implementation of C. Although the 68000 C Compiler does not support floats, it
  25234. does have features not found in any other CUG C compiler: longs, structures,
  25235. unions, complex types (e.g. char **argv), enumerated types, and functions
  25236. which return pointers to structures.
  25237. The disk includes one page of documentation outlining the limitations of the
  25238. compiler. Brandt offers the following warning: "The author makes no
  25239. guarantees. This is not meant as a serious development tool although it could,
  25240. with little work, be made into one." The preprocessor does not support
  25241. parameterized macro substitutions, only #include and #define macros are
  25242. supported. Brandt advises that function arguments declared as char may not
  25243. work properly and should be changed to int. When the compiler encounters a
  25244. syntax error, an error number is printed but no descriptive text is provided.
  25245. Lastly, the size of functions is slightly limited due to the fact that the
  25246. entire function is parsed before any code is generated.
  25247. The compiler can be compiled by Microsoft C v3.0 or higher. MSC will issue
  25248. many warnings but they can be ignored. The file MAKE.BAT may be used to
  25249. rebuild the compiler.
  25250.  
  25251.  
  25252. CUG221: 6809 C Compiler (FLEX OS)
  25253.  
  25254.  
  25255. This implementation of Small C for 6809 (FLEX OS) was completed by Dieter H.
  25256. Flunkert. The author has made several improvements to RC Small C v1.1 plus
  25257. most of the features of JH Small C v2.1 except goto/label. Small C for 6809
  25258. (FLEX OS) has all other C control statements including switch/case, do/while,
  25259. and for. Additionally, all C operators are supported including the elusive
  25260. comma (,), ternary (?), and assignment operators (+=, -=, et. al). However,
  25261. like most other Small C implementations, the data types for float, double,
  25262. long, structures, and unions are not present.
  25263. An executable version of the compiler is not provided on the diskette. This
  25264. system requires the TSC relocatable assembler, library generator and linking
  25265. loader which the user must supply. The standard C I/O library is included in
  25266. both C source and assembly language formats. The compiler has seven pages of
  25267. documentation detailing the grammar and preprocessor commands.
  25268. When compiled under Microsoft C v5.1, it was revealed that many of the
  25269. #include directives did not have quoted filenames (e.g. #include stdio.h).
  25270. Once again, many warnings appeared from the use of integers as pointers.
  25271. Proper compilation required adding #define VMS to every module.
  25272.  
  25273.  
  25274. CUG243: DECUS C Preprocessor (PC-DOS)
  25275.  
  25276.  
  25277. The DECUS C Preprocessor (CPP) was originally implemented by Martin Minnow.
  25278. CPP was subsequently ported to PC-DOS by Ted Lemon and Jym Dryer. CPP reads a
  25279. C source file, expands macros and include files, and writes an input file for
  25280. the C compiler. If no file arguments are given, it reads from stdin and writes
  25281. to stdout. If one filename is given, it will be the input file. If a second
  25282. filename is given, it will be the output file. The full command line format
  25283. is:
  25284. cpp [-options] [infile [outfile]]
  25285. The DECUS C Preprocessor has been updated to meet the specifications of the
  25286. Draft ANSI C Standard. However, this C preprocessor is not designed to handle
  25287. floating point expressions. An experimental floating point source file is
  25288. provided for those who wish to experiment with it.
  25289. The following options are supported. Options may be given in either case.
  25290.  
  25291. -I directory
  25292. Add this directory to the list of directories searched for
  25293. #include "..."
  25294. and
  25295. #include ...
  25296. commands. Note that there is no space between the -I and the directory string.
  25297. More than one -I command is permitted. On non-UNIX systems -I directory is
  25298. forced to upper case.
  25299. -D name=value
  25300. Define the name as if the programmer wrote #define<name><value> at the start
  25301. of the first file. If is not given, a value of 1 will be used. On non-UNIX
  25302. systems, all alphabetic text will be forced to upper case.
  25303. -U name
  25304. Undefine the name as if #undef name were given. On non-UNIX systems, name will
  25305. be forced to upper case.
  25306. -X number
  25307. Enable debugging code. If no value is given, a value of 1 will be used. (For
  25308. maintenance of CPP only.)
  25309. The preprocessor will look for an environment variable INCLUDE if include
  25310. files cannot be found in the -I directories. Unfortunately, only a single
  25311. search directory can be specified in the INCLUDE path (e.g. SET
  25312. INCLUDE=\MSC\INCLUDE;\MY\SRC will fail).
  25313. CPP has been successfully built with Lattice C v2.00 and Microsoft C v3.00.
  25314. The distribution disk contains four pages of documentation detailing how to
  25315. prepare CPP under several different memory models.
  25316. Bibliography
  25317. Cain, Ron. "A Small C Compiler for the 8080s." Dr. Dobb's Journal, April-May
  25318. 1980, pp. 5-19.
  25319. Cain, Ron. "A Runtime Library for the Small C Compiler." Dr. Dobb's Journal,
  25320. September 1980, pp. 4-15.
  25321. Hendrix, J. E. "Small-C Expression Analyzer." Dr. Dobb's Journal, December
  25322. 1981, pp. 40-43.
  25323. Hendrix, J. E. "Small-C Compiler, v.2." Dr. Dobb's Journal, December 1982, pp.
  25324. 15-63. and January 1983, pp. 48-64.
  25325. Hendrix, J. E. and Payne, L. E. "A New Library for Small_C." Dr. Dobb's
  25326. Journal, May 1984, pp. 50-81, and June 1984, pp. 56-69.
  25327. Hendrix, J. E. "Small-C Update." Dr. Dobb's Journal, August 1985, pp.84-91.
  25328. Hendrix, J. E. The Small C Handbook. Redwood City, CA: M&T Publishing Inc.,
  25329. 1984.
  25330. Hendrix, J. E. A Small-C Compiler: Language, Usage, Theory, and Design.
  25331. Redwood City, CA: M&T Publishing Inc., 1988.
  25332. Volkman, Victor R. "Revised Handbook Details Small C Innards," The C Users
  25333. Journal, February 1989, pp. 9-10.
  25334. Ward, Robert and Donna, Ed., The C Users' Group Library, McPherson, KS: R&D
  25335. Publications, Inc., 1986.
  25336. Figure 1 Summary of CUG C Compilers
  25337.  
  25338.  Target Implementation
  25339.  CUG Target Operating Based on Port Date of Last Overall
  25340. Disk # CPU System From Revision Rating
  25341. ------------------------------------------------------------------------
  25342.  104 Z-80/8080 CP/M 80 v2.2 RC Small C v1.1 06/28/1981 ***
  25343.  132 6809 0S-9 RC Small C v1.1 10/18/1983 **
  25344.  146 6800 FLEX v2.1 RC Small C v1.1 09/09/1982 **
  25345.  156 Z-80 CP/M RC Small C v1.2 08/02/1984 ****
  25346.  163 8086 PC-DOS 1.1 JH Small C v2.0 01/14/1984 ***
  25347.  170 8086 PC-DOS 1.0 RC Small C v1.0 06/01/1982 *
  25348.  204 68000 Unix V N/A 01/01/1986 ****
  25349.  221 6809 FLEX RC Small C v1.0 11/15/1986 ***
  25350.  243 8086 PC-DOS 2.0 DECUS 12/01/1985 N/A
  25351.  
  25352. The overall ratings were based on my perception of the documentation,
  25353. completeness, and usability of the implementation.
  25354.  
  25355. Listing 1
  25356. atan(), /* arc tangent */
  25357. sin(), /* sine */
  25358. atan2(), /* atan2(a,b) = arctan of a/b */
  25359. sinh(), /* hyperbolic sine */
  25360. cos(), /* cosine */
  25361. sqrt(), /* square root */
  25362. cosh(), /* hyperbolic cosine */
  25363. tan(), /* tangent */
  25364. exp(), /* exponential */
  25365. tanh(); /* hyperbolic tangent */
  25366. log(), /* natural logarithm */
  25367. pow(), /* pow(x,y) = x**y */
  25368. log10(), /* log base 10 */
  25369.  
  25370.  
  25371. float(x); double x; /* integer to floating point
  25372. conversion */
  25373. fmod(x,y); double x,y; /* mod(x,y) /
  25374. if 0 < y
  25375. then 0 <= mod(x,y) < y and
  25376. x = n*y + mod(x,y)
  25377. for some integer n */
  25378. fabs(x); double x; /* absolute value */
  25379. floor(x); double x; /* largest integer not greater
  25380. than */
  25381. ceil(x); double x; /* smallest integer not less than */
  25382. rand(); /* random number in range 0...1 */
  25383.  
  25384.  
  25385.  
  25386.  
  25387.  
  25388.  
  25389.  
  25390.  
  25391.  
  25392.  
  25393.  
  25394.  
  25395.  
  25396.  
  25397.  
  25398.  
  25399.  
  25400.  
  25401.  
  25402.  
  25403.  
  25404.  
  25405.  
  25406.  
  25407.  
  25408.  
  25409.  
  25410.  
  25411.  
  25412.  
  25413.  
  25414.  
  25415.  
  25416.  
  25417.  
  25418.  
  25419.  
  25420.  
  25421.  
  25422.  
  25423.  
  25424.  
  25425.  
  25426.  
  25427.  
  25428.  
  25429.  
  25430.  
  25431.  
  25432.  
  25433. Standard C
  25434.  
  25435.  
  25436. Wha Gang Agley
  25437.  
  25438.  
  25439.  
  25440.  
  25441. P.J. Plauger
  25442.  
  25443.  
  25444. P.J. Plauger has been a prolific programmer, textbook author, and software
  25445. entrepreneur. He is secretary of the ANSI C standards committee, X3J11, and
  25446. convenor of the ISO C standards committee.
  25447.  
  25448.  
  25449. Nothing is perfect. A document produced by a committee is certainly no
  25450. exception. It is hardly surprising, therefore, that people have found much to
  25451. criticize in the ANSI standard for C.
  25452. Most of the imperfections can be chalked up to political compromise. Some are
  25453. existing practices that are too deeply entrenched to change, no matter how
  25454. strong the current consensus against them. A few are simply things that the
  25455. standards committee arguably got wrong and didn't fix. A few more are
  25456. important additions that somehow never garnered enough concerted support to
  25457. make it in.
  25458. Preprocessing, for example, was in the worst shape of any part of the C
  25459. language. The committee did rather a good job of tidying up several messes in
  25460. this area. Just defining the preprocessing phases more precisely was a major
  25461. contribution. Still, there were a few botches and omissions.
  25462. I have been one of the strongest defenders of the ANSI C standard produced by
  25463. committee X3J11. As an active participant, I saw the need for compromise and
  25464. the need to retain backward compatibility even when it hurt. I also know
  25465. intimately how much work went into producing the standard. If a few areas
  25466. couldn't get cleaned up in time, so be it. The ANSI C standard is still one of
  25467. the best language standards I have ever encountered.
  25468. Nevertheless, I am not blind to the shortcomings of the document we produced.
  25469. We missed a number of opportunities to make the language better in small ways.
  25470. We committed the sin of inconsistency more times than I care to admit. We left
  25471. out all sorts of clever improvements to the C language. I have my own list of
  25472. gripes about the C standard.
  25473. I figured that it was time for a change of pace in these pages. After a couple
  25474. of years of explaining and defending the C standard, I plan to take a few
  25475. potshots at it. What follows is a weakly ordered collection of observations.
  25476. Each describes some way in which I feel the standard could have been better.
  25477. For now, I confine my remarks to the language proper. I plan to devote
  25478. considerable attention to the Standard C library in the months to come.
  25479.  
  25480.  
  25481. What Didn't Get Cleaned Up
  25482.  
  25483.  
  25484. We missed several opportunities to tidy up the language proper. Here are a few
  25485. of them.
  25486. Historical usage prevented us from making floating literals type float by
  25487. default. It makes more sense to add a prefix to get type double. Sadly, you
  25488. must add an F to get the former, since C has traditionally considered floating
  25489. literals to have type double.
  25490. Similarly, the committee had to back off from making string literals type
  25491. array of const char. Too many existing programs have code such as
  25492. char *p = "abc";
  25493. which would require a cast to avoid a diagnostic. So string literals have the
  25494. curious property of being semantically const (for a portable program) without
  25495. having the type that goes with the semantics.
  25496. The French standards committee, AFNOR, wanted to put the null pointer constant
  25497. NULL into the language. So did a few other people. It has the same slippery
  25498. semantics that nul enjoys in Pascal, but without the same full language
  25499. support. As a consequence, different implementations must define it as a macro
  25500. in different ways. That invites its misuse, which in turn makes it harder to
  25501. write portable programs.
  25502. Several people proposed various schemes for making enumerations more strongly
  25503. typed. Most were too scary to adopt. The rest failed to garner enough support
  25504. even for extended debate. What we ended up with is somewhat better than using
  25505. preprocessor macros to name constants, but not much.
  25506. Each enumeration you write becomes a synonym for one of the integer types that
  25507. promotes to type int. (An implementation can tailor the storage it uses to
  25508. represent an enumeration.) As far as type checking goes, however, an
  25509. enumeration constant or data object simply has an integer type. You can mix
  25510. apples and oranges.
  25511. We talked more about making bitfields better, but in the end we didn't do
  25512. much. What you want, at the very least, is the ability to declare the size of
  25513. "storage unit" that you are carving up into bitfields. You want eight
  25514. different base types, the signed and unsigned flavors of char, short, int, and
  25515. long.
  25516. The standard provides only three base types, plain int, signed int, and
  25517. unsigned int. The plain flavor has special meaning in this context (and only
  25518. in this context). It lets the implementation define whether the component
  25519. bitfields have values that are signed or unsigned. That wart was added to be
  25520. nice to existing implementations, not to make bitfields any more usable.
  25521. We talked at great length about value preserving versus unsigned preserving
  25522. arithmetic. (It is more fair to say that we fought tooth and nail.)
  25523. Nevertheless, none of us tried to fix a closely related problem, the surprises
  25524. that abound when you mix signed int and unsigned int operands.
  25525. C traditionally calls for the signed operand to be converted to unsigned,
  25526. which is the type of the result. To get a sensible value in many cases,
  25527. however, you should convert both to a slightly larger signed type. We
  25528. shuddered to think what changing this rule might do to existing programs, so
  25529. we left the problem alone. I wish we could have fixed it.
  25530. When I wrote my first C compiler many years ago, the first thing I found
  25531. myself hating was the unrestricted goto statement. You can write a goto that
  25532. transfers control into a block from somewhere outside. You can even jump to
  25533. the statements controlled by if, else , while, and other flow-of-control
  25534. keywords. What that does to code optimization is beyond belief. Either you
  25535. despair of doing many optimizations or you write a much larger translator.
  25536. We discussed restricting goto statements on several occasions. What prompted
  25537. us to leave them alone was the protests of an important constituency. More and
  25538. more people write applications that generate C code to be compiled, as a sort
  25539. of universal assembly language. A number of existing applications depend on
  25540. the ability to write ugly goto statements that no human being need ever see.
  25541. Were we to tidy up the semantics of control flow, we would require serious
  25542. restructuring of these applications. With no little sadness, we left the goto
  25543. alone.
  25544. There was one area that even our extensive cleaning could not rescue
  25545. completely. It was simply too dirty. I refer to the whole business of
  25546. declaring and naming external variables. The problem is that C must work with
  25547. many existing assemblers and linkers built to ancient specifications. That
  25548. severely limits the length of external names. The committee had no serious
  25549. problem increasing internal names to 31 significant characters. But we balked
  25550. at requiring more than the worst-case six characters (and single case of
  25551. letters) required by the stupidest of existing linkers. Despite heated debate,
  25552. the majority did not want to add to the difficulty of linking C with other
  25553. languages.
  25554. Another aspect of this problem affects how you write multiple declarations for
  25555. the same external variable. C programmers need reliable methods for ensuring
  25556. that each variable has a definition, and that none has a multiple definition.
  25557. Linkers vary all over the map in the kind of machinery they provide. As a
  25558. consequence, C developed several dialects in this area. I believe the
  25559. committee did an admirable job of embracing all these dialects and
  25560. accommodating the varied linker technologies. It's too bad, however, that we
  25561. couldn't just throw it all away and do it over properly.
  25562.  
  25563.  
  25564. What Went In Wrong
  25565.  
  25566.  
  25567. In some cases, what we added to the language proper wasn't exactly right.
  25568. We botched things a bit when we introduced preprocessing numbers. These are
  25569. tokens that subsume all valid numeric C tokens. We defined them to clarify
  25570. what intermediate forms can occur during preprocessing while you endeavor to
  25571. paste together valid numeric C tokens. The only problem is, 0X12E+3 now looks
  25572. like a single preprocessing number (which becomes an invalid numeric C token).
  25573. In the past, most translators knew to parse it as a hexadecimal literal, a
  25574. plus operator, and a decimal literal. We must now learn to be wary of
  25575. hexadecimal literals that end in E.
  25576. The include directive had to compromise between two rather different
  25577. implementation styles. One approach is to parse just enough of each C source
  25578. line during preprocessing to decide what to do with the rest. In this case,
  25579. angle brackets and double quotes parse as special delimiters within the
  25580. include directive. The other approach is to parse every line into
  25581. preprocessing tokens, then decide what to do. That makes it very exciting to
  25582. parse directives such as
  25583. #include </*.h>
  25584. If you see that you are building an include directive soon enough, you know to
  25585. ignore anything funny before the closing angle bracket. If you first tokenize
  25586. and then look, you may decide that the /* signals the start of a comment.
  25587. The committee endeavored to describe preprocessing in such a way that either
  25588. approach is acceptable. Sadly, the words were reworked several times by
  25589. editors with conflicting views. I can't honestly report that the
  25590. pre-tokenizers were well treated in the end. You can still pre-tokenize each
  25591. line when parsing C, but you have to indulge in a few heroic measures to
  25592. rescue include directives.
  25593. Another example also has to do with how you write declarations, but you can't
  25594. blame any problems on existing linkers. The difficulties are purely internal
  25595. to C.
  25596. I refer to the outrageous overloading of the storage class keywords. What you
  25597. mean by static or extern (or by writing no storage class at all) can have
  25598. three different meanings, depending upon where you write the declaration. And
  25599. if another declaration for the same name is in scope, each of these meanings
  25600. can change again. C has always been messy in this regard, but the committee
  25601. made it even messier with one or two arbitrary decisions.
  25602. I have tried to tabulate the semantics of storage class keywords several
  25603. different ways. (See, for example, "What's in a Name?" CUJ February 1988, and
  25604. Standard C by P.J. Plauger and Jim Brodie, Microsoft Press, Redmond WA, 1989.)
  25605. None of the presentations have a compelling logic, because the underlying
  25606. machinery is not entirely logical. It could have been made much cleaner.
  25607. Another thing we got wrong was allowing the sizeof operator to accept an
  25608. rvalue operand. I suspect most people who voted for the extension assumed you
  25609. could make useful tests with it. For instance, you might think that sizeof
  25610. (x+y) would tell you whether two floats are added in double precision on a
  25611. particular implementation. Not so. The type of the expression is float even if
  25612. the intermediate representation happens to be double.
  25613. The extension was worse than useless, however, because it caused trouble.
  25614. People started asking all sorts of embarrasing questions about the types of
  25615. various rvalues. And the committee started deciding answers all sorts of
  25616. different ways. We now have the situation that sizeof 'a' can be larger than
  25617. sizeof <'a' even though sizeof (char) is less than sizeof (wchar_t). Yuk.
  25618.  
  25619. There is only one other thing in the C language proper that I think we got
  25620. really wrong -- the semantics of pointers to constant data objects. What I
  25621. wanted was a fairly serious promise. The data object pointed to by any pointer
  25622. to const type should be truly constant, at least for a while. ("A while"
  25623. should be from the time execution enters the function containing a reference
  25624. using the pointer until the function returns.)
  25625. What this restriction provides is much of the semantics you need to safely
  25626. parallelize C code automatically. What it evidently costs you is additional
  25627. subtle compatibility problems with C++. At least that was the strongest
  25628. argument I heard against the stronger semantics.
  25629. So we settled for a fairly wimpy position. All that a pointer to const assures
  25630. you is that you can't alter the value stored in a data object by using that
  25631. particular pointer. You can't optimize much, however, because some other
  25632. agency might be changing the stored value.
  25633. I backed the addition of the notorious noalias type qualifier in large part
  25634. because of the differences over pointers to const. I identified five or six
  25635. desirable sets of semantics for accessing data objects. Three type qualifiers
  25636. gives you eight possibilities. When noalias got shot down, we had to settle
  25637. for only four. They weren't the four I wanted.
  25638.  
  25639.  
  25640. What Didn't Get In
  25641.  
  25642.  
  25643. Lots of things didn't get into the language proper. Here are a few whose loss
  25644. I lament.
  25645. Our failure to solve the non-ASCII character set problem still haunts us at
  25646. the international level. We need alternate spellings of the operators and
  25647. punctuators that use the more esoteric ASCII characters, since these are often
  25648. recycled in ISO 646 or even absent in EBCDIC. Trigraphs such as ??< just don't
  25649. cut it for readability. Sadly, the committee could never agree on a particular
  25650. set of more readable operators.
  25651. All sorts of clever additions were suggested to make macros more powerful.
  25652. Most I cheerfully helped beat down, but two failed suggestions I miss. One is
  25653. for some form of conditional macro, such as
  25654. #define ptc(f,c)
  25655. eq(f,stdin,putchar(c),putc(f,c))
  25656. If the first two arguments to eq match (after expansion) then the third is
  25657. retained, otherwise the fourth. With recursion, you can write wondrous macro
  25658. definitions.
  25659. The other thing I miss is some way to create character literals. You can now
  25660. create a string literal from argument X by writing #X within a macro
  25661. definition. It would be nice if you could create a character literal by some
  25662. similar mechanism. Since the next obvious operator ## is already defined,
  25663. however, that suggests a rather odious ### which few people could swallow.
  25664. Dave Prosser suggested a rather nice notation, but not until well after the
  25665. committee (and several implementations) got settled with the current one.
  25666. A typeof operator would also help make more powerful macros. It would let you
  25667. declare temporary data objects having the same type as one of the arguments to
  25668. a macro. You could then write a generic "swap" macro, as in:
  25669. #define swap(x, y)\
  25670. { typeof (x) t;\
  25671. t = (x);\
  25672. (x) = (y);\
  25673. (y) = t; }
  25674. Of course, swap can only take the place of a statement. It cannot yield a
  25675. value. That's what you need to write a safe macro for, say, the maximum value
  25676. of two arguments. Otherwise, it is hard to avoid evaluating an argument
  25677. expression twice, side effects and all. To get temporaries inside a
  25678. subexpression, you need some way to delimit a local scope. Several schemes
  25679. were proposed, none were adopted.
  25680. A similar but somewhat different need is the ability to construct a structure
  25681. on the fly. More than one existing implementation lets you write something
  25682. like (struct complex){cos(th), sin(th)} within an expression. C is certainly a
  25683. more attractive language, at least to some constituencies, with such
  25684. expressive capabilities.
  25685. The last thing I really miss is some form of repetition counts within data
  25686. initializers. The Whitesmiths C complier let you write things like:
  25687. char pattern[1000] = {
  25688. [100] '.',
  25689. [800] 'X',
  25690. [100] '.'};
  25691. which is much easier to type, and maintain, than spelling out all the data.
  25692. Beyond this point, my wish list dribbles off with items I find less important.
  25693. Many of my customers loved the case ranges we added to Whitesmiths' C. Unnamed
  25694. unions within structures can eliminate the need for dummy member names.
  25695. Arbitrary rvalues in initializers for auto arrays and structures can have
  25696. their uses. All of these features I can take or leave, however.
  25697. I would like to have seen arrays become first class objects in Standard C.
  25698. Array assignment and functions returning arrays have always been expressible,
  25699. despite what many people think. The advent of function prototypes gave us a
  25700. way to pass functions as arguments. Nevertheless, the confusion surrounding
  25701. arrays as lvalues in C is so widespread that even I must acknowledge the
  25702. dangers. I remain a minority of one in this area, I fear, in being willing to
  25703. face those dangers and fix array handling in Standard C.
  25704.  
  25705.  
  25706. Conclusion
  25707.  
  25708.  
  25709. Having said all this, I now feel moved to make a few disclaimers. First, I
  25710. acknowledge that everyone has a list of grievances about the current C
  25711. standard. I don't presume that my list is more important or (much) more wisely
  25712. considered than all others. It just happens to be my list, and this is my
  25713. soapbox.
  25714. Second, I do not feel ill used that my list of grievances is so long. I got
  25715. plenty of opportunity to mouth off during the committee meetings. (Many
  25716. witnesses can attest that I got more than my share of opportunities.) I felt
  25717. well heard and was pleased to see any number of issues go the way I hoped.
  25718. Last and most important, I don't even want most of these grievances satisfied.
  25719. (I argued against fixing many of them when they were debated.) I respect the
  25720. need to satisfy diverse constituencies. If I got my way on many of these
  25721. issues, I would feel duty bound to accept the strong desires of others in
  25722. similar areas. I far prefer a compromise language with widespread support to
  25723. one that meets my needs but alienates many others.
  25724. Even if I were the sole arbiter, I still would not make many of the changes I
  25725. outlined here. Why? Because the language would be too different from the C we
  25726. know and love. And it would get that much bigger for a questionable increase
  25727. in value.
  25728. Standard C is essentially twice as big as the C described by Kernighan and
  25729. Ritchie. Admittedly, complexity is hard to quantify, but I arrive at that
  25730. number through three telling metrics. The size of the Whitesmiths C compiler
  25731. doubled in lines of source by the time we achieved full compliance with
  25732. Standard C. It also doubled in bytes of executable code. And the size of the
  25733. reference manual that went with it doubled in pages. I believe Standard C is
  25734. still intellectually manageable, but is beginning to strain the bounds of a
  25735. "small" language.
  25736. Think how big the language would have gotten had committee X3J11 tried to
  25737. please everyone. Or even just me.
  25738. Standard Finalized
  25739. The ANSI C standard has been adopted! The ANSI Board of Standards Review (BSR)
  25740. voted unanimous approval at their December meeting of the draft developed by
  25741. committee X3J11 and approved by X3.
  25742. BSR was meticulous in informing the complainant who had delayed progress of
  25743. the standard for the past year. He was given a generous period of time to file
  25744. a further protest with BSR. The time period expired, however, with no protests
  25745. filed.
  25746. The official designation if the new C standard is ANSI X3.159-1989. It came in
  25747. just under the wire, but it did earn a 198X designation.
  25748. ISO Update
  25749. The C standard commenced its six-month balloting period as a "draft
  25750. international standard" (DIS) in December 1989. That is normally the final
  25751. approval process before SC22 sends the draft on for mechanical review and
  25752. adoption by ISO. It is widely understood, however, that both the United
  25753. Kingdom and Denmark are determined to make changes in the C standard at the
  25754. ISO level.
  25755. A meeting of the ISO C committee WG14 will be held in London in late May or
  25756. early June 1990 to commence work on two "normative addenda." These were
  25757. approved by the parent committee, SC22, at a recent meeting. One addendum is
  25758. an attempt by the British to make the language of the standard more precise.
  25759. The other is expected to add machinery for writing C source files more
  25760. readably in European character sets.
  25761. Once these normative addenda are developed and approved by WG14, they must
  25762. follow the same approval path through ISO as the standard developed by X3J11.
  25763. It remains to be seen whether the DIS will be held up pending approval of the
  25764. addenda. It also remains to be seen how much support exists within ISO for
  25765. amending the ANSI C standard.
  25766.  
  25767.  
  25768.  
  25769.  
  25770.  
  25771.  
  25772.  
  25773.  
  25774.  
  25775.  
  25776.  
  25777.  
  25778.  
  25779.  
  25780.  
  25781.  
  25782.  
  25783.  
  25784.  
  25785.  
  25786.  
  25787.  
  25788.  
  25789.  
  25790.  
  25791.  
  25792.  
  25793.  
  25794.  
  25795.  
  25796.  
  25797.  
  25798.  
  25799.  
  25800.  
  25801.  
  25802.  
  25803.  
  25804.  
  25805.  
  25806.  
  25807.  
  25808.  
  25809.  
  25810.  
  25811.  
  25812.  
  25813.  
  25814.  
  25815.  
  25816.  
  25817.  
  25818.  
  25819.  
  25820.  
  25821.  
  25822.  
  25823.  
  25824.  
  25825.  
  25826.  
  25827.  
  25828.  
  25829.  
  25830.  
  25831.  
  25832.  
  25833.  
  25834.  
  25835.  
  25836.  
  25837.  
  25838. Dr. C's Pointers(R)
  25839.  
  25840.  
  25841. Error Handling In C
  25842.  
  25843.  
  25844.  
  25845.  
  25846. Rex Jaeschke
  25847.  
  25848.  
  25849. Rex Jaeschke is an independent computer consultant, author and seminar leader.
  25850. He participates in both ANSI and ISO C Standards meetings and is the editor of
  25851. The Journal of C Language Translation, a quarterly publication aimed at
  25852. implementers of C language translation tools. Readers are encouraged to submit
  25853. column topics and suggestions to Rex at 2051 Swans Neck Way, Reston, VA, 22091
  25854. or via UUCP at uunet!aussie!rex.
  25855.  
  25856.  
  25857. Handling errors in programs is easy. You just don't make any! Well, it's not
  25858. quite that simple since every now and then your programs must deal with input
  25859. provided by a human, and humans make mistakes. (Who was it that said
  25860. "Computing would be real fun if it wasn't for users."?)
  25861. Certainty, it is possible to validate data before attempting an operation but
  25862. it's also common to assume that the routine receives valid data, and design
  25863. the routine to recover if faulty data causes a process to fail. That is, don't
  25864. pay the price of validation every time, only when invalid data is detected.
  25865. However, this approach can break down, particularly if it is impossible,
  25866. difficult, or expensive to recover from certain errors. And the earlier you
  25867. trap bad data, the more information you will have about its origins and what
  25868. to do next.
  25869.  
  25870.  
  25871. Approaches To Error Handling
  25872.  
  25873.  
  25874. Unlike other mainstream languages, most of the things that can fail in a C
  25875. program are library functions. Since C has no I/O statements, there are no
  25876. equivalents to END= and ERR= in FORTRAN's READ and WRITE statements. There is
  25877. also no equivalent to BASIC's ON ERROR GOTO. About the only kind of errors
  25878. that can be generated in the C language itself are things like arithmetic
  25879. over- and underflow, memory access violations (either by attempting to
  25880. dereference a pointer not pointing to an object or function or by a pointer
  25881. cast to an unaligned type), and stack overflow. All of these are design issues
  25882. and will not be discussed here.
  25883. Since much of the "real" work in C is done via functions, any error
  25884. information must be communicated between the function detecting the error and
  25885. that function's caller. This is typically done either by returning an error
  25886. indicator value or by initializing an error variable passed in by address, or
  25887. by a combination of both. For example:
  25888. status1 = f(arg);
  25889. if (status1 != 0)
  25890. /* handle error */
  25891. Here, the function returns zero on success and a specific error value on
  25892. failure. In the next case:
  25893. status2 = g(arg, &errorcode);
  25894. if (status2 == ERROR)
  25895. /* handle error */
  25896. the function reserves one return value only to indicate an error. The variable
  25897. errorcode (passed in by address) contains the actual reason if ERROR is
  25898. returned.
  25899. Unfortunately, none of C's standard library functions uses either of these.
  25900. (Well certainly not the second approach anyway. You could argue that malloc
  25901. and friends use the first approach since the only "real" reason they fail is
  25902. not that enough memory is available, regardless of what they were attempting
  25903. to do.)
  25904. C has its own approach; inter-function error communication is done via a
  25905. global variable, an approach that most structured programmers are strongly
  25906. warned against for a number of very good reasons. However, that's the way it
  25907. is so I won't philosophize about it here.
  25908.  
  25909.  
  25910. errno To The Rescue
  25911.  
  25912.  
  25913. Of course, the global keeper of the error number is our dear friend errno.
  25914. Historically, errno has been a global int in every program we've written
  25915. whether we have used it or not. It's really been like a reserved word in the
  25916. namespace of external identifiers. And since one of ANSI's jobs is to
  25917. consolidate existing practice, errno survived the ANSI C standardization
  25918. process pretty much intact.
  25919. To help get you into the spirit of things, here's an example of using errno
  25920. (Listing 1).
  25921. It is the programmer's responsibility to clear errno (a zero value means "no
  25922. error") each time before calling a function that may set it. No library
  25923. function is required to clear errno explicitly. You must also test errno or
  25924. store its value for later testing, immediately after the library function in
  25925. question returns. If you do not, any other library routine (or user-written
  25926. routine for that matter) might overwrite errno in the meantime. That is, just
  25927. because a library function is not documented as setting errno, doesn't mean
  25928. that it doesn't use it for a scratch variable. Messy, but that's the case.
  25929. In the example above, the first occurrence of errno = 0 is unnecessary since
  25930. at program startup errno is supposed to be cleared.
  25931.  
  25932.  
  25933. ANSI C And errno
  25934.  
  25935.  
  25936. The proposed ANSI C Standard pins down a number of things regarding errno. The
  25937. header errno.h was invented as a home for the definition of errno itself and
  25938. various macros of the form E* that relate to reporting error conditions. errno
  25939. is allowed to be either a global int or macro that expands to a modifiable
  25940. lvalue having type int. That is, it could be a macro that expands to something
  25941. to like *_ _errno().
  25942. Only two error value macros are defined by ANSI C: EDOM for domain errors and
  25943. ERANGE for range errors. However, an implementer is permitted to provide their
  25944. own E* value macros in this header.
  25945. The library functions that are documented as setting errno are: acos, asin,
  25946. cosh, exp, fgetpos, fsetpos, ftell, ldexp, log, log10, perror, pow, signal,
  25947. sinh, strtod, strtol, and strtoul. Note that fopen (and most other I/O
  25948. functions) are not included. As such, you cannot portably recover from a file
  25949. open failure (which is not surprising since there can be many system-specific
  25950. reasons for such an error).
  25951. The library functions perror and strerror can be used to produce formatted
  25952. messages corresponding to errno's value. However, the commonly implemented
  25953. table of messages, sys_list, and its associated machinery are not part of
  25954. Standard C.
  25955.  
  25956.  
  25957. An Error Handling Envelope
  25958.  
  25959.  
  25960. Rather than explicitly clear and test errno all the time, it is much more
  25961. elegant to have an error handling interface inserted between your code and
  25962. that in the library. Unfortunately, the standard library uses two different
  25963. ways to return an error a negative int value or a NULL pointer value. You may
  25964. have to have two interfaces, one to handle each.
  25965.  
  25966. Calling an extra function for each math library operation, for example, is an
  25967. added cost but so too is including the explicit error checking in each place.
  25968. It's the old speed versus code size tradeoff.
  25969. Listing 2 uses the setjmp/longjmp library mechanism to implement recovery from
  25970. attempts to take the square root of a negative number.
  25971. One problem here is the need to explicitly pass the setjmp context into mysqrt
  25972. -- it doesn't really look like a call to sqrt. You could hide this behind a
  25973. macro:
  25974. #define sqrt(d) sqrt((d), context)
  25975. but you would still need to define context yourself. Since ANSI C permits a
  25976. macro to expand to its own name without recursive death, all existing calls to
  25977. sqrt could be redirected in this manner with intermediate error checking being
  25978. added at the cost of recompilation in the presence of this macro. Perhaps a
  25979. cleaner approach is to make context a global so it never need be passed in. A
  25980. word of caution about redefining sqrt though. ANSI C effectively reserves the
  25981. names of all standard library functions and if you invent something of your
  25982. own with the same name, the behavior is undefined. However, for a given
  25983. implementation the macro approach may work.
  25984.  
  25985.  
  25986. The matherr Concept
  25987.  
  25988.  
  25989. Many systems provide a cleaner way to trap (and also recover from) certain
  25990. kinds of library errors. The idea originated with UNIX systems but has been
  25991. widely emulated. It involves a function called matherr. Each library routine
  25992. that can detect certain errors calls another library routine, matherr. Now
  25993. this default version of matherr may do nothing or it may simply write an error
  25994. message to stderr. By writing your own version of matherr and linking to it
  25995. instead of the library version, you can take control when one of the trapable
  25996. errors occurs. Listing 3 shows a primitive version of matherr. In reality you
  25997. would probably try to recover from the error.
  25998. When Listing 3 is linked with the first example above, the following output is
  25999. produced.
  26000. #1 OK
  26001. Function sqrt failed with error type DOMAIN
  26002. #2 OK
  26003. The reason the second call to sqrt does not show errno set is that matherr
  26004. returned a non-zero value, indicating that the normal reporting of the error
  26005. condition should be bypassed (presumably because the error has been "fixed" in
  26006. the userwritten matherr). With matherr you can bypass or follow the default
  26007. error handling rules and to a certain extent you can recover from errors and
  26008. substitute a value that should be returned by the math function instead.
  26009. The exception structure has several other members too and the type member
  26010. values are usually macros or enumeration constants defined in math.h along
  26011. with the structure template. Check your library manual for more details.
  26012. Note that matherr is not included in ANSI C.
  26013.  
  26014.  
  26015. Numerical C Extensions Group
  26016.  
  26017.  
  26018. This group (abbreviated as NCEG) was formed by me early in 1989. Its purpose
  26019. is to publish a technical report on directions for adding extensions to
  26020. Standard C, to support such things as complex arithmetic, IEEE floating-point,
  26021. vector and parallel operations, and variable dimensioned arrays.
  26022. The IEEE floating-point standards deal with a number of interesting things
  26023. (such as +/-infinity and not-a-number (NaN)) that need to be supported (and
  26024. taken advantage of) in modern C compilers. According to leading IEEE numerical
  26025. C implementers, errno gets in their way. Likewise for vendors of C compilers
  26026. doing parallel operations. As such, errno might well have to be ignored in
  26027. some implementations, simply for the sake of functionality and/or performance.
  26028. As I write this (mid-December 1989), the ANSI C Standards Committee X3J11 is
  26029. receiving a letter ballot asking members to admit NCEG as a full working group
  26030. (tentatively called X3J11.1) within ANSI C. The results of this ballot were 22
  26031. for and one against, and will be forwarded to SPARC for their consideration.
  26032. Contact me for further information on NCEG.
  26033.  
  26034. Listing 1
  26035. #include <stdio.h>
  26036. #include <errno.h>
  26037. #include <math.h>
  26038.  
  26039. main()
  26040. {
  26041. double d;
  26042.  
  26043. errno = 0;
  26044. d = sqrt(10);
  26045. if (errno == EDOM)
  26046. printf("#1 domain error\n");
  26047. else
  26048. printf("#1 OK\n");
  26049.  
  26050. errno = 0;
  26051. d = sqrt(-10);
  26052. if (errno == EDOM)
  26053. printf("#2 domain error\n");
  26054. else
  26055. printf("#2 OK\n");
  26056.  
  26057. }
  26058.  
  26059. #1 OK
  26060. #2 domain error
  26061.  
  26062.  
  26063. Listing 2
  26064. #include <stdio.h>
  26065.  
  26066. #include <setjmp.h>
  26067.  
  26068. main()
  26069. {
  26070.  
  26071. double value, result;
  26072. jmp_buf context;
  26073. double mysqrt(double value, jmp_buf context);
  26074.  
  26075. while (1) {
  26076. if (setjmp(context) != 0)
  26077. printf("Value is out of the domain for sqrt\n");
  26078.  
  26079. printf("Enter fp value: ");
  26080. scanf("#lf", &value);
  26081. if (value == -1.0)
  26082. return;
  26083. result = mysqrt(value, context);
  26084. printf("sqrt(%f) = %f\n", value, result);
  26085. }
  26086. }
  26087.  
  26088. #include <errno.h>
  26089. #include <math.h>
  26090.  
  26091. double mysqrt(double value, jmp_buf context)
  26092. {
  26093. double d;
  26094.  
  26095. errno = 0;
  26096. d = sqrt(value);
  26097. if (errno == EDOM)
  26098. longjmp(context, 1);
  26099. else
  26100. return (d);
  26101. }
  26102.  
  26103. Enter fp value: 1.234
  26104. sqrt(1.234000) = 1.110856
  26105. Enter fp value: 12345
  26106. sqrt(12345.000000) = 111.108056
  26107. Enter fp value: -0.000000
  26108. sqrt(-0.000000) = -0.000000
  26109. Enter fp value: -0.0000001
  26110. Value is out of the domain for sqrt
  26111. Enter fp value: -5
  26112. Value is out of the domain for sqrt
  26113.  
  26114.  
  26115. Listing 3
  26116. #include <math.h> /* get struct */
  26117.  
  26118. int matherr(struct exception *pe)
  26119. {
  26120. int retval = 1; /* assume we'll recover */
  26121.  
  26122. printf("Function %s failed with error type ", pe->name);
  26123. if (pe->type == DOMAIN)
  26124. printf("DOMAIN\n");
  26125.  
  26126. else if (pe->type == SING)
  26127. printf("SING\n");
  26128. else if (pe->type == OVERFLOW)
  26129. printf("OVERFLOW\n");
  26130. else if (pe->type == UNDERFLOW)
  26131. printf("UNDERFLOW\n");
  26132. else if (pe->type == TLOSS)
  26133. printf("TLOSS\n");
  26134. else {
  26135. printf("UNKNOWN\n");
  26136. retval = 0; /* can't handle here */
  26137. }
  26138.  
  26139. return retval;
  26140. }
  26141.  
  26142.  
  26143.  
  26144.  
  26145.  
  26146.  
  26147.  
  26148.  
  26149.  
  26150.  
  26151.  
  26152.  
  26153.  
  26154.  
  26155.  
  26156.  
  26157.  
  26158.  
  26159.  
  26160.  
  26161.  
  26162.  
  26163.  
  26164.  
  26165.  
  26166.  
  26167.  
  26168.  
  26169.  
  26170.  
  26171.  
  26172.  
  26173.  
  26174.  
  26175.  
  26176.  
  26177.  
  26178.  
  26179.  
  26180.  
  26181.  
  26182.  
  26183.  
  26184.  
  26185.  
  26186.  
  26187.  
  26188.  
  26189. Questions & Answers
  26190.  
  26191.  
  26192. More On Passing Arrays And Precedence Rules
  26193.  
  26194.  
  26195.  
  26196.  
  26197. Ken Pugh
  26198.  
  26199.  
  26200. Kenneth Pugh, a principal in Pugh-Killeen Associates, teaches C language
  26201. courses for corporations. He is the author of C Language for Programmers and
  26202. All On C, and is a member on the ANSI C committee. He also does custom C
  26203. programming for communications, graphics, and image databases. His address is
  26204. 4201 University Dr., Suite 102, Durham, NC 27707.
  26205.  
  26206.  
  26207. You may fax questions for Ken to (919) 493-4390. When you hear the answering
  26208. message, press the * button on your telephone. Ken also receives email at
  26209. kpugh@dukeac.ac.duke.edu (Internet) or dukeac!kpugh (UUCP).
  26210.  
  26211.  
  26212.  
  26213.  
  26214. Announcing The Great Name/Obscure Code Contest
  26215.  
  26216.  
  26217. Based on a reader's response later on in this column, it appears reasonable to
  26218. launch a new contest.
  26219. Send examples of the worst names or abbreviations that you have seen in other
  26220. people's programs (or even your own). Include both the name and a description
  26221. of what it is supposed to represent. The best (or worst) examples will be
  26222. published here, with credit for your submission. The name of the programmer
  26223. who actually wrote the code in which the name is used will not be mentioned
  26224. without his/her express permission.
  26225. Q
  26226. When we met at the Triangle C Users' Group meeting, you invited questions at
  26227. any difficulty level on 'C'. So, here is one:
  26228. I try to pass a char array to a function. In the function, I use sizeof to get
  26229. the array's allocated size. It doesn't work. The function is returning the
  26230. sizeof passed_array as if it were a pointer, two bytes long. It must be a
  26231. pointer with the location of the beginning of real_array in it. Right?
  26232. I asked our instructor about this, and he said the code in Listing 1 would
  26233. work. At least, that's how I understood what he said. It produces the results
  26234. shown below it when compiled with Power C from MIX or Instant C 3.0 from
  26235. Rational Systems, and fails with these compiler messages under Turbo-C 1.5:
  26236. (marker between char and passed_array[]; in
  26237. getarray()'s formal parameter list)
  26238. " Error 10:Type mismatch in redeclaration of
  26239. 'getarray' "
  26240. (then, at the end of function getarray(), it complains:)
  26241. " Warning 13:Parameter
  26242. 'passed_array' is never used in
  26243. function "
  26244. How can I get an array's allocated size within a function it has been passed
  26245. to?
  26246. What is bothering Turbo-C, and why don't the other compilers complain
  26247. similarly?
  26248. If an array's name is really a pointer to the array's beginning, why doesn't
  26249. sizeof(real_array) also return a 2 when called in main()? (Not that I WANT it
  26250. to ... :-)
  26251. Glenn Jordan
  26252. RTP, NC
  26253. A
  26254. The declaration of an array in a function as a local variable actually sets
  26255. aside storage for the array. In this sense the sizeof(real_array) is 20,
  26256. because that is how much storage is set aside for it. The name of an array
  26257. (one declared as local variable) (or a static/external) represents the address
  26258. when passed to a function,
  26259. When you declare a parameter to be an array (e.g. passed_array), you are not
  26260. really declaring an array at all. You are really declaring that the parameter
  26261. is a pointer. You pass an address in the call (i.e. real_array), and the
  26262. function receives that address in passed_array.
  26263. The sizeof passed_array is the size of a pointer (two or four bytes, depending
  26264. on the memory model). Alternatively, you could have declared it as int
  26265. *passed_array;. For parameter declarations, both int *passed_array; and int
  26266. passed_array[]; are equivalent. The compiler interprets both as meaning that
  26267. the parameter is a pointer. Your instructor may not have mentioned that you
  26268. can reference an individual int with a pointer by using either:
  26269. passed_array[i]
  26270. or
  26271. *(passed_array + i)
  26272. The compiler treats both declarations identically.
  26273. Instead of using sizeof, you could pass both a pointer to the array and its
  26274. size in either bytes or in elements. Usually the element count is more useful
  26275. than the byte count:
  26276. function(real_array, 20);
  26277. .....
  26278.  
  26279. function(passed_array, size)
  26280. char passed_array[];
  26281. int size;
  26282. {
  26283.  
  26284. for (i = 0; i < size; i++)
  26285. {
  26286. ......
  26287. You can avoid passing the size by designating a unique value for the end of
  26288. the valid elements in the array just as strings (character arrays) are
  26289. terminated with the NUL (zero or all bits off) character. Remember, the
  26290. terminator must be some unique value that will never appear as a valid value
  26291. for the type you are manipulating.
  26292. Q
  26293. My instructor says that (ch)++ evaluates as increment by one type-size-length
  26294. the value found in address ch. No quarrel there.
  26295. Being curious, I asked him how the expression would evaluate without the
  26296. parenthesis (). He said that since ++ and * are unary operators, and that *
  26297. had higher precedence than ++, the following was true:
  26298. *ch++ is evaluated identically to just ch++ without the *
  26299. and that in both cases, you would just increment the address ch by 1.
  26300. I disagreed (never disagree with your instructor). I said that if * had higher
  26301. precedence than ++, like he insisted, that
  26302. *ch++ should be exactly the same as (*ch)++
  26303. He said no, since ++ is a unary operator, it could only see the ch, not *ch,
  26304. even if * had already operated on ch. I told him I thought that was really
  26305. crazy, and he got upset...
  26306. Now, actually, as you experienced programmers know, * and ++ have the exact
  26307. same precedence, and are evaluated right-to-left when there is an
  26308. associativity question, as above. So, he was right, the parentheses are
  26309. required to make (*ch)++ increase the value held in address ch. But the stuff
  26310. about ++ acting only on the adjacent operand must be wrong, right ???? I mean,
  26311. wouldn't:
  26312. ++*ch do exactly the same as (*ch)++
  26313. He claimed that in the case, ++*ch, the ++ would not know what to do with the
  26314. operand *, while I claimed that *ch would already be evaluated to the
  26315. single-value contents of address ch when ++ attacked. He responded that he had
  26316. been programming in C for years, and knew what he was talking about.
  26317. Comments? Perhaps I am the one who is misunderstanding...
  26318. Glenn Jordan
  26319. RTP, NCA
  26320. A
  26321. You are correct in your interpretation. By associativity and precedence rules:
  26322. ch++ equals *(ch++)
  26323. Both use the address contained in ch as a pointer and then increment the
  26324. contents of ch using pointer arithmetic.
  26325. ++*ch equals ++(*ch)
  26326. These forms use the address in ch as a pointer and increment the contents at
  26327. that address.
  26328. *++ch equals *(++ch)
  26329. These forms increment the contents of ch using pointer arithmetic and then use
  26330. that new value as a pointer.
  26331. In order to post-increment the contents of a target location, you need to use
  26332. explicit parentheses to overcome the precedence, yielding:
  26333. (*ch)++
  26334. This combination uses the address in ch as a pointer and increments the
  26335. contents at that address.
  26336. For example, if we assume that doubles are eight bytes long, then incrementing
  26337. a pointer to a double increases that pointer by eight. The comments in Listing
  26338. 2 detail the behavior of various pointer/increment combinations.
  26339.  
  26340.  
  26341. Reader Responses:
  26342.  
  26343.  
  26344.  
  26345.  
  26346. Character Constants
  26347.  
  26348.  
  26349. This letter is in response to your discussion of character constants on pages
  26350. 113 and 114 of The C Users Journal, January 1990. I believe that your
  26351. discussion is flawed and that the Microsoft C and Quick C implementations do
  26352. not comply with the draft standard.
  26353. You say that the character Ã¨ (where e is replaced by the accented e, code 138
  26354. decimal) is not part of ASCII and so the compiler could do with it what it
  26355. wants. I refer you to the following items in the December 7, 1988 draft C
  26356. standard: Section 2.2.1, Page 11, Line 12:
  26357. Both the basic source and the basic execution character sets shall have at
  26358. least [emphasis added by RHG] the following members ...
  26359. Section 3.1.3.4, Page 29, Line 16: c-char:
  26360. any member of the source character set except the single quote ', backslash \
  26361. , or new-line character escape-sequence
  26362. Section 3.1.3.4, Page 30, Line 33:
  26363. If an integer constant contains a singlecharacter or escape sequence, its
  26364. value is the one that results when an object with type char whose value is
  26365. that of the single character or escape sequence is converted to type int.
  26366. Given that Microsoft's C and Quick C compilers accept character 138 without
  26367. any diagnostic, I think it is safe to assume that they consider the character
  26368. to be part of the source character set. Therefore, the literal is indeed a
  26369. legal character constant and so should be treated like (int) (char) Ã¨ which
  26370. has the value -118 if characters are treated as signed. Hence, your example
  26371. demonstrates a bug in the compilers, not an implementation dependency.
  26372. I have copied this note to the postmaster at Microsoft in the hope that he
  26373. will forward it to the appropriate person in the C compilers development group
  26374. for comment. You may also use this letter in a future column if you see fit.
  26375. Richard H. Gumpertz
  26376. Leawood, Kansas
  26377. A
  26378. You (Mr. Gumpertz) are correct, I believe. The sample program in the article
  26379. (pg. 114) shows a bug that has actually been in MS C since well before the
  26380. ANSI standard (I duplicated it all the way back to C v4.0).
  26381. We do appreciate you bringing this to our attention. This bug will be fixed in
  26382. our upcoming version of MS C.
  26383. Thanks.
  26384. Dave Weil
  26385. Group Development Mgr.,
  26386. System Languages
  26387. Microsoft Corp.
  26388.  
  26389. Thank you. I stand corrected on this technicality. You are correct if a
  26390. character representation is accepted in a character constant, then it should
  26391. act according to the rules for characters. Non-ASCII characters are not in the
  26392. ANSI standard source character set that must be supported by a conforming
  26393. compiler.
  26394. I strongly urge against using non-ASCII characters as character values. You
  26395. can always use a #define in their place. Not only do you avoid the inherent
  26396. non-portability of such a program, you also avoid word processing problems.
  26397. For example, I was porting a program somebody had written with a word
  26398. processor that accepted non-ASCII characters. My word processor does not
  26399. accept them. It uses the high order bit as an internal designation of the end
  26400. of a word. It read the program, but the non-ASCII characters appeared as the
  26401. ASCII value with the high-order bit off.
  26402. If you do use characters with the high-order bit on, then you could declare
  26403. the variables that use them as unsigned chars preventing sign extension when
  26404. the char is expanded to an integer. --KP
  26405.  
  26406.  
  26407. Naming Conventions And Indentation
  26408.  
  26409.  
  26410. Everyone knows the "CMP" stands for corrugated metal pipe, not compare or
  26411. compute. --Marcus Russell, West Berlin, NJ
  26412. This comment refers to a previous response I had given to a question regarding
  26413. naming conventions. I suggested that one should adopt some standard
  26414. abbreviations, if one did not spell out names in full.
  26415. Comments from other people have suggested that there is a widespread
  26416. distinction between the vowel droppers and the first few letter users.
  26417. "compute" could be abbreviated as "cmpt" or "comp", depending on your
  26418. preference. In my earlier days I used to use "cmp" as a shortening of "cmpt".
  26419. This always caused conflict when "compare" got shortened to "cmpr" and then to
  26420. "cmp" also.
  26421. I find it interesting reading listings in this and other magazines. I believe
  26422. that a program should be almost as readable as a book. Using fully spelled out
  26423. variable names contributes as much as any other factor to easier understanding
  26424. of a program.
  26425. This leads me on to another topic of readability -- the great brace debate.
  26426. Brace alignment of compound statements seems to be a topic that provokes a
  26427. variety of opinions. I think that, like taste in art, each person's view is
  26428. different and sufficient justification can be developed to support any
  26429. particular stand.
  26430. A recent article in the C Gazette had some words to say about indentation
  26431. styles. I recommend the magazine for those who like reading C code in order to
  26432. learn about it. There are a lot of source listings in that magazine.
  26433. There are many possibilities for brace alignment. If braces are placed on
  26434. lines by themselves, then either or both can be aligned with the enclosed
  26435. statements or one tab stop to the left of the statements. Alternatively, the
  26436. opening brace may be on the same line as the controlling statement. The
  26437. closing brace might be on the same line of the last enclosed statement. This
  26438. yields a number of possibilities. In Chapter 14 of All on C, I listed four
  26439. common formats. Here are those with a few more. I've left off several
  26440. variations which appear rather ugly and of no use.
  26441. Braces on separate lines and aligned with enclosed statements.
  26442. if (x)
  26443. {
  26444. ...
  26445. }
  26446. Braces on separate lines and aligned with controlling statement.
  26447. if (x)
  26448. {
  26449. ...
  26450. }
  26451. Opening brace on same line as controlling statement, closing brace aligned
  26452. with enclosed statements [my preference -- rlw].
  26453. if (x) {
  26454. ...
  26455. }
  26456. Opening brace on same line as controlling statement, closing brace aligned
  26457. with controlling statement (Kernighan and Ritchie style).
  26458. if (x) {
  26459. ...
  26460. };
  26461. Opening brace on same line as controlling statement and closing brace on same
  26462. line as last enclosed statement.
  26463. if (x) {
  26464. ...}
  26465. Luckily there are "pretty-print" programs that you can use to alter the style
  26466. of the indentations, for programs you have written or that you have received
  26467. and are trying to alter or maintain. However it's usually wise to adopt one
  26468. style and use it faithfully. I originally adopted the style:
  26469. if (x) {
  26470. ...
  26471. }
  26472. The initial choice was arbitrary. Later I reviewed my usage and found a few
  26473. compelling reasons to switch to:
  26474. if (x)
  26475. {
  26476. ...
  26477. }
  26478. This appearance is more consistent with the use of indentation for
  26479. non-compound statements. Those looks like:
  26480. if (x)
  26481. statement
  26482. It also makes it easy to match up braces. The other styles which have
  26483. unaligned braces make it more difficult to do this.
  26484. Those of you who submit code for this column will find that I have reformatted
  26485. the listing for the sake of consistency within the column. --KP
  26486.  
  26487. Listing 1
  26488. #include <stdio.h>
  26489.  
  26490. main()
  26491. {
  26492. char real_array[20];
  26493. printf("Realarray can hold %d chars.", sizeof(real_array));
  26494.  
  26495. getarray(real_array);
  26496. }
  26497.  
  26498. void getarray(passed_array)
  26499. char passed_array[];
  26500. {
  26501. printf("\nPassedarray can hold %d chars.",
  26502. sizeof(passed_array));
  26503. }
  26504.  
  26505. ---------------------------------------------------------------
  26506. Results : (under Power C and Instant C)
  26507.  
  26508. Realarray can hold 20 chars.
  26509. Passedarray can hold 2 chars.
  26510.  
  26511. ---------------------------------------------------------------
  26512.  
  26513.  
  26514. Listing 2
  26515. double d[5] = {1., 2., 3., 4., 5.};
  26516. /* Assume this starts address 100 */
  26517. double *ch;
  26518. double e;
  26519.  
  26520. ch = d; /* 100 placed into ch */
  26521.  
  26522. *(ch++) = 5.; /* 5. placed in d[0]
  26523. ch incremented to 108.
  26524.  
  26525. ++(*ch); /* Contents of d[1] (at address 108)
  26526. incremented by 1, to 3. */
  26527.  
  26528. *(++ch) = 7.; /* ch incremented to 116
  26529. 7. placed in d[2] (at address 116) */
  26530.  
  26531. (*ch)++ /* The 7. at d[2] is incremented to 8. */
  26532.  
  26533. e = ++(*ch); /* The 8. at d[2] is incremented to 9.
  26534. 9. is placed in e */
  26535.  
  26536. e = (*ch)++; /* The 9. at d[2] is placed in e
  26537. d[2] is incremented to 10. */
  26538.  
  26539.  
  26540.  
  26541.  
  26542.  
  26543.  
  26544.  
  26545.  
  26546.  
  26547.  
  26548.  
  26549.  
  26550.  
  26551.  
  26552.  
  26553.  
  26554.  
  26555.  
  26556.  
  26557.  
  26558. How To Do It... In C
  26559.  
  26560.  
  26561. Practical Schedulers For Real-Time Applications
  26562.  
  26563.  
  26564.  
  26565.  
  26566. Robert Ward
  26567.  
  26568.  
  26569. Robert Ward is president of R&D Publications and author of Debugging C, an
  26570. introduction to scientific debugging. He has done consulting work in software
  26571. engineering and data communications and holds an M.S.C.S. from the University
  26572. of Kansas.
  26573.  
  26574.  
  26575.  
  26576.  
  26577. What Is Real Time?
  26578.  
  26579.  
  26580. Real-time is not a synonym for "real-fast". Contrary to popular opinion,
  26581. making everything "real fast" won't necessarily make a real-time program work
  26582. correctly. A much better synonym is "on time" since, in a real-time program,
  26583. certain events must happen at a specific time.
  26584. Making input and output events happen "on time" is pretty straightforward if
  26585. you have only one I/O path to worry about. But real-time programs, especially
  26586. embedded real-time systems, are often also multi-tasking programs. Most
  26587. real-world, real-time programs are expected to simulate several pieces of
  26588. simultaneously operating hardware.
  26589. When analyzing a project that is both multi-tasking and real-time, the
  26590. designer must recognize that some tasks are less urgent than others. For each
  26591. real-time task, "on time" may have a different meaning, depending upon the
  26592. time constraints associated with that particular task. A continuously running
  26593. built-in self-test, for example, usually runs without any time constraints,
  26594. even if it is testing a real-time system. Some real-time events may need to
  26595. happen at a specific wall-time; others at a specific interval from some
  26596. external event. The real-time program must properly balance these varying
  26597. needs at every instant, under every imaginable combination of input
  26598. conditions.
  26599. This article will show how an appropriate general purpose scheduler can
  26600. significantly reduce the design complexity in such programs and also
  26601. significantly increase your confidence in the feasibility of the design even
  26602. before you write any significant amount of code.
  26603.  
  26604.  
  26605. What Is A Scheduler?
  26606.  
  26607.  
  26608. A scheduler is simply code that decides which task to perform next. Thus a
  26609. scheduler can be as simple as the loop in Listing 1. This "slop-cyclic"
  26610. scheduler cycles repeatedly through each task (cyclic) at a rate that may vary
  26611. depending upon the time required to execute each task (slop). A "rate-cyclic"
  26612. scheduler can be almost as simple, as shown in Listing 2. The rate cyclic
  26613. scheduler cycles through all the tasks at a constant rate of once per clock
  26614. tick.
  26615. If you are accustomed to writing real-time systems as one large loop with
  26616. input polling and capture code sprinkled throughout the system, it may seem
  26617. pretentious to describe Listing 1 as a "scheduler." After all, one could
  26618. argue, the execution sequence is, like the polling loop, just a hard-wired
  26619. loop -- the so-called scheduler just adds calling overhead.
  26620. Even this trivial scheduler, though, offers several important advantages.
  26621. First, all the scheduling information is in one place. If you must alter the
  26622. code (and the timing relationships between the pieces), you know just where to
  26623. look to make the necessary scheduling adjustments. For example, if after
  26624. writing the project, you found that task3() and task4() didn't execute as
  26625. rapidly as expected, causing task1() to "miss events", you might solve the
  26626. problem by making the change in Listing 3. Now task1() gets scheduled more
  26627. than once during the cycle. You can even drop one task into the "middle" of
  26628. another by breaking one task into several subparts (see task2a() and task2b()
  26629. in Listing 4).
  26630. With the scheduling code isolated in a single module, the designer also
  26631. reserves the option to completely change the scheduling mechanism. For
  26632. example, instead of splitting task2 into two parts (as in Listing 4), you
  26633. might obtain the same effect by installing a more sophisticated pre-emptive
  26634. scheduler like the one we'll develop later in this article.
  26635. A distinctly separate scheduler can also make development and testing much
  26636. easier. You might plan to use a simple loop like Listing 1 to perform initial
  26637. testing and characterization of your task code and then install a more
  26638. sophisticated scheduler for final test and production.
  26639.  
  26640.  
  26641. Priority & Pre-emption
  26642.  
  26643.  
  26644. In the trivial schedulers of Listing 1 - Listing 4, all tasks are of equal
  26645. importance and the order of task execution is statically determined by how the
  26646. code is written. This egalitarian approach forces the programmer to adjust for
  26647. differences among the tasks by adjusting the code, for example by making
  26648. multiple calls to task1() and by splitting task2() into two parts. If the
  26649. scheduler were more competent, we wouldn't need to make these coding
  26650. compromises. What we need is a scheduler that can recognize that some tasks
  26651. (task1() for example) are more important than others (have higher priority)
  26652. and that sometimes long tasks like task2() may need to be interrupted (be
  26653. pre-empted) so that some shorter, more important task can run "on-time". The
  26654. scheduler should not only recognize these differences, it should be able to
  26655. dynamically adjust the execution order to accommodate them.
  26656. Prioritized scheduling can be accomplished by augmenting the simple slop
  26657. cyclic scheduler so that it uses a different control structure driven by
  26658. several "ready" lists. Listing 5 presents the basic structure for a very
  26659. simple environment where each task is assigned to a different priority level
  26660. and each ready list consists of a separate flag in the structure ready.
  26661. The ready flags are set by an interrupt service routine that captures related
  26662. input, or by some other task (for example, task2a() would set task2b()'s flag,
  26663. thereby "scheduling" task2b()).
  26664. Listing 5 will schedule events dynamically based on their readiness, but it
  26665. still lets each task run to completion. The next level of scheduler
  26666. sophistication, pre-emption, complicates matters considerably, but is still
  26667. easy to implement once the calling conventions are understood.
  26668. The pre-emptive scheduler pre-supposes an environment where virtually all
  26669. events are serviced by interrupts. These interrupts create natural "break
  26670. points" at which other tasks can be pre-empted. (Actually, you can get the
  26671. same effect by sprinkling special calls, gotos and stack manipulations
  26672. throughout each task, but you won't want to maintain the result.) Each
  26673. interrupt service routine ends with a call to the scheduler. The scheduler
  26674. then examines all higher priority ready lists to see if some more urgent task
  26675. needs to run. If not, the scheduler simply returns, allowing the interrupted
  26676. task to continue. If there is a more urgent task waiting, the scheduler calls
  26677. it (see Listing 6).
  26678. This scheduler treats LEVEL1 as the highest level of priority. IDLE tests
  26679. greater than all other levels.
  26680. Listing 6 assumes several conventions and data structures not explicitly
  26681. shown. The functions push() and pop() manipulate a stack of current priority
  26682. levels. If you are willing to pass the current priority level as a parameter
  26683. to every task, and to accept responsibility for always calling the scheduler
  26684. with the current level's priority as a parameter, you can stack this
  26685. information implicitly as function parameters.
  26686. The function getnext() searches a linked ready list for actions more urgent
  26687. than the action just interrupted. If a more urgent task is found, getnext()
  26688. copies its descriptor from the ready list to the next structure. The task is
  26689. then invoked via a pointer to function.
  26690. The assignments marked /*lock*/ in Listing 6 must be executed with interrupts
  26691. disabled to avoid potential synchronization errors. Before using this code,
  26692. you must at least bracket these lines with code to disable and enable
  26693. interrupts. The scheduler must always be called with interrupts enabled --
  26694. otherwise it will provide only one level of pre-emption and may cause some
  26695. interrupts to be missed.
  26696. This implementation is very stack-intensive. Each interrupt can potentially
  26697. generate three stack frames for each interrupt (interrupt, interrupt's call to
  26698. scheduler, scheduler's call to task). In an environment where many interrupts
  26699. can arrive simultaneously, the stack may expand very rapidly.
  26700.  
  26701.  
  26702. Some Design Advice
  26703.  
  26704.  
  26705. A real-time design should begin with a careful analysis of the possible events
  26706. and the relationship among them. The goal is to decouple (with respect to
  26707. time) as many actions as possible. Decoupling will often greatly increase your
  26708. ability to service critical events, by allowing the great bulk of the
  26709. processing to occur in the time between critical events. This analysis should
  26710. identify the time-critical events (those that really must be done NOW), and
  26711. prioritize the other events according to their relative criticality.
  26712. Generally, actions subject to similar constraints should be made members of a
  26713. priority class and broken into execution units that are small relative to the
  26714. time-tolerance of the next, more urgent class.
  26715. Early in the design analysis, you should compute the probable CPU utilization.
  26716. If the tasks assigned to the system will consume more than 70 percent of the
  26717. CPU throughput, you should probably consider the design impractical and either
  26718. find faster algorithms for certain modules, add additional hardware, or run on
  26719. a faster CPU. In truly asynchronous environments, a processor utilization of
  26720. greater than 70 percent greatly increases the likelihood that one of your
  26721. ready queues will grow to an unmanageable length. You can make an exception to
  26722. the 70 percent rule if you can prove that your waiting lists will never grow
  26723. beyond some small fixed length.
  26724. The structure of the program will mirror the classification of events.
  26725. Critical events will be serviced by interrupt handlers, non-critical events
  26726. will be processed according to their priority by a general-purpose scheduler,
  26727. and queues will handle communication among the pieces. Since these systems are
  26728. almost always concurrent, it is imperative that the programmer be comfortable
  26729. with the issues of deadlock avoidance and shared resource management.
  26730. With careful analysis of events and adequate throughput, a simple cyclic
  26731. scheduler is often adequate. In some applications where some actions consume
  26732. very large amounts of time compared to the time-tolerance of higher-priority
  26733. tasks, it may be necessary to implement a pre-emptive scheduler.
  26734.  
  26735.  
  26736.  
  26737. A Case Study
  26738.  
  26739.  
  26740. Suppose you are to build a real-time system with four major functions:
  26741. Process Control. This function consists of monitoring a sensor on a production
  26742. line and adjusting a control output to keep the process within acceptable
  26743. limits. The sensor is to be sampled every 100 ms (±1 ms) and necessary output
  26744. corrections must be made within 100 ms. Statistical quality control methods
  26745. are to be used to decide if the sample input represents an unacceptable
  26746. deviation. Test programs have shown these calculations to require 7 ms. A
  26747. programmable internal timer is to be used to control the sampling interval.
  26748. The timer can generate interrupts.
  26749. Manual Override. The system should accept human specifications for the control
  26750. output from a keyboard. This keyboard debounces inputs, but once the character
  26751. is validated, it must be read within 70 Âµs. The keyboard's "character ready"
  26752. status line is connected to the CPU's interrupt line.
  26753. Time-of-day Clock. An hour and minute time-of-day display. Presumably it will
  26754. take its timing from the internal timer. The display is mechanical, each digit
  26755. on a "flip board", driven by a stepper motor. The motor must be pulsed through
  26756. 60 steps to change a digit. Each step takes 10 ms, to complete. Pulses are
  26757. directed to the appropriate digit position by a multiplexor, thus the position
  26758. must be selected and then the pulses sent to change a digit.
  26759. Synchronous Communications Support. The system functions as a "repeater" in a
  26760. communications network. Supporting hardware captures data a block at a time
  26761. and requests your system to perform a crc-16 on the data. If the data is
  26762. correct, your system must initiate a write 75 ms (± 100 Âµs) after the block
  26763. was marked received. A failure to meet this requirement will result in the
  26764. subordinate hardware missing a polling cycle and loss of the block. The
  26765. subordinate hardware's "block ready" shares the interrupt line with the
  26766. keyboard. The block ready signal remains set until reset by the CPU.
  26767. Table 1 summarizes these specifications and adds estimates for each task's
  26768. execution time. These execution time estimates can be based on expected code
  26769. size for each task, on prior experience with similar problems, on padded
  26770. measurements of execution speeds for certain critical inner loops, or on
  26771. measurements taken from "prototype" implementations for each task (perhaps
  26772. written in a high-level language). Since adding a scheduler to the design
  26773. makes each task a piece of stand-alone code, time spent coding each task for
  26774. these measurements isn't just wasted. Most of your characterization code can
  26775. be used in the finished design.
  26776.  
  26777.  
  26778. Critical Operations
  26779.  
  26780.  
  26781. Capturing a keystroke and (because it shares an interrupt with the keyboard)
  26782. capturing a block ready indication are the only critical tasks in the system.
  26783. These will be processed by an interrupt handler with interrupts turned out
  26784. throughout the service.
  26785.  
  26786.  
  26787. Priorities
  26788.  
  26789.  
  26790. Level 1. Capturing a data sample, capturing a clock tick and initiating a
  26791. block write are "nearly" critical. It also makes sense for all three to be
  26792. handled in the same interrupt service routine. Since they have lower priority
  26793. than the critical events, interrupts will be enabled during as much of the
  26794. service routine as possible. Thus the data sampling interrupt routine could be
  26795. interrupted during its execution. We'll assume that the first 15 Âµs of this
  26796. process can't be interrupted. Note that this priority level isn't recognized
  26797. in the scheduler because it is fully processed in the interrupt handler -- I
  26798. just wanted to show that even interrupt handlers differ in their urgency.
  26799. Level 2. The block checksum and sample analysis will be grouped at the next
  26800. level of priority. The checksum has been broken into several short parts so
  26801. that it can't "block out" the input analysis for more than a few milliseconds.
  26802. Each part will schedule its successor after it completes. This ensures the
  26803. sample analysis will be able to "sneak" in between two parts (the sample
  26804. analysis is scheduled by an interrupt routine, possibly while the checksum is
  26805. executing).
  26806. Level 3. All clock control and keystroke parsing will be performed at level 3
  26807. (or background) activities. Notice that even though none of these events
  26808. consumes more than 10 ms, if three such events were allowed to be interspersed
  26809. with level 2 events, the block check would miss its "output deadline."
  26810. Level 4. This level is reserved for pure "waiting" activities, such as waiting
  26811. for the clock stepping motor to time-out.
  26812. Listing 7 presents the structure of the entire application in a C-like psuedo
  26813. code. This code would use the scheduler of Listing 6.
  26814.  
  26815.  
  26816. Throughput Requirements
  26817.  
  26818.  
  26819. Processor utilization is computed by combining the frequency estimates and
  26820. time consumed estimates from Table 1. Table 2 shows that this design falls
  26821. well within the 70 percent rule, and should probably be feasible.
  26822. Total utilization isn't the only prerequisite to feasibility, however. The
  26823. design must also meet the response time restrictions. Response time
  26824. performance is evaluated by calculating the worst time performance for each
  26825. event. Worst case analysis should always include the possibility that the
  26826. program has just responded to some interrupt or that multiple copies of the
  26827. analyzed interrupt arrive at the closest interval possible. Table 3 analyzes
  26828. the design's latency when performing a process control cycle.
  26829.  
  26830.  
  26831. Additional Ideas
  26832.  
  26833.  
  26834. When a variety of events happen at non-harmonic intervals, consider
  26835. implementing a timer scheduling queue. Events can specify the timing of other
  26836. events by putting a timer programming request in a special queue.
  26837. If your system has multiple interrupting events and no vectored interrupts,
  26838. restrict the interrupt handler to just capturing the interrupt and queueing
  26839. it. The highest priority task then examines the information in this queue and
  26840. schedules other work.
  26841. To make certain two tasks of equal priority get fair scheduling, partition
  26842. them into pieces (as with the checksum above) and let each piece upon
  26843. completion schedule its successor. This scheme will allow the shorter tasks to
  26844. be scheduled. This trick can often eliminate the need for a pre-emptive
  26845. scheduler.
  26846.  
  26847.  
  26848. Conclusion
  26849.  
  26850.  
  26851. An appropriate scheduler can greatly simplify real-time designs by allowing
  26852. the individual task modules to remain ignorant of their interaction with other
  26853. real-time tasks. A distinct scheduler also simplifies debugging and
  26854. performance analysis. If you aren't comfortable with the concurrency issues
  26855. implicit with handling the ready queues and other shared resources in the
  26856. dynamic schedulers, you can still use the static versions and preserve the
  26857. option of incorporating a more complex scheduler when the project eventually
  26858. demands it.
  26859. Little schedulers like those developed here are usually all the real-time
  26860. support a controller needs. They offer distinct advantages over a commercial
  26861. real-time kernel: the scheduler is smaller, simpler to understand, comes
  26862. complete with source code, and is much less expensive.
  26863. Table 1
  26864. Events Trigger time latitude freq
  26865.  consumed
  26866.  
  26867. capture sample timer interrupt 70 us +-500 us 10/s
  26868. analyze sample input available 7 ms -0,+92 ms 10/s
  26869. output correction analysis complete 35 us n/a 10/s
  26870.  
  26871. capture keystroke G.P. interrupt 35 us -0,+35us 5/s
  26872. parse input keystroke stored 1 ms 500 ms 1/s
  26873.  
  26874.  
  26875. clock tick timer interrupt 15 us +-500 us 10/s
  26876. minute change clock tick 150 us ? 1/60s
  26877. digit change minute change 50 us ? 4/60s
  26878. digit step digit change 10 ms ? 320/60s
  26879.  
  26880. capture block ready G.P. interrupt 35 us -0, +35us 7/s
  26881. check 1st q capture block 13 ms +- 3 ms 7/s
  26882. check 2nd q 1st q checked 13 ms +- 3 ms 7/s
  26883. check 3rd q 2nd q checked 13 ms +- 3 ms 7/s
  26884. check 4th q 3rd q checked 13 ms +- 3 ms 7/s
  26885. initiate block write 4th q checked 35 us +- 100us 7/s
  26886. Table 2
  26887. Events factors time used capacity used
  26888.  
  26889. capture sample 70 us * 10/s 700us/s .0007
  26890. analyze sample 7 ms * 10/s 70 ms/s .07
  26891. output correction 35 us * 10/s 350us/s .000350
  26892.  
  26893. capture keystroke 35 us * 5/s 175us/s .000175
  26894. parse input 1ms/s .001
  26895.  
  26896. clock tick 15 us * 10/s 150us/s .000150
  26897. minute change 150us * 1/60s 150us/60s .000003
  26898. digit change 50 us * 4/60s 200us/60s .000004
  26899. digit step 10 ms * 320/60s 53.3ms/s .053300
  26900.  
  26901. capture block ready 35us * 7/s 245us/s .000245
  26902. check 1st q 13ms * 7/s 91ms/s .091000
  26903. check 2nd q 13ms * 7/s 91ms/s .091000
  26904. check 3rd q 13ms * 7/s 91ms/s .091000
  26905. check 4th q 13ms * 7/s 91ms/s .091000
  26906. initiate block write
  26907.  Total Utilization .489927
  26908. Table 3
  26909. Start checksum 0
  26910. Receive GP interrupt 35 us
  26911. Receiver Timer interrupt 15 us
  26912. Receive second GP intr. 35 us
  26913.  complete timer intr 55 us
  26914.  complete chksum part 13 ms
  26915. Perform analysis 7 ms
  26916.  ----------------------------
  26917.  Total 20.14 ms
  26918.  
  26919. Listing 1
  26920. while (FOREVER) {
  26921. task1();
  26922. task2();
  26923. task3();
  26924. task4();
  26925. }
  26926.  
  26927.  
  26928. Listing 2
  26929. while (FOREVER) {
  26930. /* sleep until awakened
  26931. by clock interrupt */
  26932. sleep(clock_tick);
  26933.  
  26934. task1();
  26935. task2();
  26936. task3();
  26937. task4();
  26938. }
  26939.  
  26940.  
  26941. Listing 3
  26942. while (FOREVER) {
  26943. task1();
  26944. task2();
  26945. task1();
  26946. task3();
  26947. task1();
  26948. task4();
  26949. }
  26950.  
  26951.  
  26952. Listing 4
  26953. while (FOREVER) {
  26954. task1();
  26955. task2a();
  26956. task1();
  26957. task2b();
  26958. task1();
  26959. task3();
  26960. task1();
  26961. task4();
  26962. }
  26963.  
  26964.  
  26965. Listing 5
  26966. while (FOREVER) {
  26967. if (ready.task1) task1();
  26968. else if (ready.task2a) task2a();
  26969. else if (ready.task2b) task2b();
  26970. else if (ready.task4) task4();
  26971. else if (ready.task3) task3();
  26972. }
  26973.  
  26974.  
  26975. Listing 6
  26976. This scheduler treats LEVEL1 as the highest level of priority.
  26977. IDLE tests greater than all other levels.
  26978.  
  26979. struct actions {
  26980. int priority;
  26981. void (*action)();
  26982. char * arg;
  26983. struct actions *nxtptr;
  26984. } ready [MAX_WAIT], next;
  26985.  
  26986. main ()
  26987. {
  26988. ...
  26989. /* initiate interrupt handlers */
  26990. while (TRUE){
  26991. clevel = IDLE;
  26992. do_loop();
  26993.  
  26994. }
  26995. }
  26996.  
  26997. void do_loop()
  26998. {
  26999.  
  27000. scheduler:
  27001.  
  27002. if (clevel == LEVEL2) return;
  27003. else if ((clevel > LEVEL2) && (getnext(LEVEL2) != EMPTY)){
  27004. push (clevel);
  27005. clevel == LEVEL2; /*should be locked */
  27006. (*next.action)(next.arg);
  27007. clevel=pop(); /*lock*/
  27008. goto scheduler;
  27009. }
  27010. else if ((clevel > LEVEL3) && (getnext(LEVEL3) != EMPTY)){
  27011. push (clevel);
  27012. clevel == LEVEL3; /*lock*/
  27013. (*next.action)(next.arg);
  27014. clevel=pop(); /*lock*/
  27015. goto scheduler;
  27016. }
  27017. else return;
  27018. }
  27019.  
  27020.  
  27021. Listing 7
  27022. Psuedo code for interrupt handler:
  27023.  
  27024. On keyboard interrupt do {
  27025. save context
  27026. if keyboard has input, save with work request to level3 queue
  27027. if block is ready {
  27028. reset indicator
  27029. add check_1st_blk to level2 queue
  27030. }
  27031. restore context
  27032. enable interrupts
  27033. return from interrupt
  27034. }
  27035.  
  27036. On timer interrupt do {
  27037. save context
  27038. if write ok flag set{ /*done with interrupts off to avoid clash */
  27039. initiate write
  27040. clear flag
  27041. }
  27042. enable interrupts
  27043. capture sample, save with work request to level2 queue
  27044. step minute counter, on overflow {
  27045. reset counter
  27046. put minute change work request in level3 queue
  27047. }
  27048. return from interrupt
  27049. }
  27050.  
  27051. Psuedo code for tasks
  27052.  
  27053.  
  27054. Analyze sample {
  27055. perform statistical analysis
  27056. if out of bounds {
  27057. compute correction
  27058. output correction
  27059. }
  27060. return
  27061. }
  27062.  
  27063. Check Block Pt 1{
  27064. compute partial checksum
  27065. save result with pt2 work request in level2 queue
  27066. return
  27067. }
  27068.  
  27069. Check Block Pt 2{
  27070. continue checksum
  27071. save result with pt3 work request in level2 queue
  27072. return
  27073. }
  27074.  
  27075. Check Block pt3 {
  27076. continue checksum
  27077. save result with pt4 work request in level2 queue
  27078. return
  27079. }
  27080.  
  27081. Check Block pt4 {
  27082. complete checksum
  27083. if ok, set write ok flag
  27084. return
  27085. }
  27086.  
  27087. Parse input {
  27088. save input parameter in command line buffer.
  27089. If input keystroke is a terminal symbol{
  27090. parse buffer;
  27091. output manual correction;
  27092. clear buffer;
  27093. }
  27094. return;
  27095. }
  27096.  
  27097. Minute change{
  27098. increment minutes-ones
  27099. add work request for digit change to minutes-ones to level4 queue
  27100. add work request for digit step level3 queue
  27101. on overflow {
  27102. add work request for digit change to minutes-tens to level 4 queue
  27103. add work request for digit step level3 queue
  27104. }
  27105. on tens overflow {
  27106. add work request for digit change to hours-ones to level 4 queue
  27107. add work request for digit step level3 queue
  27108. }
  27109. on hours ones overflow {
  27110. add work request for digit change to hours-tens to level 4 queue
  27111. add work request for digit step level3 queue
  27112. }
  27113.  
  27114. on hours-twelve overflow {
  27115. add work request for digit change to hours-ones to level 4 queue
  27116. for (i=0; i<8; i++)
  27117. add work request for digit step to level3 queue
  27118. add work request for digit change to hours-tens to level 4 queue
  27119. add work request for digit step to level 3 queue
  27120. }
  27121. }
  27122.  
  27123. digit change {
  27124. set multiplexor to select requested digit
  27125. }
  27126.  
  27127. digit step {
  27128. for (i=1; i<60; i++) {
  27129. add work request for one_pulse to level4 queue
  27130. }
  27131. }
  27132.  
  27133. one_pulse {
  27134. pulse stepping motor
  27135. busy-wait for 10 ms
  27136. return
  27137. }
  27138.  
  27139.  
  27140.  
  27141.  
  27142.  
  27143.  
  27144.  
  27145.  
  27146.  
  27147.  
  27148.  
  27149.  
  27150.  
  27151.  
  27152.  
  27153.  
  27154.  
  27155.  
  27156.  
  27157.  
  27158.  
  27159.  
  27160.  
  27161.  
  27162.  
  27163.  
  27164.  
  27165.  
  27166.  
  27167.  
  27168.  
  27169.  
  27170.  
  27171.  
  27172.  
  27173.  
  27174.  
  27175.  
  27176.  
  27177. On The Networks
  27178.  
  27179.  
  27180. A Perl Of Great Price
  27181.  
  27182.  
  27183.  
  27184.  
  27185. Sydney S. Weinstein
  27186.  
  27187.  
  27188. Sydney S. Weinstein, CDP, CCP is a consultant, columnist, author and president
  27189. of Datacomp Systems, Inc., a consulting and contract programming firm
  27190. specializing in databases, data presentation and windowing, transaction
  27191. processing, networking, testing and test suites and device management for UNIX
  27192. and MS-DOS. He can be contacted care of Datacomp Systems, Inc., 3837 Byron
  27193. Road, Huntingdon Valley, PA 19006-2320 or via electronic mail on the
  27194. Internet/Usenet mailbox syd@DSI.COM (dsinc!syd for those that cannot do
  27195. Internet addressing).
  27196.  
  27197.  
  27198.  
  27199.  
  27200. Administrivia
  27201.  
  27202.  
  27203. As I have said in prior columns, I am willing to forward a list of Usenet
  27204. sites near you for access to Usenet, Netnews and E-mail. However, I can only
  27205. provide this service to those who send a self-addressed stamped envelope.
  27206. Also, please include your area code in the request. An area code gives me a
  27207. greater chance of finding a site that might be a local call for you.
  27208. Note, however, I do not contact these sites for permission. All I am doing is
  27209. extracting the names and contact information from the Usenet mapping
  27210. information and sending you that printout. It is up to you to contact the
  27211. sites listed in the maps. Remember, they are doing you a favor if they let you
  27212. connect.
  27213.  
  27214.  
  27215. Pearl Of The Month: Perl
  27216.  
  27217.  
  27218. One of the most respected freely distributed software authors on the net is
  27219. Larry Wall of JPL-NASA. He has written many software tools including the
  27220. popular netnews reader RN, the source language patching program Patch, and a
  27221. software configuration and distribution support toolset Dist. His latest large
  27222. effort has been Perl -- Practical Extraction and Report Language, or
  27223. Pathologically Eclectic Rubbish Lister. Perl was first released as version 2.
  27224. This review is of his new release, version 3.
  27225. To quote the manual page: "Perl is an interpreted language optimized for
  27226. scanning arbitrary text files, extracting information from those text files,
  27227. and printing reports based on that information. It's also a good language for
  27228. many system management tasks. The language is intended to be practical (easy
  27229. to use, efficient, complete) rather than beautiful (tiny, elegant, minimal).
  27230. It combines (in the author's opinion, anyway) some of the best features of C,
  27231. sed, awk, sh, so people familiar with those languages should have little
  27232. difficulty with it. (Language historians will also note some vestiges of csh,
  27233. Pascal, and even BASIC PLUS.)"
  27234. That paragraph was true for version 2, but is an understatement for version 3.
  27235. Perl can now handle binary files, network sockets, and even dbm database files
  27236. with ease.
  27237. Perl runs counter to the typical UNIX tool philosophy of "do one item in a
  27238. tool and hook many tools together with shell scripts and pipes." Perl allows
  27239. you to combine all the sections together in one efficient script. Perl's two
  27240. great claims to fame in my opinion are its ability to provide the right set of
  27241. features for writing useful tools for systems, and its interpretative nature
  27242. to allow for easy debugging and development.
  27243.  
  27244.  
  27245. Installation
  27246.  
  27247.  
  27248. Perl is not a small program, so if snarfed off the network it comes in many
  27249. parts. Perl also, as of this writing in mid-December (Now you know, these
  27250. columns have about a four month lead time) six patches have been issued to
  27251. Perl, making the current version Release 3.0 Patchlevel 6. After unpacking all
  27252. the parts and applying the six patch files (using Larry's patch utility, of
  27253. course), the instructions say to run a shell script called Configure. It's
  27254. worth obtaining Perl just to see how this is done. Configure, a giant shell
  27255. script, written by the Perl program metaconfig from Larry's Dist package
  27256. analyzes your system and determines what features of Perl your system can
  27257. support, where things are located on your system, and a great deal of
  27258. additional information to make Perl install correctly on your system
  27259. automatically. If only other packages used this method (Note, Elm does).
  27260. After the Configure is run, a special version of the make script automatically
  27261. figures the dependencies for each C file as you have configured them and
  27262. adapts the Makefile. Then the system is compiled three times, once for normal
  27263. Perl, once for a version called taintperl, and lastly for a setuidperl
  27264. version. The tainted versions prevent any command line argument, environment
  27265. variable, or input nor any result of operations on these values from being
  27266. used in subshells, system calls, or for modifying files or directories. This
  27267. is used for setuid scripts.
  27268. Now, another thing I wish more authors provided: Perl has a rather complete
  27269. regression test suite to validate Perl's configuration and compilation. This
  27270. test suite may not catch all problems, but it goes a long way towards
  27271. providing confidence that a package as large as this one was configured
  27272. properly and compiled without compiler induced errors. Perl runs the
  27273. regression test automatically after the system has been built, and performs
  27274. over 850 separate tests.
  27275.  
  27276.  
  27277. Features
  27278.  
  27279.  
  27280. As a combination of the shell, C, sed and awk, Perl has a syntax close to C,
  27281. with most of its operators, plus the ability to process variables and run
  27282. subprocesses like the shell, perform pattern matching and substitution like
  27283. sed, and report generation features similar to awk. A couple of the more
  27284. interesting features include:
  27285. Associative arrays: In addition to scalar variables (single numbers or text
  27286. strings) and normal arrays (vectors of numbers or strings), Perl also provides
  27287. an array concept called an associative array. This array is a mapping of
  27288. tuples. Thus the array index, called a key, is itself just a number or text
  27289. string. The difference between this and an array where the index is an
  27290. enumerated type is that the index is dynamic and includes any values desired
  27291. at run time. Thus you could say
  27292. $balls{'red'} = 7;
  27293. $balls{'green'} = 34;
  27294. $balls{'blue'} = 12;
  27295.  
  27296. while (($color, $number) = each
  27297. %balls) {
  27298. print "I have $number
  27299. $color balls\n";
  27300. }
  27301. Variables are preceded by a $ and the { } array indices are for associative
  27302. arrays. Standard arrays use [] for their indices. The % prefix indicates the
  27303. entire associative array. Thus this program initializes the associative array
  27304. and then loops using the each function to return each tuple of key and value.
  27305. These tuples are assigned to the scalar variables color and number and then
  27306. used in the print statement. An easier way to initialize the array would be to
  27307. use the list construct of Perl:
  27308. %balls = (
  27309. 'red', 7,
  27310.  
  27311. 'green', 34,
  27312. 'blue', 12');
  27313. Open function: Perl's open call can also open pipes to or from other
  27314. processes. Thus Perl can start other processes and either read their results
  27315. (very useful for letting Perl figure out the SQL to run and then running SQL
  27316. to obtain the data) or for passing Perl's output to another program (such as
  27317. the print spooler). Of course, Perl can read, write, and append to files with
  27318. the open function.
  27319. Formats: Perl supports a BASIC-like format option for output files in addition
  27320. to the print and printf constructs. The Perl program in Listing 1 converts
  27321. UNIX System V type df (disk free) listings into the BSD type of report.
  27322. Listing 1 shows several of the features of Perl, as well as demonstrating the
  27323. format capabilities.
  27324. The first three lines make sure that Perl is running the program, allowing a
  27325. plain "executable" file to automatically be a Perl script. If the system
  27326. supports the #! notation, then the kernel will spawn Perl to handle this file
  27327. automatically, and not the shell. Otherwise the second line causes the shell
  27328. to execute Perl on the script. When Perl does see the script, it treats the
  27329. first line as a comment, and the second and third lines as a valid Perl
  27330. statement. Since the variable running_via_sh is not non-zero (it isn't even
  27331. defined yet), the eval statement is not executed and Perl just continues on in
  27332. the script.
  27333. he array (@ is the symbol for an entire regular array) of the arguments passed
  27334. to the Perl script, not counting the name of the script. Thus the join line
  27335. makes a text string of all the arguments separated by spaces. This string is
  27336. used in the open call to the df process, causing it to output only the
  27337. requested file systems (if arguments are given), or all the file systems (if
  27338. no arguments are given). Note that if the df fails, the shell construct is
  27339. honored to allow the error message to be output when the open fails.
  27340. The formats could appear anywhere in the script. The default top of page
  27341. format for the STDOUT file is called top, but that association can easily be
  27342. changed. In this case the top format is used to provide column headers. Note
  27343. that format continues across lines until a line with just a period is
  27344. encountered, thus outputting multiple lines. Both top of page and file formats
  27345. may also include variable substitutions. The STDOUT format "writes" to the
  27346. standard output file and have three types, <, , and > for left justify,
  27347. center, and right justify respectively. All variable substitution formats
  27348. start with a @ character and take as many spaces as are desired. Each line
  27349. with @s in it is immediately followed by a line listing the variables to print
  27350. on that line. It is not necessary to space variables as I did, but I think the
  27351. spacing improves readability.
  27352. The while loops over the lines read from the Df file. The special symbols <>
  27353. mean read a line from the file. The if block uses regular expression matching
  27354. on the line just read and only performs the then clause of the if when the
  27355. line contains the text string total blocks. In the else clause the s commands
  27356. are string substitution, again based on regular expression mapping. These
  27357. commands work on the input line by default, however the =~ operator is used a
  27358. couple of lines later to specify that the substitute should be performed on
  27359. the $name variable instead of the input line. The write function is used to
  27360. output a line using the format specified earlier.
  27361. Finally, the last if block uses the special variable syntax $#name, which
  27362. references the subscript of the highest element. Since this Perl script
  27363. origins arrays at zero (the default), a less than zero check tells whether any
  27364. arguments were passed to the script. As a result of this test, the total line
  27365. is only printed if no arguments were passed and the df is for all file
  27366. systems.
  27367. Perl scripts are also easy to debug, in part because of the debugger imbedded
  27368. within Perl. Adding a -d argument to the invocation line tells Perl to run the
  27369. script in debug mode. Debug mode supports breakpoints, single stepping program
  27370. browsing and "immediate mode" execution of any valid Perl statement. Thus the
  27371. contents of variables can be examined or changed at any time.
  27372. I didn't even describe directory processing, BSD socket access, subroutines or
  27373. much on regular expression processing. Perl does come with a complete
  27374. reference manual, although a tutorial manual is not provided.
  27375. Perl, of course, is most useful on UNIX (or Xenix) systems. However,
  27376. restricted portions of Perl have been compiled on VMS and on MS-DOS. Perl has
  27377. gotten so popular there is now a Usenet news group comp.lang.perl. But Perl is
  27378. not a small program, and its load size causes a sizeable overhead at startup.
  27379. Of course, for longer scripts this delay is not a problem, but the overhead is
  27380. enough to keep Perl from replacing the shell for all scripts.
  27381.  
  27382.  
  27383. There's More
  27384.  
  27385.  
  27386. comp.sources.unix was active for a short while and has again gone quiet.
  27387. During its active time, Rich Salz, the moderator of comp.sources.unix, did
  27388. provide some unusual postings.
  27389. From Harold Walters at Oklahoma State University came a set of 109 functions
  27390. called xxalloc providing dynamic array manipulation in one, two and three
  27391. dimensions. xxalloc includes routines for allocating, initializing, printing,
  27392. renumbering and fleeing both arrays of structures and arrays of simple types.
  27393. An "edge-vector" approach is used for two- and three-dimensional arrays to
  27394. allow for development of reusable subroutine libraries without regard to some
  27395. "maximum" dimension. The package includes installation instructions, a test
  27396. program to exercise most of the package, and manual page. It has been tested
  27397. on System V, BSD and MS-DOS machines and is available as Volume 20, Issue 28.
  27398. Chin Huang has written a program to automatically generate C function
  27399. prototypes and variable declarations from C language source code. It differs
  27400. from other similar programs in that it doesn't parse the function body. This
  27401. package needs FLEX, which is also available from the archive sites. Cproto is
  27402. Volume 20, Issue 29.
  27403. For those still using curses instead of bit-mapped screens, John Lupien at
  27404. AT&T has published a curses-based digital clock for VT100 and compatibles.
  27405. It's a small, simple program and is Volume 20, Issue 45.
  27406. Plum-Hall has placed into the public domain a simple set of benchmarks
  27407. intended to give programmers timing information about common C operations.
  27408. They were designed to be short enough to type while browsing at trade shows,
  27409. and are protected from overly aggressive compiler optimizations. The
  27410. plumbenchmarks are Volume 20, Issue 47.
  27411. David Curry at NASA Ames Research Center has posted Index, a program to allow
  27412. you to maintain multiple databases of textual information, each with a
  27413. different format. For each database Index allows insertion, deletion, edits on
  27414. existing entries, searches using full regular expressions, restricted
  27415. searches, pattern matching and arbitrary formatting. Index is Volume 20, Issue
  27416. 56 and 57.
  27417. Richard O'Rourke of Microplex Systems, Ltd. provided a pegboard program which
  27418. keeps track of who is in and out of the office, and when they are due back.
  27419. The program is designed for Xenix, but should work on other flavors of UNIX
  27420. and is in Volume 20, Issue 76.
  27421. For those running Xenix or UNIX V3.2.1, Volume 20, Issue 81 and 83 from Eric
  27422. Raymond has provided an editor/minilanguage to rebind the keyboard on the
  27423. console. Useful for Emacs users and for changing the virtual terminal selector
  27424. keys.
  27425. One of the stranger programs in comp.sources.unix in that last spurt of
  27426. postings was the "Reactive Keyboard". Mark James of the University of Calgary
  27427. has augmented a general-purpose command line editor with predictive text
  27428. generation. The program interfaces with a standard shell, allows simple
  27429. editing of input lines, and will predict input lines based on previous input.
  27430. It's weird to type an edit followed by a compile and have the command
  27431. processor provide the file name for the compile, and then after you edit the
  27432. file again, have it predict another compile. The Reactive Keyboard is Volume
  27433. 20, Issues 29-32, but it requires BSD-style ptys to work properly.
  27434. Chip Salzenberg of AT-Engineering has posted his latest version of Deliver, a
  27435. program which delivers electronic mail once it has arrived at a given machine.
  27436. Deliver extends inflexible E-mail delivery systems to allow complete control
  27437. over mail deliver through the use of delivery files. Delivery files are shell
  27438. scripts which are executed during message delivery. These scripts control
  27439. which people or programs get each E-mail message. Look for Volume 20 Issues
  27440. 23-27.
  27441. Pcomm, v1.2, is a UNIX telecommunications program made to look like Datastorm
  27442. Technologies ProComm for MS-DOS. New in v1.2 is BSD support, auto-login
  27443. scripts (using shell scripts), imbedded external file transfer programs, and
  27444. faster operation via I/O buffering. Emmet Gray from the US Army submitted this
  27445. as Volume 20, Issues 67-75.
  27446. For those stuck with the old troff, and wanting to deal with printers other
  27447. than the Wang C/A/T phototypesetter, Chris Lewis of Elegant Communications,
  27448. Inc. has provided psroff. It converts the output of standard troff to
  27449. postscript, di-troff format, and a partial attempt at HP-LJ family of
  27450. printers. Several patches are also available to further enhance this package
  27451. which is Volume 20, Issues 33-38.
  27452.  
  27453.  
  27454. And Still More:
  27455.  
  27456.  
  27457.  
  27458.  
  27459. comp.sources.misc
  27460.  
  27461.  
  27462. Although Rich Salz has been intermittent with postings, Brandon Allbery, the
  27463. moderator of comp.sources.misc has been providing plenty to write about.
  27464. In Volume 8, Issue 99, Paul Blackburn of the Open Software Foundation provided
  27465. a script for keeping track of changes to files. Useful to system
  27466. administrators who need to detect unwanted or unexpected changes to files.
  27467. A UNIX make work-alike, Make v1.5 was posted as Volume 8 Issues 104-106 by
  27468. Greg Yachuk of Informix Software. This make is very close to the make provided
  27469. on Sun systems and runs under MS-DOS or UNIX. New features include the -k, -S
  27470. and -q options, supporting the $(MAKE) macro, and several bug fixes.
  27471. A 16-bit MS-DOS compress is also available. Most versions for MS-DOS cannot
  27472. handle 16-bit compression tables (the default on most UNIX systems). This
  27473. version can, and is based on, the Compress 4.0 UNIX sources. It requires about
  27474. 400K to run. Posted as Volume 9 Issue 5 by Doug Graham.
  27475. Steve Tynor has his head in the clouds to provide us with FPLAN, a flight
  27476. planning program intended for use in general aviation. It reads a file
  27477. consisting of departure and destination airports, navigation aids,
  27478. intermediate checkpoints, fuel consumption rates and winds aloft and produces
  27479. a flight plan with wind corrected heading, fuel consumption for each leg, vor
  27480. fixes for each checkpoint (Volume 9, Issues 11-16).
  27481. Lastly is popi, a program to perform interactive digital image
  27482. transformations. Based on the program described in the book Beyond
  27483. Photography--The Digital Darkroom by Gerald J. Holzmann, this implementation
  27484. by Rich Burridge consists of an interactive previewer and a digital matrix
  27485. transformation system. Popi can perform transformations on arbitrary images in
  27486. grey scale. A sample image is included to show how to invert the grey scale
  27487. (make it a negative), frame it (crop), and solarize it (fancy signal
  27488. processing). Popi includes a postscript printing facility and modules to allow
  27489. it to work for Amiga, Apollo, Atari, IBM PC, Kermit, MGR, NeWS, SunView, X11
  27490. and XView systems. The nine parts are Volume 9 Issues 47-55.
  27491.  
  27492. Listing 1
  27493. #!/usr/local/bin/perl
  27494. eval "exec /usr/local/bin/perl -S $0 $*"
  27495. if $running_via_sh;
  27496. # A cute Berkeley style df formatter for those running USG df
  27497. # Do what you want with it; it's yours.
  27498. # R. Craig Peterson, N8INO
  27499. $fs=join(' ',@ARGV);
  27500. open(Df, "df -t $fs ") die "Can't run df.";
  27501.  
  27502. format top =
  27503. Filesystem kbytes used avail capacity iused ifree %iused Device
  27504.  
  27505. format STDOUT =
  27506. @<<<<<<<<<<<<<< @>>>>>> @>>>>>> @>>>>> @>% @>>>>> @>>>>> @>% @<<<<<<<<<<<<<<
  27507. $fs $kbytes $used $avail $capacity $iused $inodes $piused $name
  27508.  
  27509. while (<Df>) {
  27510. if (/total blocks/) {
  27511. ($d,$tblocks,$d,$d,$tinodes,$d) = split(' ');
  27512. $tinodes *= 8;
  27513. $kbytes = $tblocks / 2;
  27514. $used = ($tblocks - $blocks) / 2;
  27515. $avail = $blocks / 2;
  27516. $capacity = int(100 - ($blocks / $tblocks * 100));
  27517. $iused = $tinodes - $inodes;
  27518. $piused = int($iused / $tinodes * 100);
  27519. write;
  27520. $tot_kbytes += $kbytes;
  27521. $tot_used += $used;
  27522. $tot_avail += $avail;
  27523. $tot_iused += $iused;
  27524. $tot_inodes += $inodes;
  27525. $tot_tinodes += $tinodes;
  27526. } else {
  27527. s/\(\s*/ \(/;
  27528. s/\s*\)/\)/;
  27529. ($fs,$name,$blocks,$d,$inodes,$d) = split;
  27530. $name =~ s![(): \t] /dev/dsk/!!g;
  27531. }
  27532. }
  27533. if ($#ARGV < 0) {
  27534. $kbytes = $tot_kbytes;
  27535. $used = $tot_used;
  27536. $avail = $tot_avail;
  27537. $capacity = int(100 - ($avail / $kbytes * 100));
  27538. $iused = $tot_iused;
  27539. $inodes = $tot_inodes;
  27540. $tinodes = $tot_tinodes;
  27541. $piused = int($iused / $tinodes * 100);
  27542. $fs = 'Totals:';
  27543. $name= '';
  27544. write;
  27545. }
  27546.  
  27547.  
  27548.  
  27549.  
  27550.  
  27551.  
  27552.  
  27553.  
  27554.  
  27555.  
  27556.  
  27557.  
  27558.  
  27559.  
  27560.  
  27561.  
  27562.  
  27563.  
  27564.  
  27565.  
  27566.  
  27567.  
  27568. Code Base 4
  27569.  
  27570.  
  27571. Darren Forcier
  27572.  
  27573.  
  27574. The author is a consulting dBase and Clipper programmer. He holds a bachelor's
  27575. degree in computer science from Central New England College of Technology in
  27576. Worcester, Mass. He can be contacted at 253 Main St., Cherry Valley, MA 01611
  27577. (508) 892-3351.
  27578.  
  27579.  
  27580. Code Base 4 from Sequiter Software, Inc., is a set of C routines which allow
  27581. you to access dBase III+ and IV files and perform screen I/O similar to
  27582. dBaseIII+. Code Base 4 includes support for dBase .NDX index files, Clipper
  27583. .NTX files, memo fields, and networked applications. The new dBaseIV .MDX
  27584. (multiple index) files are not yet supported. Code Base 4 also adds some new
  27585. levels of functionality with special routines for windows, menus, and memory
  27586. handling.
  27587. Code Base 4 supports dBase/Clipper functionality with 12 categories of
  27588. routines:
  27589. conversion routines
  27590. database routines
  27591. expression evaluation
  27592. field routines
  27593. get routines
  27594. memory handling routines
  27595. index file routines
  27596. memo routines
  27597. menuing routines
  27598. utility routines
  27599. windowing routines
  27600. extended routines
  27601. The conversion routines convert data from one format to another. For example,
  27602. c4dt_dbf() converts Julian dates from the internal double representation to a
  27603. string formatted CCYYMMDD (century, year, month and day).
  27604. The database routines are the main meat of the Code Base 4 package. They allow
  27605. C programmers to access and store information in the well-established dBase
  27606. .DBF file format and include C function equivalents of many dBase commands.
  27607. Due to the more stringent nature of compiled C programming as opposed to the
  27608. dBase interpretive environment, some differences exist. Table 1 compares dBase
  27609. commands to their Code Base 4 function counterparts.
  27610.  
  27611.  
  27612. Expression And Indexes
  27613.  
  27614.  
  27615. With the dBase LINK_DEST 125 command, one may use any valid dBase expression
  27616. to interactively build an index. For example, to create an index for an
  27617. accounts receivable file based on invoices sorted by invoice date, you could
  27618. use the following command:
  27619. INDEX ON STR(YEAR(invc_date))+; STR(MONTH(invc_date))+STR(DAY(invc_date)); TO
  27620. INVOICE
  27621. The STR(YEAR(invc_date.... portion is an "index expression". dBase evaluates
  27622. this expression from left to right at runtime, and builds an .NDX file based
  27623. on the expression. Code Base 4 uses an internal expression parser similar to
  27624. dBase's. Several of the expression functions are available to advanced
  27625. programmers who might wish to build an SQL-like front end to their
  27626. application.
  27627. The field routines access individual fields and information pertaining to the
  27628. fields (e.g., their name, length, and data type).
  27629. The get routines form the basis for data entry. The get routines arrange field
  27630. data entry blocks and control field-to-field navigation within that block. As
  27631. with dBase, you can control the get field in various ways, through pictures
  27632. and validation clauses. Unlike dBase, Code Base 4 also allows you to access
  27633. the individual attributes of each get field, allowing each Code Base 4 field
  27634. to have its own color, brightness, or other attribute setting.
  27635. Programmers with strong memory management background can use the Code Base 4
  27636. memory handling routines to optimize their applications memory usage. Routines
  27637. are included to allocate, reallocate, and free memory from the internal Code
  27638. Base 4 structures.
  27639. Index files are used to access data in sorted order. With Code Base 4, either
  27640. dBase .NDX files or Clipper .NTX files can be used. Like dBase and Clipper,
  27641. Code Base 4 automatically updates all open index files when a record is
  27642. written. However, with Code Base 4 you can use lower-level routines to access
  27643. and update the index files directly.
  27644. The memo routines access dBase variable length memo fields (pointers to a
  27645. separate text file containing free format text).
  27646. Unfortunately, Code Base 4 doesn't provide an editor for memo fields. A small
  27647. Wordstar-compatible editor window function should have been included in the
  27648. package, with definable screen coordinates. Nantucket's Clipper provides a
  27649. MEMOEDIT() function which takes screen coordinates as its parameters, and
  27650. allows you to edit memo fields in a word-wrapping window.
  27651. The menuing routines include pulldown, popup, vertical, horizontal, and Lotus
  27652. 1-2-3 style menus.
  27653. The utility routines provide some low-level functions for parsing and
  27654. validating file names, sorting arrays, and locking and unlocking regions of a
  27655. shared file.
  27656. The windowing routines display and manipulate regions of the I/O screen. The
  27657. windowing routines form the basis for the menuing routines.
  27658. The extended routines support some of dBaseIII and dBaseIV's extended
  27659. functions, including record filtering (a subset view of a file based on some
  27660. selection criteria), dBase relations (conditions which synchronize the
  27661. movement of record pointers in separate files), an edit function
  27662. (interactively change records), a record listing function, and an insertion
  27663. function (adds blank records at any point).
  27664. Code Base 4 comes with complete source code and conditional compilation
  27665. switches for Clipper .NTX compatibility, no screen management, OS/2 support,
  27666. Turbo C compatibility and Microsoft Windows support. A batch file is supplied
  27667. for performing customized compilations of the library.
  27668. dBase programs ported to Code Base 4 should be faster than the interpreted
  27669. dBase original and smaller than a version compiled by Clipper. The Clipper (t)
  27670. compiler from Nantucket Software speeds up program execution greatly, but due
  27671. to library overhead, Clipper applications have a minimum size of 150K.
  27672. Complete applications are typically about 300-350K. This makes it difficult to
  27673. run a Clipper program with many TSRs loaded or in a multitasking environment
  27674. such as DoubleDos. With Code Base 4, the linker only links in exactly what it
  27675. needs from the library, so the application is much smaller.
  27676. Code Base 4 assumes the user has a lot of C and dBase experience and a good
  27677. command of C pointers, memory allocation, and structures. Beginning C
  27678. programmers may quickly find themselves lost. The package makes heavy use of
  27679. linked lists to model database, field, window, and menu structures. Code Base
  27680. 4 is primarily a tool for the programmer who has mastered C and dBase and
  27681. wants to transcend the limits of dBase and Compiled dBase (Clipper,
  27682. Quicksilver).
  27683. The wire-bound, 200-page Code Base 4 manual, while comprehensive, has no
  27684. tutorial or examples section. Each chapter is broken down into a functional
  27685. area of the Code Base 4 library, and each library function is documented for
  27686. that section. Some function descriptions have small source code examples;
  27687. others do not. Several sample programs are supplied on diskette. The samples
  27688. are well written, and illustrate examples of how to build menus, windows, and
  27689. database applications. I liked the manual's organization but often found the
  27690. function descriptions a little thin. A front-end tutorial section with plenty
  27691. of examples would help this manual greatly.
  27692. Technical support is available by phone.
  27693. Overall, Code Base 4 does the job for which it is intended: it brings a
  27694. dBase-like programming environment to C, and allows you to share data between
  27695. dBase and C programs. It does have some limitations. Using an external editor
  27696. to access dBase memo fields is a little awkward.
  27697. I have worked with several other dBase C libraries. APEX ADL from Apex
  27698. Software offers similar support for dBase file and index creation and access,
  27699. but doesn't include any menuing, windowing, or get functions.
  27700. I would recommend Codeview4 to any programming shop that needs to transcend
  27701. the limits of dBaseIII+ and Clipper. Code Base 4 can address speed, memory
  27702. requirements, or specialized dBase compatible applications (such as a memory
  27703. resident TSR). For straightforward applications development, dBase and Clipper
  27704. are still pretty powerful programming environments.
  27705. The MS-DOS version is listed at $295 and the UNIX version at $495. For more
  27706. information contact Sequiter Software Inc., P.O. Box 5659, Station L,
  27707. Edmonton, Alberta T6C 4G1 (403) 439 8171; FAX (403) 433-7460.
  27708. Table 1 Dbase Commands & Their Codebase4 Equivalents
  27709. Dbase Codebase4
  27710. Command/Function Function Description
  27711. --------------------------------------------------------------
  27712. go bottom d4bottom() Go to last record
  27713.  
  27714. go top d4top() Go to first record
  27715. use d4close() Close individual .DBF file
  27716. close all d4close_all() Close all open database files
  27717. create d4create() Create a new .DBF File
  27718. delete d4delete() Mark a record for deletion
  27719. deleted() d4deleted() Return TRUE if record deleted
  27720. go <record#> d4go() Go to a specific record #
  27721. rlock()/flock() d4lock() Lock a portion or all of file
  27722. pack d4pack() Remove deleted records
  27723. recall d4recall() Undelete marked records
  27724. reccount() d4reccount() # of records in file
  27725. recno() d4recno() Current record number
  27726. seek "key-val" d4seek() Look up record in index key
  27727. select d4select() Make database area current
  27728. skip <#records> d4skip() Move record pointer #records
  27729. unlock d4unlock() Unlock File/Record
  27730. use <Filename> d4use() Open a database file
  27731. replace d4write() Update database record
  27732. append blank d4write(0) Append a blank record
  27733. zap d4zap() Delete all records & pack
  27734.  
  27735. Listing 1 test.c
  27736. #include <d4base.h>
  27737.  
  27738. #define SAFETY_ON 1 /* d4create will return -1 if database already exists.. */
  27739. #define SAFETY_OFF 0 /* d4create will overwrite database if it exists... */
  27740.  
  27741.  
  27742. /* Declare Field Structure for database */
  27743.  
  27744. static FIELD FIELDS[] =
  27745. {
  27746. /* Field Name, Type, Width, Dec, Offset */
  27747. {"FIRST_NAME", 'C', 25, 0, 0 }, /* Char 25 */
  27748. {"LAST_NAME", 'C', 25, 0, 0 }, /* Char 25 */
  27749. {"COMPANY", 'C', 30, 0, 0 }, /* Char 30 */
  27750. {"TELEPHONE", 'C', 12, 0, 0 }, /* Char 12 */
  27751. {"LAST_SALE", 'N', 12, 2, 0 }, /* Numeric */
  27752. {"LAST_DATE", 'D', 8, 0, 0 }, /* Date */
  27753. {"GOOD_CUST", 'L', 1, 0, 0 } /* Logical */
  27754. } ;
  27755.  
  27756. int create_name( void ); /* Prototype for CUSTOMERS.DBF creation function */
  27757.  
  27758. main()
  27759. {
  27760. int rc; /* Return Code */
  27761. rc = create_name();
  27762. printf("\n rc return code was %d",rc);
  27763. return;
  27764. }
  27765.  
  27766. int create_name( void )
  27767. {
  27768. int rc; /* Return Code */
  27769.  
  27770. rc = d4create("CUSTOMER.DBF",7,FIELDS,SAFETY_OFF);
  27771. return rc;
  27772. }
  27773.  
  27774.  
  27775.  
  27776.  
  27777.  
  27778.  
  27779.  
  27780.  
  27781.  
  27782.  
  27783.  
  27784.  
  27785.  
  27786.  
  27787.  
  27788.  
  27789.  
  27790.  
  27791.  
  27792.  
  27793.  
  27794.  
  27795.  
  27796.  
  27797.  
  27798.  
  27799.  
  27800.  
  27801.  
  27802.  
  27803.  
  27804.  
  27805.  
  27806.  
  27807.  
  27808.  
  27809.  
  27810.  
  27811.  
  27812.  
  27813.  
  27814.  
  27815.  
  27816.  
  27817.  
  27818.  
  27819.  
  27820.  
  27821.  
  27822.  
  27823.  
  27824.  
  27825.  
  27826.  
  27827.  
  27828.  
  27829.  
  27830.  
  27831.  
  27832.  
  27833.  
  27834.  
  27835.  
  27836.  
  27837. Advanced C: Tips And Techniques
  27838.  
  27839.  
  27840. Randy Hohl
  27841.  
  27842.  
  27843. Randy Hohl is a consultant with Interactive Systems Corporation. He has worked
  27844. as an industry programmer for six years and has bachelor's degrees in computer
  27845. engineering and psychology. He can be reached at (708) 505-9100 or randy@ i88.
  27846. isc. com.
  27847.  
  27848.  
  27849. Advanced C: Tips and Techniques is the third installment in a series of four
  27850. books on C Programming in the Hayden Books C Library. The first two books
  27851. present the fundamentals of C. Advanced C emphasises portability, compiler
  27852. code-generation, and execution speed. The book is authored by the team of Paul
  27853. and Gail Anderson. The authors discuss some of the more difficult components
  27854. of C using a variety of clever programming techniques.
  27855. The book is organized into six chapters and five appendices. Chapter one
  27856. serves as a C refresher, the remaining chapters each cover a set of related
  27857. topics. Every chapter concludes with a number of programming exercises. Each
  27858. appendix details the features of a particular C compiler.
  27859. Chapter one introduces some portable techniques for swapping variable
  27860. contents, ASCII-to-integer and decimal-to-hex conversions, and bit-level
  27861. operations. The techniques recur throughout the book and are presented as
  27862. program examples employing an advanced usage of unsigned variables, unions,
  27863. casts and macros. This chapter sets the theme for the authors' low-level
  27864. approach to writing efficient and portable C; some of the examples are simply
  27865. assembly-language tricks converted to C.
  27866. Chapter two provides a comprehensive explanation of the C runtime environment.
  27867. The compiler's distribution of program statements and variables into the text
  27868. area, data area, the stack, and the heap is examined. This chapter answers
  27869. questions like: "Where does a literal string live?" and "Is it faster to
  27870. initialize static or automatic variables?" Examples are provided which
  27871. demonstrate the compiler-mapping of program variables by printing the hex
  27872. address of variables at runtime. I bought the book for this chapter and was
  27873. thoroughly pleased. A complete source-code solution to the fragmentation of
  27874. heap memory resulting from numerous runtime allocations is developed in
  27875. chapter six.
  27876. You may not find a need for arrays of two or three dimensions very often, but
  27877. if you do, chapter three, the longest in the book, is probably the most
  27878. complete treatment of the subject available. Portions of this chapter read
  27879. like formula derivations and proofs in a math text. This method is used to
  27880. derive equivalent pointer expressions for multi-dimensional array references.
  27881. The derivations are used to demonstrate the storage map equations used by a C
  27882. compiler to evaluate array references. Other compiler formulae are also
  27883. presented. I found this chapter to be rather complicated, but still valuable.
  27884. The authors show two methods to increase the speed of array referencing.
  27885. Method one uses compact pointer expressions, such as *ptr++, rather than
  27886. sequential pointer offsets, such as array[offset]; offset++. The argument here
  27887. is that a compact pointer expression maps directly to a single assembler
  27888. instruction. Method two uses pointer array declarations rather than
  27889. multi_dimensional arrays. The methods are incorporated into a suite of ten
  27890. benchmark programs which the authors have timed on 286, 386 and 68020
  27891. machines. The results show an average performance improvement of 27 percent
  27892. using the optimized methods across the three architectures.
  27893. Ever been faced with a C declaration like, char *(*(*buf[20]) ())[10]? Complex
  27894. declarations are succinctly deciphered in chapter four. The authors use a
  27895. presentation mode developed in the preceding chapter, namely the repeated
  27896. application of a rule, in this case the "Right-Left" rule. The Right-Left rule
  27897. is simple and works nicely for both creating and reading complex declarations.
  27898. This chapter also explains how to use varargs.h to create portable routines
  27899. that accept a variable number of arguments.
  27900. Debugging techniques in C is the topic of chapter five. The authors develop
  27901. six categories of custom debugging tools. Each category is predicated on
  27902. surrounding utilities, including the C preprocessor, the assert() library
  27903. routine, and the UNIX signal() system call. Some of the tools have the
  27904. advantage of providing a variable degree of error-checking without the need to
  27905. re-compile the original source, a big advantage in large systems. Where
  27906. significant compile- or run-time overhead is required to implement a custom
  27907. technique, the authors are quite frank about the tradeoffs involved. The value
  27908. of some of these techniques took a while to sink in.
  27909. The appendices of the book are a well-written description of the AT&T C
  27910. compiler and four different compilers targeted for Intel processors. All the
  27911. options and all the memory models provided by each compiler are described.
  27912. Chapter two's discussion of the C runtime environment is prerequisite
  27913. knowledge for understanding the memory models. New constructs from the
  27914. proposed ANSI C Standard are discussed for compilers which are supportive.
  27915. The book includes an order form for a set of two floppies, one containing the
  27916. C source code for all the program examples, the other containing solutions to
  27917. all the exercises. The price is $39.95. I examined the contents of both disks
  27918. and was impressed. The examples disk contains over 150 .c files; each file
  27919. contains a stand-alone C program drawn from the book. For book examples which
  27920. are only code fragments, the fragment is expanded into a complete program.
  27921. Nearly all examples make judicious use of printf statements to illustrate the
  27922. subject matter. The solutions disk contains over 70 .c files, each an
  27923. individual program. The solutions are complete and contain ample explanatory
  27924. notes as C comments. Two of the chapter three solutions are timed on the 286.
  27925.  
  27926.  
  27927. Conclusions
  27928.  
  27929.  
  27930. I have mainly bravos for this book. Each topic is covered from both the
  27931. compile-time and runtime perspectives. The authors incorporate portability and
  27932. efficiency throughout, using the proper features of C. The presentation is
  27933. well-paced and properly organized. All major points are illustrated with
  27934. appopriate-length program examples. Some program examples are reused and
  27935. enhanced in the light of the current topic. Advanced usage of some features of
  27936. C, such as macro definitions with arguments, typedef variable types and
  27937. compact conditional operator expressions, are shown implicitly in the program
  27938. examples. Proven techniques, such as the benefit of compact pointer
  27939. expressions, are used in subsequent programs. The use of customized standard
  27940. header files is especially creative.
  27941. Each chapter can be digested as a single entity, allowing the reader an
  27942. arbitrary perusal. The content of the exercise questions follows logically
  27943. from each chapter's points; for instance, some of the chapter three exercises
  27944. ask you to extend program examples to arrays of four dimensions.
  27945. The book's preface lists the eight combinations of five machines and seven C
  27946. compilers in which all program examples were compiled and executed. The
  27947. execution results of program examples in specific combinations is a regular
  27948. part of the narrative. I was able to successfully compile select program
  27949. examples and exercise solutions on an Amdahl 580 under UTS and an AT&T PC 6300
  27950. under MS-DOS.
  27951. Chapter two's discussion of the runtime environment is valuable for
  27952. experienced programmers who are learning C; it may aid in preventing dangling
  27953. pointer bugs. The remainder of the book is primarily useful for experienced C
  27954. programmers, particularly those who are beyond "make it work" and are into the
  27955. "make it clean" and "make it fast" stages. A working knowledge of the routines
  27956. in a standard C library and their interfaces is also required. Proficiency in
  27957. binary arithmetic is needed to work through the bit-level operations. I would
  27958. also recommend a familiarity with UNIX shell commands and the UNIX System V
  27959. system call, signal(). (The authors neglect to identify the signal() in the
  27960. book as the System V, rather than Berkeley, version). I would have preferred
  27961. some header file examples from an operating system library other than UNIX.
  27962. Despite this bent toward the UNIX/XENIX family, the tips and techniques
  27963. presented in the book are beneficial across operating systems.
  27964. Advanced C: Tips and Techniques
  27965. Paul and Gail Anderson
  27966. Howard W. Sams & Co. 1988
  27967. $24.95, 446 pages.
  27968.  
  27969.  
  27970.  
  27971.  
  27972.  
  27973.  
  27974.  
  27975.  
  27976.  
  27977.  
  27978.  
  27979.  
  27980.  
  27981.  
  27982.  
  27983.  
  27984.  
  27985.  
  27986.  
  27987.  
  27988.  
  27989.  
  27990.  
  27991.  
  27992.  
  27993.  
  27994.  
  27995.  
  27996.  
  27997. Publisher's Forum
  27998. In this issue we again present -- as part of our continuing public service
  27999. program -- the winners of the International Obfuscated C Code contest (see Don
  28000. Libes' Column). As always, the winning entries are perversely clever and
  28001. entertaining -- and frightfully effective demonstrations that "clever and
  28002. entertaining" don't build understandability.
  28003. Least there be any misunderstanding, we do not encourage these obscure
  28004. programming practices. We publicize the results only as a public service -- to
  28005. give this dangerous form of expression a safe outlet. "Please, please, boys
  28006. and girls, don't attempt this trick at home. Remember these are highly trained
  28007. professionals." (We hope.)
  28008. I suspect an eastern mystic would take satisfaction from the pre-processor's
  28009. critical role in many of the winning entries. When used simply and directly,
  28010. the pre-processor contributes as much to understandability and maintainability
  28011. as any language component. But when purposely exploited -- when the
  28012. pre-processor's latent powers are stretched to achieve non-obvious ends --
  28013. understandability and maintainability are seriously damaged.
  28014. "Too much of a good thing" and all that.
  28015. We'll continue our public service campaign in the next issue by announcing the
  28016. winners of our Bad C Pun Contest. Like the obfuscated code contest, the bad
  28017. pun contest is intended as a "safety valve". Through this contest we were able
  28018. to capture and destroy several hundred distressingly bad C puns, hopefully
  28019. removing them from circulation, at least for a while.
  28020. An independent judge selected the most nearly humorous groaners for prizes. We
  28021. saved only the winners, and will share those with you in the next issue. But,
  28022. please don't expect much -- remember this was a "bad" pun contest, and bad
  28023. they were.
  28024. Sincerely yours,
  28025. Robert Ward
  28026. Editor/Publisher
  28027.  
  28028.  
  28029.  
  28030.  
  28031.  
  28032.  
  28033.  
  28034.  
  28035.  
  28036.  
  28037.  
  28038.  
  28039.  
  28040.  
  28041.  
  28042.  
  28043.  
  28044.  
  28045.  
  28046.  
  28047.  
  28048.  
  28049.  
  28050.  
  28051.  
  28052.  
  28053.  
  28054.  
  28055.  
  28056.  
  28057.  
  28058.  
  28059.  
  28060.  
  28061.  
  28062.  
  28063.  
  28064.  
  28065.  
  28066.  
  28067.  
  28068.  
  28069.  
  28070.  
  28071.  
  28072.  
  28073.  
  28074.  
  28075.  
  28076.  
  28077.  
  28078. New Products
  28079.  
  28080.  
  28081. Industry-Related News & Announcements
  28082.  
  28083.  
  28084.  
  28085.  
  28086. Expert Analyzes Code Quality
  28087.  
  28088.  
  28089. Conley Computing is shipping Codecheck, a rule based expert system that checks
  28090. C and C++ source code for maintainability, portability and compliance with
  28091. in-house style guidelines. Codecheck evaluates the portability of C to various
  28092. dialects, including ANSI, K & R, Harbison and Steele and C++. Codecheck has
  28093. been designed to target code for compatibility between PC-DOS, OS/2,
  28094. Macintosh, UNIX and VMS.
  28095. Codecheck also provides a statistical analysis of code complexity and style.
  28096. Versions are available for MS-DOS, Macintosh, OS/2, AIX, PC/IX, and QNX.
  28097. Priced from $495. Contact Conley Computing Corp., 7033 S.W. Macadam Ave.,
  28098. Portland, OR 97219. (503) 244-5253; FAX (503) 244-8375.
  28099.  
  28100.  
  28101. ProtoView Development Tool Produces Windows Code
  28102.  
  28103.  
  28104. ProtoView Development Corporation has released an application development tool
  28105. for C programmers working in the Microsoft Windows environment. The Protoview
  28106. Screen Management Facility includes a WYSIWYG screen painter and code
  28107. generator that produces source code, header, resource, definition and make
  28108. files for a complete, executable version of each screen. ProtoView contains a
  28109. dynamic link library of over 150 C language functions and a second dynamic
  28110. link library that contains nine types of editing controls.
  28111. With a single function call, table-driven, pop-up windows can be incorporated
  28112. into an application, including calculator-style data input, date, money,
  28113. string, real and security field objects. All fields can be edited. Fields can
  28114. be mandatory, protected, alphabetic, numeric, uppercase, range checked, choice
  28115. checked and even looked up in a table.
  28116. ProtoView works with any database or communications package that can be linked
  28117. under Windows. the package includes a 350-page manual, source code for all
  28118. field controls, and the source for building the dynamic link library of
  28119. controls.
  28120. ProtoView applications are SAA/CUA compatible and carry no runtime or
  28121. licensing fees. ProtoView requires the Microsoft Windows SDK, Microsoft
  28122. Windows 286 or 386 and Microsoft C v5.0 or later.
  28123. Price $595. Demo versions for $15. Contact ProtoView Development Co., 162
  28124. Kingdom Ave., New York, NY 10312 (718) 948-5195.
  28125.  
  28126.  
  28127. Panel Plus License Revised
  28128.  
  28129.  
  28130. Roundhill Computer Systems Limited have announced a new licensing policy for
  28131. their PANEL Plus II screen manager and screen library package. The new license
  28132. will include source code for the screen design editor and code generators.
  28133. Pricing for multi-user systems will be based upon the number of programmers
  28134. instead of the system architecture. Single user MS-DOS versions remain $495.
  28135. Contact Steve Hersee (USA) at (708) 690-3737, FAX (708) 665-9841 or Tim Frost
  28136. (UK) at 0672-84535; FAX 0672-84525.
  28137.  
  28138.  
  28139. C++ Class Library Supports Matrices
  28140.  
  28141.  
  28142. The M++ Matrix Class Library from Ansys Software Co., Inc., allows C++ users
  28143. to declare dynamic matrices or arrays. M++ matrix objects support the direct
  28144. manipulation of arrays, matrices or other groups of data, much as do symbolic
  28145. matrix languages like Matlab, GAUSS or APL, but retain all the portability and
  28146. speed advantages of C++.
  28147. M++ implements generalized submatrices, allowing a programmer to write and
  28148. manipulate general subsets of a given matrix or array, a feature particularly
  28149. useful to those developing systems utilizing time-series data. Submatrices
  28150. allow the programmer to view and manipulate data in alternate ways without
  28151. physically reordering, restoring or rereading the data from disk.
  28152. The class library handles and isolates memory allocation and provides
  28153. compile-time selectable bounds checking, aiding in the debugging of complex
  28154. programs.
  28155. The M++ library provides int, float, double, complex, submatrix, index and
  28156. decomposition matrix classes which can be extended, modified or limited via
  28157. inheritance. Also, full C++ source code is available to support direct
  28158. customization.
  28159. The M++ Matrix Class Library is available for MS-DOS, OS/2 and UNIX C++
  28160. compilers conforming to C++ versions 1.2 or 2.0. Prices start at $195. Contact
  28161. Ansys Software Co., Inc., 16950 151st Ave. SE, Renton, WA 98058 (800) 366-1573
  28162. or (206) 228-3170.
  28163.  
  28164.  
  28165. CASE Tool Adapted To Nets
  28166.  
  28167.  
  28168. Syscorp International has released MicroSTEP 1.4, a network version of its
  28169. STEP CASE tool. MicroSTEP produces networkable applications for Novell, IBM
  28170. token ring and Netbios networks. MicroSTEP's mouse-driven, graphic
  28171. specification environment includes integrated design tools to: build data flow
  28172. diagrams, specify data structures, layout screens, format reports, and
  28173. describe an application's computations and logic. The $6000 system (training
  28174. included) generates C. Contact Syscorp International, 9420 Research Blvd.,
  28175. Suite 200, Austin, TX 78759 (800) 727-7837.
  28176.  
  28177.  
  28178. LALR Updates Parser Generator
  28179.  
  28180.  
  28181. LALR research has released LALR v4.0, featuring: extended BNF with regular
  28182. expressions, operator precedence, and tree building notation; derivation
  28183. tracing of grammar conflicts; automatic symbol-table and abstract-syntax-tree
  28184. construction; smaller parser tables and faster parsing times; and fast
  28185. generated scanners. Contact LALR Research, 1892 Burnt Mill Rd., Tustin, CA
  28186. 92680. (716) 832-2274.
  28187.  
  28188.  
  28189. Expert Packaged As C Function
  28190.  
  28191.  
  28192.  
  28193. Hy-phen-ex, a hyphenation expert packaged as a C function, is now available
  28194. from GeoMaker Software. Hy-Phen-Ex applies over 4800 rules to (American)
  28195. English text to identify places where a word may correctly be divided.
  28196. Hy-Phen-Ex will even rank alternatives, if a word has more than one acceptable
  28197. dividing point.
  28198. The price is $89. Contact Geomaker Software, P.O. Box 273124, Concord, CA
  28199. 94527 (415) 680-1964.
  28200.  
  28201.  
  28202. Borland Opens Paradox Engine
  28203.  
  28204.  
  28205. Borland International has opened the Paradox Architecture to C programmers
  28206. with a new C library product, the Paradox Engine, that enables programmers to
  28207. build applications that create and access Paradox data. The Paradox Engine API
  28208. includes more than 70 functions (for single and multi-user environments) to:
  28209. create, read and write Paradox tables, records and fields; support multi-user
  28210. concurrency control; access tables sequentially or via indexes; and handle
  28211. security tasks.
  28212. The $495 package is expected to ship during first quarter and will be
  28213. available for $195 during a 90 day introductory period. Contact Borland at
  28214. 1800 Green Hills Rd., Scotts Valley, CA 95066.
  28215.  
  28216.  
  28217. Case Tool Supports Serial Terminals
  28218.  
  28219.  
  28220. CASET Corporation has released a new version of its Software Engineering
  28221. Toolkit (SET) that supports multiple, overlapping windows, buttons and dynamic
  28222. menus for color or monochrome serial devices. This release supports DEC VT and
  28223. Tektronix type terminals on Apollo, Digital, Hewlett-Packard, Silicon
  28224. Graphics, Sony and SUN CPUs, running Aegis, Ultrix, UNIX, or VMS.
  28225. SET supports prototyping, development and management of the user interface and
  28226. dialog portion of an application. Version 3.6 supports graphical interactive
  28227. user interface development, including complete window (stack, pop, move,
  28228. resize, copy, scroll, and delete) and dialog management, interface layout, and
  28229. code generation facilities on serial terminals. Form support includes
  28230. scrolling lists, buttons, toggles, and input type and range validation. User
  28231. interactions are managed through a context sensitive command interface which
  28232. can include a command line, pop-up, pull-out, or static menus, buttons, forms,
  28233. prompts, input validation, hierarchical help text, and a configurable
  28234. keyboard. An optional 2D/3D graphics system integrates into the windowing
  28235. system with zoom and pan functions managed by SET. All color, graphics, and
  28236. text features supported by the terminal can be accessed via SET.
  28237. SET generates C or Fortran code. Price $925. Contact CASET Corporation, 33751
  28238. Connemara Drive, P.O. Box 939, San Juan Capistrano, CA 92693 (714) 496-8670;
  28239. FAX (714) 661-5463.
  28240.  
  28241.  
  28242. Abraxas Releases More Toolkits
  28243.  
  28244.  
  28245. Abraxas Software is now shipping toolkits that allow COBOL, ADA, and FORTRAN
  28246. to be embedded into applications. The toolkits run in conjunction with Abraxas
  28247. Software's existing PCYACC and MACYACC products. Contact Abraxas Software,
  28248. 7033 S.W. Macadam Ave., Portland, OR 97219 (503) 244-5253; FAX (503) 244-8375.
  28249.  
  28250.  
  28251. Peritus Releases C++ Compiler
  28252.  
  28253.  
  28254. Peritus International has released an ANSI C and C++ compiler for 386/486 UNIX
  28255. systems.
  28256. The compiler offers switch-selectable support of K&R and ANSI dialects as well
  28257. as C++. Peritus C++ is implemented as a "true" compiler, rather than as a
  28258. pre-processor pass. Available code optimizations include: global register
  28259. allocation, constant propagation and folding, backward code motion with loop
  28260. invariant removal, induction variable elimination, redundant store and dead
  28261. code removal, and constant elevation.
  28262. Peritus has recently licensed its compiler technology to Amdahl Corporation
  28263. for use with Amdahl's UTS operating system. Peritus technology has previously
  28264. been selected by Apple, Control Data Corporation and Concurrent Computer
  28265. Corporation for use in proprietary compilers.
  28266. The Peritus C++ Compiler is currently available for 386/486 systems under SVR3
  28267. UNIX and SunOS 4.0 UNIX for $1000. Contact Peritus International, 10201 Torre
  28268. Ave., Suite 295, Cupertino, CA 95014 (408) 725-0882.
  28269.  
  28270.  
  28271. Avocet Integrates Embedded Tools
  28272.  
  28273.  
  28274. Avocet Systems, Inc., has announced AvCase, an integrated development
  28275. environment for embedded systems. AvCase includes an editor, C compiler,
  28276. assembler, linker, and simulator/source level debugger.
  28277. AvCase is scheduled to ship in February 1990. It runs on PC-clone and requires
  28278. no special hardware. The first release will target the Intel 8051 family.
  28279. Later releases will target 68HC11, 6801, Z80 and 68000 products.
  28280. Price for the entire package is $1895. Modules are also available separately.
  28281. Contact Avocet systems, Inc., 120 Union St., P.O. Box 490, Rockport, ME 04856
  28282. (800) 448-8500; FAX (207) 236-6713.
  28283.  
  28284.  
  28285. Computer Innovations Upgrades QNX Compiler
  28286.  
  28287.  
  28288. Computer Innnovations, Inc., has released a major upgrade of their compiler
  28289. for the QNX operating system. C86 v3.10 for QNX now includes dynamic linked
  28290. libraries, a source-level execution profiler, a quick make utility, an
  28291. intelligent diff file comparator, and a strip executable field minimizer. This
  28292. release also improves compiler efficiency, sourcel level debugging facilities,
  28293. documentation, and the libraries.
  28294. Dynamic linked libraries allow users to build programs that are smaller,
  28295. require less memory and load faster. Users can build their own dynamic
  28296. libraries as well as using C86 supplied libraries.
  28297. The source level profiler tracks source level constructs, including files,
  28298. modules, functions and lines.
  28299. Contact Computer Innovations Sales, 980 Shrewsbury Ave., Tinton Falls, NJ
  28300. 07724 (201) 542-5920.
  28301.  
  28302.  
  28303. Microsoft Ships OS/2 v2 SDK
  28304.  
  28305.  
  28306. Version 2.0 of the Microsoft OS/2 Software Development Kit (SDK) with
  28307. Presentation Manager is now being shipped. Though developed as a joint IBM and
  28308. Microsoft product, the pre-release is available through Microsoft. The $2,600
  28309. kit may be ordered directly from Microsoft by calling (800) 227-4679.
  28310.  
  28311.  
  28312. CSL Ported To SCO UNIX
  28313.  
  28314.  
  28315.  
  28316. CSL, a scientific programming library, is now available for SCO UNIX System
  28317. V/386. CSL includes linkable modules for linear algebra, eigensystems, matrix
  28318. computations, time series, smoothing, filtering and prediction, statistics,
  28319. regression, linear and integer programming, optimization, differential
  28320. equations, interpolation and curve fitting, and solutions for nonlinear
  28321. equations. Licensing options include single end-users, multi-users,
  28322. professional developers and site. Prices start at $295. Contact Eigenware
  28323. Technologies, 13090 La Vista Drive, Saratoga CA 95070 (408) 867-1184.
  28324.  
  28325.  
  28326. Planned Lattice Compiler Packages Will Include Dos Extender
  28327.  
  28328.  
  28329. Lattice, Inc., plans to release its new 80286 and 80386 C Development Systems
  28330. for MS-DOS and OS/2 on March 1 and April 1 respectively.
  28331. According to Dave Schmitt, Lattice president, "These new C compiler packages
  28332. will feature a complete programming environment, including a DOS extender,
  28333. compiler, assembler, debugger, editor, global optimizer, programming
  28334. utilities, and nearly 800 library functions."
  28335. The Lattice 80286 C Development System runs under MS-DOS, Extended DOS, or
  28336. OS/2 to create a single executable program which can run under MS-DOS,
  28337. Extended DOS, or OS/2. The package includes a royalty-free DOS Extender which
  28338. developers may include with their software at no charge. Lattice's DOS
  28339. Extender allows users to run programs of up to nearly 16 megabytes. An
  28340. included configuration optimizer performance tunes the DOS Extender.
  28341. Lattice's 80386 C Development System takes advantage of the 80386 and 80486's
  28342. 32-bit processing both for the compiler and generated programs. The system
  28343. runs under Lattice's 80386 Extended DOS, PharLap's Extended DOS, or OS/2 v2
  28344. and generates programs which run under Extended DOS or OS/2 v2. The compiler
  28345. is upwardly compatible so it will accept source code written for MS-DOS or
  28346. OS/2 v1.
  28347. These new compilers introduce Lattice's "Extended Family Mode." In this mode,
  28348. Schmitt explains, "a single program can run under either Extended DOS or OS/2.
  28349. Software developers only need to maintain one program even though their
  28350. customers use the program under several different operating systems."
  28351. The 80286 C Development System is priced at $495; the 80386 version at $900.
  28352. Contact Lattice, Inc., 2500 South Highland Ave., Lombard, IL 60148 (708)
  28353. 916-1600; FAX (708) 916-1190.
  28354.  
  28355.  
  28356. Library Processes 'Live Video'
  28357.  
  28358.  
  28359. Victor, a new C library from Catenary Systems, supports image processing
  28360. applications. The package operates on gray scale images from any source.
  28361. Victor includes image processing functions like sharpening filters, outline,
  28362. linearization, and matrix convolution. Among the video digitizer support
  28363. functions are functions to display 'live video' on a VGA adapter at rates
  28364. varying from two to 15 frames per second.
  28365. Victor also includes several resize functions, enabling applications to resize
  28366. images directly to a VGA, to the digitizer display, or to an image buffer.
  28367. Prices begin at $195. Contact Catenary Systems, 470 Belleview, St. Louis, MO
  28368. 63119 (314) 962-7833.
  28369.  
  28370.  
  28371. Debugger Works With Archimedes
  28372.  
  28373.  
  28374. A new version of Softaid, Inc.'s source level debugger now supports the
  28375. Archimedes v 3.0 C compiler. The debugger also supports Softaid's line of
  28376. in-circuit emulators. Price $795. Contact Softaid, Inc., 8930 Route 108,
  28377. Columbia, MD 2104 (800) 433-48812.
  28378.  
  28379.  
  28380. Tool Directs Methodology
  28381.  
  28382.  
  28383. Silico-Magnetic Intelligence has introduced Better-C, a coding methodology
  28384. manager and program generator.
  28385. According to the vendor, Better-C was developed with the assistance and
  28386. cooperation of C, structured programming, and AI experts. The Better-C
  28387. methodology incorporates complexity management, natural language naming,
  28388. top-down design, and object-orientation.
  28389. Objects in Better-C are created by an "open" and referenced via a handle, much
  28390. as are C files. Objects may be arbitrarily complex structures, such as trees,
  28391. lists, databases, or windows.
  28392. Better-C is compatible with all major compilers and runs on a PC-clone under
  28393. MS-DOS v2.0 or better. Price $98. Contact Silico-Magnetic Intelligence, 24
  28394. Jean Lane, Chestnut Ridge, NY 10952 (914) 426-2610.
  28395.  
  28396.  
  28397. Mi-Shell Sports Own Debugger
  28398.  
  28399.  
  28400. OPENetwork is set to release Mi-Shell, a configurable MS-DOS shell on April
  28401. 15, 1990. Mi-Shell's "point and shoot" interface is accompanied by a
  28402. FORTH-like script language (complete with debugger) which allows users to
  28403. define the display and actions to be executed when a key is pressed in a
  28404. certain environment. Price $89. Contact OPENetwork, 215 Berkeley Place,
  28405. Brooklyn, NY 11217 (718) 638-2240.
  28406.  
  28407.  
  28408. JDYX Releases UNIX Graphics
  28409.  
  28410.  
  28411. JDYX Enterprises is now shipping v3.0 of their 80386 UNIX Graphics Library, a
  28412. source code library supporting EGA/VGA/SVGA graphics on 80386 UNIX systems
  28413. including Interactive 386/ix, AT&T System V/386, and Xenix 386 v2.3.
  28414. The JDyx library supports twelve video modes -- through 800 x 600 x 16 and 360
  28415. x 480 x 256 on all VGA cards and through 640 x 400 x 256 on cards with the
  28416. Paradise chip-set. The routines support concurrent graphics applications on
  28417. different virtual terminals.
  28418. These routines do not use the BIOS or Xenix CGI interface, but directly access
  28419. the video card. Primitives such as point, line, solid, bibblt, ellipse and
  28420. clipping are supported, and all sixteen color routines have 12 different alu
  28421. operations. A bus mouse software cursor is also implemented. The library is
  28422. designed so that one binary can run on different adapters as well as in
  28423. different video modes.
  28424. Source licenses are $199, binary licenses $99. Contact JDyx Enterprises, 907
  28425. Tuxworth Circle, Decatur, GA 30033 (404) 320-7624.
  28426.  
  28427.  
  28428. Oregon C++ Now On Tower
  28429.  
  28430.  
  28431. NCR Corporation, Europe Group, and Oregon Software, Inc., have signed an
  28432. agreement to port Oregon C++ to the NCR Tower 32 Series. NCR will refer
  28433. customers for the $3000 package to Oregon and its distributors. Contact Oregon
  28434. Software, 6915 S.W. Macadam Ave., Suite 200, Portland, OR 97219 (503)
  28435. 245-2202; FAX (503) 245-8449.
  28436.  
  28437.  
  28438.  
  28439. Solution Systems Releases Brief 3.0
  28440.  
  28441.  
  28442. Solution Systems has released Brief v3.0, which includes a C-like macro
  28443. language and a translator to convert macros from the original LISP-like
  28444. syntax. Registered owners of Brief v2.1 can update for $70 plus shipping.
  28445. Contact Solution Systems, 541 Main Street, Suite 410, So. Weymouth, MA 02190
  28446. (800) 821-2492.
  28447.  
  28448.  
  28449.  
  28450.  
  28451.  
  28452.  
  28453.  
  28454.  
  28455.  
  28456.  
  28457.  
  28458.  
  28459.  
  28460.  
  28461.  
  28462.  
  28463.  
  28464.  
  28465.  
  28466.  
  28467.  
  28468.  
  28469.  
  28470.  
  28471.  
  28472.  
  28473.  
  28474.  
  28475.  
  28476.  
  28477.  
  28478.  
  28479.  
  28480.  
  28481.  
  28482.  
  28483.  
  28484.  
  28485.  
  28486.  
  28487.  
  28488.  
  28489.  
  28490.  
  28491.  
  28492.  
  28493.  
  28494.  
  28495.  
  28496.  
  28497.  
  28498.  
  28499.  
  28500.  
  28501.  
  28502.  
  28503.  
  28504.  
  28505.  
  28506. New Releases
  28507.  
  28508.  
  28509. CUG307 ADU & COMX (Device Driver)
  28510.  
  28511.  
  28512. Submitted by Alex Cameron (Australia), ADU is a disk utility program designed
  28513. to work with both the IBM PC standard and non-PC disk formats. By choosing an
  28514. option from the main menu, you can analyze the disk format, then read and
  28515. write the contents of the disk, sector by sector. The menu is also
  28516. user-configurable so that the disk parameters can be adapted to almost any
  28517. conceivable disk format. The initial alien disk parameters are derived by
  28518. scanning the disk and building up a disk_base table, which may then be
  28519. modified by the user. The disk includes C source code and well-written
  28520. documentation revealing the low-level detail of the PC's disk drive
  28521. configuration, not available anywhere else. The program is compiled under
  28522. Turbo C v2.0 or v1.5. No assembly is required.
  28523. Submitted by Hugh Daschbach (CA), COMX, an MS-DOS communication port device
  28524. driver, is an answer to a question posed by Jose Alfonso Corominas (Question &
  28525. Answers, CUJ November 1989, page 52). COMX provides buffered I/O to a serial
  28526. port with optional XON/XOFF flow control through standard read/write requests
  28527. or interrupt 0x14. The program uses mixed memory models. COMX.C is compiled
  28528. under the small model with explicitly declared far pointers and a front end
  28529. program forces the linkage editor to produce a tiny model executable. This
  28530. program is specifically written for Microsoft C (v5.0 or later) and some
  28531. assembly code comes with the C source code.
  28532.  
  28533.  
  28534. CUG308 MSU, REMZ & LIST
  28535.  
  28536.  
  28537. Dinghuei Ho (WA) has submitted MSU, an educational simulation of simple
  28538. computer organization and operation. MSU can simulate a computer that has a 4K
  28539. word memory space (each word is 32 bits), a CPU that includes four segment
  28540. origin registers (code segment, input segment, output segment, and workspace
  28541. segment), instruction register, program status register, a card reader and
  28542. line printer for input/output, and a clock. Using merely 10 basic
  28543. instructions, you can operate this computer and derive output. The program
  28544. runs under VMS on the Dec VAX 8820, but you can port it to other environments
  28545. by modifying the code.
  28546. Bob Briggs (CA) has submitted REMZ, the classic Parks-McClellan-Remez FIR
  28547. filter design program based on the FORTRAN version appearing in Theory and
  28548. Application of Digital Signal Processing by Rabiner & Gold (Prentice Hall).
  28549. The program compiles under Turbo C or Quick C.
  28550. Michael Kelly (MA) has submitted LIST, an object-oriented implementation of a
  28551. linked list using C. LIST is able to imitate C++ notation
  28552. (address_list.sort()) by defining a general structure whose fields are
  28553. pointers to functions, each corresponding to the operations of an object.
  28554.  
  28555.  
  28556. CUG309 6809 C Compiler for MS-DOS
  28557.  
  28558.  
  28559. Brian Brown (New Zealand) has ported CUG221 6809 C for FLEX to MS-DOS.
  28560. Modifications allow the program to run with ASxxxx assembler (CUG292), as well
  28561. as with Motorola AS9 assembler. The program also generates ROMmable code. The
  28562. disk includes a complete set of C source code, well-written documentation, and
  28563. a run-time library such as routines for controlling the ACIA serial port,
  28564. functions for character handling and data conversion between character strings
  28565. and integers, routines for controlling a Hercules card, routines for a
  28566. magnetic card reader, memory manipulation routines, PC serial card functions,
  28567. and string handling functions.
  28568.  
  28569.  
  28570.  
  28571.  
  28572.  
  28573.  
  28574.  
  28575.  
  28576.  
  28577.  
  28578.  
  28579.  
  28580.  
  28581.  
  28582.  
  28583.  
  28584.  
  28585.  
  28586.  
  28587.  
  28588.  
  28589.  
  28590.  
  28591.  
  28592.  
  28593.  
  28594.  
  28595.  
  28596.  
  28597.  
  28598.  
  28599.  
  28600.  
  28601.  
  28602.  
  28603.  
  28604.  
  28605.  
  28606.  
  28607. We Have Mail
  28608. Dear Mr. Ward,
  28609. I felt that some of the points raised by Phil Cogar in the letter published in
  28610. the Jan. '90 issue deserved a response, although I don't know whether such a
  28611. response fits within the subject range of your magazine.
  28612. Evidently, there is a market for language translation tools, at least three of
  28613. which are advertised in this issue at relatively reasonable prices. I hope to
  28614. get an opportunity to try some of them myself.
  28615. Rex Jaeschke has pointed out that C may not be the most cost-effective
  28616. language for development or maintenance of software which fits the design of
  28617. other languages, such as Fortran. Assuming that one has decided to use
  28618. software developed in another language as part of a system written in C,
  28619. several courses of action are available. These might include translating once
  28620. and discarding the original, modifying the original to the extent necessary to
  28621. maintain satisfactory parallel versions in two languages, or building a system
  28622. in more than one source language. Any combination of these methods might be
  28623. valid, and either of the first two would benefit from translation tools.
  28624. Considering the difficulties of translation and the undesirable practices
  28625. found in most freely available code, no translator can pretend to be able to
  28626. generate bug-free code. It is usually easier to compile the code in the
  28627. original language and verify operation with a few test cases, and maybe even
  28628. clean it up and retest it before translating.
  28629. One of the problems with a multi-language system is that the interfaces
  28630. between languages are not always satisfactory, never covered by any
  28631. nonproprietary standard, and unlikely to be subject to any of the usual safety
  28632. checks, such as lint. This often leads to errors, like my forgetting that I
  28633. was writing about a matrix set up by C rather than one set up by Fortran.
  28634. A compromise which often works well is to choose one language as the primary
  28635. one, with only low-level functions with simple calling interfaces written in
  28636. the other language. This may be no more than a minor extension of the way the
  28637. language system is actually written, as in the case of a Fortran runtime
  28638. library much of which is written in C.
  28639. On many modern systems, the C compiler has received the most attention of the
  28640. various languages, and it may generate more efficient code. In particular, the
  28641. amount of code required to set up loops seems to be consistently less in C,
  28642. and operations such as those required in searches and sorts are unlikely to be
  28643. optimized in early versions of compilers for other languages.
  28644. For some examples which do not particularly favor C, we may look at some old
  28645. Fortran coded problems. On the Multiflow Trace computer, 22 of the 24
  28646. Livermore loops run up to 10% faster in C than in Fortran, with the other two
  28647. running much faster in Fortran only through the use of compiler directives
  28648. (pragma) or in-line compilation of math functions. The Sun 4.0 C compiler is
  28649. able to compile linear searches through floating point arrays with code which
  28650. runs 40% faster than under their optional Fortran.
  28651. For those who wish to know what axes I am trying to grind, I am on the verge
  28652. of embarking on a project to support the SLATEC mathematical library in C and
  28653. Fortran in a way which should suit the needs of those who need source code at
  28654. a fair price to run on a variety of platforms. If we don't get approval from
  28655. the owners of the rights, we will be looking for alternatives. I am working
  28656. also on a series of hands-on learning seminars which will likely be presented
  28657. in 6 hour increments, beginning with application performance tuning for
  28658. pipelined architectures in C and Fortran, and UNIX familiarization for Fortran
  28659. programmers. All in addition to my job in aerodynamics design and computation.
  28660. Tim Prince
  28661. 39 Harbor Hill
  28662. Grosse Pte Fms, MI 48236
  28663. Just for your peace of mind, you are not alone. Researchers in the Advanced
  28664. Computational Resources Lab (I think I have the name almost right) at Argonne
  28665. National Labs are also interested in persuading scientists to develop
  28666. numerical applications in C -- in part because the most advanced parallel
  28667. hosts are first programmable in C. You might find their book Portable Programs
  28668. For Parallel Processors interesting (Boyle, Butler, et. al.). --rlw
  28669. Dear Mr. Ward,
  28670. I have my problems with recommending The Awk Programming Language by Aho,
  28671. Kernighan and Weinberger. It is an excellent reference to Awk, but is
  28672. confusing when one is working with the older versions (older than System V
  28673. v3.1). It brought tears of frustration until I happened to do a
  28674. tail /usr/bin/awk od -c
  28675. and came up with "(Berkeley) 9/16/83.". Clearly, an early version.
  28676. There is a very good simpler explanation of awk in the chapter "The Awk Power
  28677. Play" in UNIX Papers for System Developers and Power Users by the Waite Group,
  28678. Howard W. Sams & Co. Learning regular expressions at the awk level is best as
  28679. the regular expressions of "sed, grep, egrep, and fgrep" are subsets of this.
  28680. One cannot be sure exactly what Dr. Whitaker is trying to do, but I have found
  28681. that awk is ideal for extracting ASCII information from tables and that little
  28682. language is all that he might need.
  28683. UNIX tools are in a sense all "little languages" and this can explain their
  28684. lack of coverage in the literature. I doubt if an author could convince his
  28685. publisher that it would pay to cover these. So one must find information
  28686. wherever he can; appendices, mixed in with other coverages and "between the
  28687. lines." I have found that Howard W. Sams and The Waite Group are excellent in
  28688. their coverage of "UNIX-oriented tools". Advanced UNIX -- A Programmer's Guide
  28689. has an appendix on UNIX tools; other recommended are UNIX System V Bible,
  28690. Tricks of the UNIX Masters, and The UNIX Shell Programming Language give
  28691. innumerable examples.
  28692. Personally, I enjoy using UNIX tools in developing applications and turn to
  28693. the C language to do whatever I cannot accomplish otherwise. In other words
  28694. using other people's ideas before reinventing the wheel. However if somebody
  28695. can tell me how to document such a mixture, I would appreciate it.
  28696. Yours sincerely,
  28697. Alan E. Ternstrom
  28698. 5321 Perkins Rd. #122
  28699. Oxnard, CA 93033
  28700. The UNIX tools are undeniably neat; I just developed a full-function mailing
  28701. list package for my wife in about 30 lines of shell script (with about six
  28702. four- or five-line awk scripts). Unfortunately, documentation is only half the
  28703. problem. We're finding that since programmers must master six or seven fairly
  28704. complex and independent syntaxes that developing skilled maintenance
  28705. programmers for mixed tool applications is especially difficult -- the
  28706. increased "bump" at the beginning of the learning curve can easily frustrate a
  28707. newcomer. Anyone have some suggestions? --rlw
  28708. Dear Editor:
  28709. I would like to ask you to look at the opening two paragraphs of the Doctor
  28710. C's Pointers in the February issue.
  28711. I like the first paragraph. It matches my experience: Try a few things to see
  28712. if the concept will work and then, because of lack of time, build the rest of
  28713. the program in a stepwise fashion. It prepares me for what I would consider an
  28714. excellent article: How to pull hard coded definitions out of a program into
  28715. headers.
  28716. The second paragraph is in total opposition: "Headers must be done before any
  28717. code is written."
  28718. In fact, the article is rather good in its information while having almost
  28719. nothing to do with either paragraph. It certainly does not assist people
  28720. working per paragraph one, while not being as rigid as paragraph two.
  28721. I think the editor needed to do some editing here.
  28722. The article "Tools For MS-DOS Directory Navigation" by Leor Zolman contains at
  28723. least one serious error and a couple of misapprehensions.
  28724. The serious error is "there is no facility for viewing all active assignments"
  28725. with SUBST. This is simply wrong. Typing SUBST with nothing after it instantly
  28726. lists all current substitutions. I use it constantly.
  28727. The primary misapprehension is that it is desirable to change default drive
  28728. when CD is used. About half the time, I would then have to change back to my
  28729. original default. I get the feeling that Zolman works in an environment where
  28730. CD supports only one directory at a time (Apple ProDOS does this, maybe UNIX
  28731. does also), not the one per drive that MS-DOS provides.
  28732. While his choices are interesting, I am not sure a C program is needed, since
  28733. most of what he does can be done with a batch file in far fewer lines. I can
  28734. do his previous in the rather stable environment I work in most of the time by
  28735. using SUBST to simply create a new drive. I typically have 8 or 10 "drives"
  28736. specified on my system (118 directories.)
  28737. Mike Firth
  28738. 1019 Martinique
  28739. Dallas, TX 75223
  28740. Leor responds:
  28741. Yes, typing "subst" by itself does indeed display all active assignments. The
  28742. DOS manual for my system didn't happen to mention that little feature. I still
  28743. dislike "subst" for several reasons, however:
  28744. 1. After selecting a virtual drive defined via "subst", there are two
  28745. different notations for specifying the full pathname of any file on the
  28746. virtual drive (one using the "real" drive, one using the virtual drive) but no
  28747. way to access those portions of the file tree that reside "above" the base of
  28748. the virtual drive without reverting to "real" drive notation. This leaves
  28749. "subst" looking adequate to specify data paths for applications programs, but
  28750. not too intuitive for general-purpose file system navigation.
  28751. 2. After a virtual drive has been assigned with "subst", any redefinition of
  28752. that drive is prohibited by DOS. This makes the implementation of a
  28753. generalized "return to last directory" mechanism using "subst" seem impossible
  28754. (at least if you want it to be able to work more than once.)
  28755. 3. Finally, to be able to use "subst" at all, CONFIG.SYS must be changed and
  28756. the system re-booted.
  28757. Regarding my alleged "misapprehensions", please realize "cde" and "ret" are
  28758. meant to work in conjunction with the built-in "cd", not necessarily as
  28759. absolute replacements for it... "cd" may still be used anytime to change any
  28760. drive's default directory without disturbing the operation of cde/ret.
  28761. The philosophy behind cde/ret is simply to reduce the number of keystrokes
  28762. needed to move between directories, and to make DOS behave a little bit more
  28763. like UNIX; whether or not that is better, of course, boils down to a matter of
  28764. personal preference.
  28765. Dear Mr. Ward:
  28766. I would like to advise CUJ readers of a potential problem that may occur when
  28767. using Turbo C's integrated environment. I encountered this problem when I
  28768. combined completed modules of the system I worked on into one library. I used
  28769. the TLIB utility, and the resulting file was named CHELIBS.LIB. But after I
  28770. replaced numerous C-source and OBJect file names in the PRJ file with
  28771. CHELIB.LIB, I encountered a surprising reaction: my Turbo C integrated
  28772. environment (v2.0, tc.exe dated 8-29-88) failed to link the executable file
  28773. due to numerous unresolved external references (_setargv, _setenvp, and _exit,
  28774. among others).
  28775. After careful study, I learned that this error occured due to a bug in Turbo
  28776. C. Turbo C does not correctly handle LIB file names listed in the project
  28777. file. My library's name began with "CH" (like CH.LIB, Borland International's
  28778. library of huge memory models), and this file was interpreted by Turbo C as
  28779. its own library. Hence, CS.LIB (I used a small memory model) was not linked at
  28780. all. When I renamed my library to LIBCHES.LIB, all was well.
  28781. My research has shown me that users' libraries can't use titles beginning with
  28782. any of Borland's library names (i.e., CS..., CM..., CC..., CL..., or CH...).
  28783. I hope my report will be useful to Turbo C users, and I am happy to make a
  28784. contribution to CUJ, however small it may be.
  28785. Sincerely,
  28786. Alexander Vladimirovich Pavlov
  28787. Poste restante,
  28788. Central Telegraph Office
  28789.  
  28790. Moscow K-9
  28791. 103009 USSR
  28792. Thanks for the information. It's neat to get letters from the USSR. --rlw
  28793. Dear CUJ,
  28794. Simon Wheaton-Smith's letter (February 1990 CUJ) deserves patience and a
  28795. response. While his tone is fanatical, even fanatics have been known to have
  28796. insights.
  28797. I have found the C source code he placed on Compuserve (GO CLMFORUM, in the
  28798. OOP Alley library as OBJECT.C). In this example, he demonstrates that it
  28799. doesn't require extensions to C or a new language to provide encapsulation,
  28800. dynamic allocation or to place panels and buttons on the screen. I hope we're
  28801. ready to agree that these characteristics are not unique to object-oriented
  28802. programming.
  28803. There are additional features of C++ which Simon admits are absent from C but
  28804. which Simon suggests are best provided by a more robust preprocessor. These
  28805. features are overloaded functions and inheritance. One could argue that, in
  28806. fact, C++ is just such a preprocessor. Or, one could argue that Simon is
  28807. suggesting a new language, since a preprocessor can be considered a language.
  28808. We might call this language "C with overloaded functions and inheritance"
  28809. instead of C++ (formerly known as "C with objects").
  28810. I would argue against preprocessors in general. If Simon has developed in C
  28811. for MVS environments, then he is probably familiar with the C compiler IBM has
  28812. been marketing. I believe this product is still a preprocessor, producing
  28813. assembler code. C's limited I/O features have not been extended to match IBM's
  28814. access methods and so, more often than not, the assembler code is doctored to
  28815. produce the desired application. The original C code ends up being discarded
  28816. and you're developing an assembler application. As you would expect, there's
  28817. not much serious C development on MVS environments. (This situation should
  28818. eventually change now that IBM has endorsed C as one of the four SAA
  28819. languages, as Simon points out.)
  28820. This is the danger with preprocessors. Just as with C macros, only the
  28821. simplest of functions belong in preprocessor macros.
  28822. I am not particularly a fan of C + +. I agree with Simon, that C + + is "a
  28823. random collection of items". I've been pleased with its cautious reception.
  28824. But C + + does have a certain amount of promise -- given C + + and a robust
  28825. class library, we should be able to quickly produce terse programs.
  28826. This seems to be what we want from a development environment, and the
  28827. direction that modern languages should take -- rapid development in a brief
  28828. and clear style that produces efficient code. The language should be suitable
  28829. for group development. We want mechanisms which encourage (perhaps enforce)
  28830. reusable code design -- it's difficult to say that software can currently be
  28831. characterized in "generations". It remains to be seen if C++ can deliver on
  28832. these promises.
  28833. A note on efficiency -- Simon would lead us to believe that the programmer is
  28834. responsible for optimization. I, on the other hand, believe this to be a
  28835. cooperative effort between the programmer and his compiler. When we have a
  28836. language which permits a programmer to briefly and clearly describe what needs
  28837. to be done and a compiler which determines the efficient way to get it done,
  28838. we'll have a development environment we can stay with for a while.
  28839. Russ Klanke
  28840. 6840 Oswego Place NE #306
  28841. Seattle, WA 98115
  28842. Thanks for writing such a reasonable response.
  28843. Personally I don't agree that C++ is a random collection of items. I was
  28844. fortunate enough to sit in on a two-day C++ seminar by Bjarne Stroustrup a
  28845. couple of years ago. I was impressed with his justification and rationale for
  28846. the features included in C++. I think he's done a remarkable job of adapting
  28847. language features invented in a "protect the programmer from himself"
  28848. environment so that they fit reasonably in C's "you'd better know what you're
  28849. doing" world. -- rlw
  28850. Dear Mr. Ward:
  28851. When I was a child it was rather common to hear from one of your playmates(?)
  28852. the taunt "I know something you don't know." I am surprised to find it
  28853. continued in The C Users Journal in Mr. Brannigan's article on "Fitting Curves
  28854. to Data".
  28855. Mr. Brannigan states: "It is not difficult, for example, to input data for a
  28856. linear regression routine to a well known statistical package (which I shall
  28857. not name) used on micros and mainframes for which the output is incorrect."
  28858. Either name the offender or do not make the accusation. In the context I am
  28859. familiar with professionally, if you disagree with something that has been
  28860. claimed, you state your disagreement and give supporting evidence for that
  28861. disagreement but you do not make the type of accusation Mr. Brannigan has. In
  28862. my opinion Mr. Brannigan has by such behavior damaged only his credibility.
  28863. Sincerely,
  28864. Morton F. Kaplon
  28865. 1047 Johnston Dr.
  28866. Bethlehem, PA 18017
  28867. When I edited that story, I almost deleted the parenthetical remark to which
  28868. you refer, just because it wasn't really necessary. Perhaps I should have.
  28869. Had I read it as you did, I certainly would have deleted it. For me, as a
  28870. small publisher, I brought other assumptions to the table. I figured Brannigan
  28871. was just trying to spare me the wrath of some major software vendor. Had he
  28872. named the vendor, I would have been forced to verify the error before
  28873. publication or run the risk of being without defense against a potential libel
  28874. suite. Since the error itself wasn't critical to the story, I would probably
  28875. have deleted the comment instead of investing resources in doing "quality
  28876. control" for a product of minimal interest to my readers. Which is better,
  28877. having readers be placed on notice (albeit vague notice), or saying nothing at
  28878. all? --rlw
  28879. Re: Passing and Returning Objects in C++
  28880. In his article in the August, 1989, issue, Bruce Eckel gives an interesting
  28881. description on how values are passed to C++ functions. Unfortunately, he makes
  28882. a few errors which detract from his presentation.
  28883. First, he incorrectly describes passing arguments to a function by name. He
  28884. states that this is when a pointer is passed to a function. In fact, passing
  28885. an argument by name uses Algol's copy rule: the entire text of an argument is
  28886. reevaluated each time the name appears in the called function. This somewhat
  28887. resembles how arguments to C macros are handled.
  28888. C does not have call-by-name or call-by-reference. In C, only values are
  28889. passed. When a pointer is passed, it is passed by value. The programmer must
  28890. account for the fact that a pointer value is needed when the function is
  28891. called and that the value of the argument received is a pointer. This means
  28892. that an expression that is not an lvalue cannot be used as an argument for a
  28893. function expecting a pointer value.
  28894. C++ did extend C to add call-by-reference (but not call-by-name). While this
  28895. is most often implemented by passing a pointer to the function, it is not the
  28896. same. The programmer need not know whether a function is called by reference
  28897. or value: the call is the same and the argument need not be an lvalue.
  28898. He makes another error in stating that structure assignment is limited. The
  28899. example he uses
  28900. A=B=C;
  28901. works in ANSI C both for simple data types (int, float, etc.) and for
  28902. structures.
  28903. He also describes a method of returning a structure which can only be used
  28904. when the called function is not recursive. When the function is recursive,
  28905. space for the returned value of the structure is usually al