home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Journal 1990 - 1995 / CUJ.iso / unix / 1991.txt < prev    next >
Encoding:
Text File  |  1996-02-07  |  3.2 MB  |  99,905 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. Resources For Portable C Applications
  614.  
  615.  
  616. Thomas Plum
  617.  
  618.  
  619. Dr. Thomas Plum is President of Plum Hall Inc., a training firm that also
  620. supplies the leading ANSI C validation suite. Dr. Plum serves as Vice Chair of
  621. ANSI X3J11, the committee that developed the C standard.
  622.  
  623.  
  624. Let me begin with a bald assertion. Bald Assertion #1: As of 1991, a C source
  625. file should be either (a) written in non-portable ANSI/ISO Standard C for a
  626. specific environment (e.g. a device driver), or (b) written in strictly
  627. portable Standard C suitable for compilation and execution in any environment
  628. whatsoever. There is no justification for intermediate levels of "somewhat
  629. portable" code.
  630. This wasn't what I set out to write about, but it is the inescapable
  631. conclusion given current resources and projects. In this article, I will
  632. describe some of the resources that are likely to be important for your work
  633. on portable C projects. Contact address information is available at the end of
  634. the article.
  635.  
  636.  
  637. Books On Portability
  638.  
  639.  
  640. There are two comprehensive survey books on the line-by-line portability
  641. coding issues: Portable C, by Rabinowitz and Schaap, and Portability and the C
  642. Language, by Jaeschke. (See Resources at the end of this article.) Rabinowitz
  643. and Schaap's book traces its origins back to a seminar that Schaap and I wrote
  644. for Bell Labs in 1980. At the time Schaap was the first consultant for the
  645. fledgling Plum Hall Inc. The work has grown into a wealth of detail. It is
  646. very accurate information. The Jaeschke book is also extremely authoritative.
  647. A project leader responsible for portability should probably read both books.
  648. Nevertheless, both books have a point of view that I quarrel with. Besides the
  649. current C Standard, they cover multiple flavors of "old C" in great detail.
  650. These dozens (or hundreds?) of pages on "old C problems" are in themselves a
  651. convincing reason to stick to strict Standard C. The world could use a
  652. "Standard-only" version of each book. Aside from this, both books are a
  653. valuable condensation of practical advice from years of project experience.
  654. Also in the category of collected hints from prior experience is Plum Hall's
  655. own book, C Programming Guidelines, ("CPG," now in Second Edition for Standard
  656. C). Presented in the form of a project coding standard, CPG contains
  657. suggestions on data types, operators, and library functions for portable
  658. coding. CPG is available in an inexpensive machine-readable format to serve as
  659. the starting point for each project's own coding standard.
  660. The fundamental portability resource is, of course, the ANSI C Standard
  661. itself. It represents seven years of work by hundreds of C experts. The
  662. corresponding ISO Standard is expected to be official by the end of 1990,
  663. identical in every way but typographic layout.
  664.  
  665.  
  666. Portability Testing Software
  667.  
  668.  
  669. Another condensation of practical experience is the FlexeLint tool from Gimpel
  670. Software. This is a compile-time syntax analyzer that monitors adherence both
  671. to Standard C and to a large collection of portability-enhancing coding
  672. restrictions. For years, Gimpel has had an active customer base suggesting
  673. (and demanding) practical compile-time tests for portability. FlexeLint
  674. scrutinizes for portability problems involving the signedness of char,
  675. variations in character sets, sizes of integers, pointer casts, declarations
  676. and linkage, called-function vs. calling-function discrepancies, etc.
  677. I am especially fond of FlexeLint's ability to selectively disable specific
  678. tests. When it comes to heuristic coding rules, no two projects agree
  679. completely. I've used FlexeLint on several Plum Hall projects, and it works
  680. well.
  681. Similar capabilities are described for the C Portability Verifier from
  682. Mindcraft, but I haven't been able to test it yet.
  683. Because the ANSI C Standard is the only official Standard for C, I suggest
  684. another bald assertion. Bald Assertion #2: Any organization that cares about
  685. portable C will insist that its procurements require certified
  686. Standard-conforming compilers.
  687. Vendors who want to sell compilers to your project can obtain this
  688. certification from the European Compiler Testing Service (ECTS). This body
  689. includes the British Standards Institution (UK), AFNOR (France), and IMQ
  690. (Italy). (The US government standards agency, NIST, plans a different
  691. approach, probably available late 1991 or early 1992. More on this in another
  692. article.) ECTS uses the Plum Hall Validation Suite for C as its criterion for
  693. certification.
  694. I'll stake my professional reputation on this claim: If your compiler vendor
  695. truthfully claims to conform to the C Standard, then that vendor's product can
  696. pass the Plum Hall Suite and get officially certified. You do not have to rely
  697. upon advertising claims in your procurements. If customers insist upon
  698. Standard-conforming compilers, vendors will provide them. Several vendors
  699. already have been fully certified by ECTS. There are dozens of others who
  700. could get certified if customers demanded it.
  701. The Standard is central to the subject of C portability because it is the
  702. "treaty" between compiler vendors and programming projects. It defines exactly
  703. what capabilities the portable C program can expect from every environment.
  704.  
  705.  
  706. A Model Implementation
  707.  
  708.  
  709. The strictness of this treaty makes possible a new form of portability-testing
  710. tool. It is one that enforces every detailed requirement that the Standard
  711. imposes upon an application program. Such a tool is the C Model
  712. Implementation, commissioned and distributed by the British Standards
  713. Institution and produced by Knowledge Software Ltd.
  714. Basically, the C Model is an extremely fussy C implementation that
  715. syntax-checks your application for every compile-time requirement of the
  716. Standard. It also generates a form of intermediate p-code, runs the p-code
  717. through an equally fussy linker, then executes the program with diagnosis of
  718. every runtime "undefined behavior." The net result is that, if your program
  719. compiles and runs clean under the C Model, and you then compile and run it on
  720. an environment that conforms faithfully to the Standard, the program will
  721. compile and run clean there, too.
  722. Thus, the C Model is an interesting departure from the item-by-item
  723. accumulation of heuristics that characterized the previous resources
  724. (including my own C Programming Guidelines). It was produced, test-by-test,
  725. directly from the Standard itself.
  726. During this past year, the C Model has found more subtle non-portabilities in
  727. our Plum Hall Suite than did all the specific hardware environments. It
  728. demonstrates to its users that strictly portable C takes some discipline to
  729. produce, but also that it can be done.
  730.  
  731.  
  732. Shrink-Wrapped C
  733.  
  734.  
  735. Closely related in concept to the C Model is the Architecture-Neutral
  736. Distribution Format (ANDF) project of the Open Software Foundation (OSF). The
  737. goal here is to distribute "shrink-wrapped" applications that can be run
  738. without modification on any suitable target system. One industry standard
  739. assumed by ANDF is ANSI/ISO Standard C (as verified by the Plum Hall Suite).
  740. Another is the OSF/1 version of the UNIX operating system, which in turn is
  741. specified to meet the IEEE 1003 (POSIX) standard and the X/Open UNIX
  742. portability standard. OSF is currently conducting a competition (now down to
  743. four finalists) for a technology that will allow applications to be
  744. distributed in a "semi-compiled" form. Actual machine-specific code generation
  745. is postponed until the end-user performs the "install" process.
  746. Obviously, an application intended for shrink-wrap distribution must be
  747. totally free from dependencies upon the specific target architecture. Not
  748. quite so obvious is that, if shrink-wrap ANDF distribution works, the economic
  749. payoff for strictly portable applications will be greater than ever before. Of
  750. course, no one can say whether the aggregate UNIX market will ever approach
  751. the volume of the PC market. Still, many firms find it important to target
  752. both markets.
  753.  
  754.  
  755. The Bad News/Opportunity
  756.  
  757.  
  758. So far, this article has summarized a progression of good news, a historical
  759. trend of success at overcoming the problems of portably targeting C
  760. applications at different CPU architectures. These latest developments (a
  761. standard, a strict test suite, and a strict model implementation) make
  762. CPU-independent code a practical reality. Now for the bad news, or perhaps I
  763. should say the "opportunity." (To paraphrase Toshiro Mifune's Samurai in
  764. Yojimbo, "This looks like my kind of town.") The bad news is, most of the
  765. difficulties in producing portable applications now lie in the area of support
  766. environments, not in CPU architectures.
  767. In the remainder of this article, I will deal with two environment issues: (1)
  768. International character sets and languages, and (2) Graphical User Interfaces
  769. (GUIs). There are several other important environment issues besides these
  770. two. (For a more detailed list, see "Portability -- A No Longer Solved
  771. Problem," by Feldman and Gentleman.)
  772. As I mentioned before, the ISO Standard for C will be official by early 1991,
  773. and identical to ANSI C. But work is progressing on a Normative Addendum that
  774. will probably add support for multi-byte (e.g., Asian) character sets. When
  775. implemented, this Addendum will allow applications to manipulate "wide chars"
  776. of 16 or more bits rather than just one-byte characters. Any locale-specific
  777. work -- inputting, outputting, classifying, collating, comparing,
  778. string-handling -- is all handled by the ISO Addendum library, not the
  779. application itself.
  780. This standardization work by the ISO C working group builds upon the
  781. experience of several vendor-specific solutions. Hewlett-Packard has been
  782. building its Native Language System (NLS) since the mid-1980s. Digital
  783. Equpiment Corporation and AT&T have both added Kanji (Japanese character)
  784. support to UNIX. This means that applications can now be designed to be
  785. compiled into object code and later linked with nationality-specific
  786. libraries.
  787.  
  788. Soon, they can be semi-compiled into an ANDF, or even fully compiled and
  789. linked into executables that dynamically link with nationality-specific
  790. "shared library" modules at run time. Programs may one day adapt to
  791. nationalities and character sets that were unknown to the application
  792. designers and programmers.
  793. This adds a new dimension, and new complications, to the generality of
  794. portable software. Many application designers may desire to avoid these
  795. complications, but the international (i.e., outside USA) marketplace is
  796. growing too fast to ignore for long.
  797.  
  798.  
  799. A Portable GUI
  800.  
  801.  
  802. There is also a connection between "wide char" programming and Graphic User
  803. Interfaces. A GUI is the only framework that smoothly encompasses both the
  804. single-byte European character sets and the multi-byte Asian sets. One of the
  805. major difficulties about designing portable programs for a GUI is that
  806. environments vary widely: Macintosh, Windows, Presentation Manager, Motif,
  807. Open Look, etc.
  808. In my opinion, the most promising approach is the one taken by the Extensible
  809. Virtual Toolkit (XVT), the brainchild of Marc Rochkind. (He also developed the
  810. Source Code Control System, or SCCS, in his Bell Labs days.) XVT abstracts the
  811. common features of all these GUI environments so that your application can be
  812. written with one common GUI interface. (The interface also supports plain old
  813. character display terminals, so even anti-GUI mossbacks may find it
  814. interesting.)
  815. The generalized model in XVT includes:
  816. window -- clipping, scrolling, overlaps, coordinates, etc.
  817. context -- pen thickness, brush pattern, type face, etc.
  818. menu -- titles, hierarchy, keyboard equivalents, dispatch, etc.
  819. controls -- scroll bar, default button, check boxes, radio buttons, etc.
  820. dialog box -- modal or modeless, window specs, list of controls, etc.
  821. Designing a good portable interface is actually a form of object-oriented
  822. design. The purpose of the interface is to shelter the application from
  823. implementation details. This is the important design work for your portable
  824. application projects, the opportunity lurking in the bad news of the plethora
  825. of whizbang environments.
  826.  
  827.  
  828. Resources
  829.  
  830.  
  831. As promised, here is a summary of the resources I mentioned in this article.
  832. Henry Rabinowitz and Chaim Schaap, Portable C, PrenticeHall, 1990.
  833. Rex Jaeschke, Portability and the C Language, Hayden Books, 1988.
  834. Thomas Plum, C Programming Guidelines, Plum Hall, 1989.
  835. FlexeLint from Gimpel Software, 3207 Hogarth Lane, Collegeville PA 19426. Tel.
  836. 215-584-4261. Contact: Jim Gimpel.
  837. C Portability Verifier from Mindcraft, 410 Cambridge Avenue, Palo Alto CA
  838. 94306. Tel. 415-323-9000. Contact: Bruce Weiner.
  839. The ANSI C Standard, ANSI Sales Department, 1430 Broadway, New York NY 10018.
  840. Tel. 212-642-4900. Ask for X3.159-1989. Orders must be prepaid: $50 plus $6
  841. for UPS ground shipping. No credit cards.
  842. The ISO C Standard, ISO Committee SC22/WG14. Contact: P. J. Plauger, Convenor,
  843. 398 Main Street, Concord MA 01742. Tel. 508-369-8489.
  844. European Compiler Testing Service, British Standards Institution, POB 375,
  845. Linford Wood, Brecklands Avenue, Milton Keynes MK14 6L0, United Kingdom. Tel.
  846. 011-44908-220-908. Contact: Neil Martin.
  847. The Plum Hall Validation Suite for C, Plum Hall Inc., 1 Spruce Avenue, Cardiff
  848. NJ 08232. Tel. 609-927-3770. Contact: Thomas Plum.
  849. The C Model Implementation. See European Compiler Testing Service above.
  850. Architecture-Neutral Distribution Format (ANDF), OSF: The Open Software
  851. Foundation, 11 Cambridge Center, First Floor, Cambridge MA 02173. Contact: Sri
  852. Vasudevan.
  853. Stuart Feldman and W. Morven Gentleman, "Portability -- A No Longer Solved
  854. Problem," Computing Systems, Vol. 3, No. 2, Spring 1990, pp. 359-380.
  855. Jim Brodie, "Extended Multibyte Support," The Journal of C Language
  856. Translation, Vol. 2, No. 2, September 1990, pp. 133-140.
  857. David Radoff, "Toward a Global Operating Environment," CommUNIXations, Vol.
  858. 10, No. 6, August 1990, pp. 15-19.
  859. The Extensible Virtual Toolkit, XVT from XVT Software Inc., 1800 30th Street,
  860. POB 17665, Boulder CO 80308. Tel. 303-443-4223. Contact: Marc Rochkind.
  861.  
  862.  
  863.  
  864.  
  865.  
  866.  
  867.  
  868.  
  869.  
  870.  
  871.  
  872.  
  873.  
  874.  
  875.  
  876.  
  877.  
  878.  
  879.  
  880.  
  881.  
  882.  
  883.  
  884.  
  885.  
  886.  
  887. Some Thoughts On Portability
  888.  
  889.  
  890. Jack Purdum
  891.  
  892.  
  893. Jack Purdum is president of Ecosoft, Inc., and has authored several articles
  894. and books on C, including The C Programming Guide and a newly released book on
  895. Quick-C. Dr. Purdum can be reached at 8295 Indy Court, Indianapolis, IN 46214.
  896.  
  897.  
  898. It seems safe to say that C remains one of the most popular development
  899. languages available for personal computers. If you ask people who use C why
  900. they chose it over the alternatives, one reason often given is, "because C is
  901. a portable language." Perhaps a better statement would be, "C can be a
  902. portable language." Much has already been written about portability -- more
  903. than we can cover in one article. The purpose of this article is to discuss
  904. some portability problems that we've run up against in the past, and to
  905. discuss what we did (or should have done) to cope with those problems.
  906.  
  907.  
  908. What Is Portability?
  909.  
  910.  
  911. In his book Portability and the C Programming Language (Howard Sams), Rex
  912. Jaeschke defines portability as, "the degree to which a program or other
  913. software can be moved from one computer system to another."
  914. That is, how easy is it to successfully recompile the source code in a
  915. different environment? The "different environment" can take several different
  916. forms. For example, we might move the source code to:
  917. 1. a different operating system
  918. 2. a different CPU
  919. 3. a different memory model
  920. 4. a different compiler
  921. 5. a different combination of the above
  922. The permutations of the last item in the list are considerable. You might have
  923. the same operating system on different CPUs (e.g., UNIX on an 80486 or 68030),
  924. different operating systems on the same CPU (MS-DOS or XENIX on a 80386),
  925. different compilers on different CPUs and operating systems, and so on.
  926. To say that C will always fulfill the portability needs for all of the
  927. possible system combinations that the programmer might face seems a bit much
  928. to assume. Indeed, it seems unlikely that any non-trivial program can be moved
  929. between computing environments without some modification. The real issue,
  930. then, is, "What can I do to make the port less difficult?"
  931. It should be obvious that the more portable a program is, the less expensive
  932. it is to move the code to the new environment. The benefits accrue not only in
  933. getting the program up and running in the new environment, but also in the
  934. support of the program once the port is complete.
  935.  
  936.  
  937. Where Am I?
  938.  
  939.  
  940. In the past thirteen years, our firm has been involved with moving two rather
  941. large programs (each consisting of several million bytes of source code) to
  942. different environments. The ports involved different operating systems,
  943. different CPUs, and even a switch in languages during the port. When we
  944. switched programming languages, we had the freedom to redesign the program
  945. with portability in mind. (Alas, we weren't sure where the code would be
  946. ported to, only that it would likely be ported "somewhere.") With our other
  947. program, we were more or less locked into the design and had to port the
  948. program within existing code constraints.
  949. The degree to which your program is (or will be) portable is influenced by
  950. where you are in the development cycle. Writing portable code while the
  951. program is in the design stage is one matter; modifying existing code to be
  952. portable is quite another. In the remainder of this article, we will examine
  953. some of the things we did in both situations to improve program portability.
  954.  
  955.  
  956. Getting Organized
  957.  
  958.  
  959. If you have the luxury of designing a program with portability in mind, you
  960. have greater flexibility in writing portable code than if you are locked into
  961. an existing design. Almost any (nontrivial) program has five major parts, as
  962. shown in Table 1.
  963. Program initialization often involves reading a configuration file (containing
  964. information such as: drive or directory where the data are found, colors,
  965. fonts, video display information, setting or disabling interrupts, etc.),
  966. allocating dynamic memory, and a host of other tasks. Program input might
  967. involve reading the keyboard, a data file, or some other device that supplies
  968. data to the system. Processing is the manipulation of the data, while output
  969. sends the processed data to the desired output device. Finally, program
  970. termination involves any housekeeping tasks required by the program (e.g.,
  971. closing open files, freeing dynamic memory, updating configuration files, and
  972. so forth).
  973. A useful step in the design stage is a "sideways" refinement of the five basic
  974. elements listed in Table 1. For example, the Output element might be refined
  975. as shown in Table 2.
  976. This is a simple process that helps you to identify the type of functions that
  977. will be needed to display the program output. With a little additional
  978. thought, the process can help you identify those functions that will probably
  979. require hardware-dependent (i.e., non-portable) resources. In Table 2, it is
  980. probably inevitable that the clearscreen() and cursor control functions will
  981. use non-portable code.
  982. Having marked the non-portable functions, you should form a source code
  983. organization plan. Each of the primary program elements in Table 1 should have
  984. its own subdirectory. For example, the primary directory might be the program
  985. name or just PROJECT. The subdirectories might be INIT, INPUT, PROCESS,
  986. OUTPUT, and TERMINAL. In some cases, it may be desirable to have
  987. subdirectories below each of these directories. For example, under the OUTPUT
  988. subdirectory, we might have PORT and NOTPORT. This helps to keep the
  989. non-portable source code separate from the code that can be ported easily.
  990. Table 3 shows how a typical project might be laid out. (Only the OUTPUT
  991. directory shows the third layer of disk organization.)
  992. In the PROJECT directory, all five elements are brought together to form the
  993. completed application. Each of the five elements will have test code that is
  994. used to exercise that particular element of the program. If done properly,
  995. much of the code in these subdirectories will likely end up in their own
  996. library (LIB) files.
  997. An alternative is to simply place all of the non-portable source code into a
  998. single "non-portable" subdirectory. The important thing is for you to identify
  999. which elements are likely to be non-portable. Once the functions are
  1000. identified and separated as being non-portable, you can look for possible
  1001. alternatives that might allow you to recode them in a more portable fashion.
  1002. At the very least, knowing which functions are non-portable will make you more
  1003. aware of the types of resources you will need in the new environment.
  1004. Using a disk layout similar to that shown in Table 3 assumes that you have a
  1005. means by which you can quickly locate any given function definition. That is,
  1006. you will need a utility program that can read all source files in a
  1007. subdirectory and tell you the file and line number where each function
  1008. definition appears. An example of such a utility can be found in the February
  1009. 1989 issue of Computer Language or in the C Programmer's Toolkit (Que
  1010. Corporation). Something as simple as having an organized plan for the source
  1011. code and being able to locate a given function quickly can significantly
  1012. reduce development time.
  1013. While you are in the process of organizing things, you should limit all I/O
  1014. functions as much as possible. For example, in our statistics package, all
  1015. screen output goes through a single function. Although we didn't anticipate
  1016. it, this is going to prove to be a very smart move when (if?) we move the
  1017. program to Windows. The reason is because printf() is not usable in the
  1018. Windows (or almost any other GUI) environment. Had we used printf() throughout
  1019. the source code, it would take days of editing just to make this one change.
  1020.  
  1021.  
  1022. Know Your Resources
  1023.  
  1024.  
  1025. If you know the environment that you are moving to, it will pay to study the
  1026. programming tools that will be used in both environments. For example, when we
  1027. worked in the CP/M environment, the development tools we used supported
  1028. identifiers of up to eight characters. When we switched compilers, the
  1029. compiler still recognized the first eight characters as significant, but the
  1030. linker now only recognized the first six as being significant. We made the
  1031. mistake of assuming that both the compiler and linker recognized the same
  1032. number of significant characters. This little erroneous assumption cost us
  1033. almost two weeks, to reduce the variable names to the shorter length.
  1034. You should also exercise care in making assumptions about other resources and
  1035. features available to you in the two environments. For example, if the
  1036. existing code was written with a K&R compiler and the target machine supports
  1037. an ANSI compiler, certain resources and features may be missing. The K&R
  1038. compiler will support low-level (unbuffered) file I/O while the ANSI compiler
  1039. is only required to supply high-level (buffered) functions as part of the
  1040. standard library. Although present compilers support both types of file I/O,
  1041. it might prove costly to make the assumption in the future.
  1042. Another feature that may be missing (even on UNIX) is that some compilers do
  1043. not support function prototyping. If you find yourself faced with this
  1044. limitation, you will need to create a header file that lists the function
  1045. declarations with prototypes (using, for example, #ifdef ANSI) and without
  1046. (#else) prototypes. The same method can be used to toggle K&R or ANSI coding
  1047. style for function arguments in a function definition. Listing 1 shows an
  1048. example, with the corresponding header file in Listing 2.
  1049. Note that both listings use the K&R style of #ifdef rather than the ANSI #if
  1050. defined preprocessor directive. While the style is not particularly pleasing
  1051. to the eye, it does let you have the advantages of function prototyping in the
  1052. ANSI environment without modifying the source code. And, after all, the goal
  1053. is to support one body of source code for all environments.
  1054. Compiler differences between environments will also affect other coding
  1055. elements. For example, things like structure passing and assignment, certain
  1056. keywords (e.g., const, enum, far, near, void, volatile), and initialization of
  1057. auto arrays can be affected. Even the size of identical data structures may
  1058. vary because one machine may require byte alignment and the other doesn't.
  1059.  
  1060. Certain coding techniques should always be used, even if you don't plan to
  1061. port the code to another environment. Hopefully, you already use a number of
  1062. these coding techniques while others might be new to you.
  1063.  
  1064.  
  1065. Magic Numbers
  1066.  
  1067.  
  1068. Every C programmer is familiar with the use of #define to avoid using magic
  1069. numbers in a program. For example, define a macro:
  1070. #define TABLESIZE 50
  1071. and then use it as in:
  1072. for (i = 0; i < TABLESIZE; i++) {
  1073. printf("\n%d", number[i]);
  1074. }
  1075. This is a common coding practice. The advantage is that changes to the
  1076. number[] array are easily accounted for by a single change to the symbolic
  1077. constant TABLESIZE regardless of how many times TABLESIZE appears in the
  1078. source code.
  1079. While this approach reduces the amount of editing required in the source file,
  1080. a better solution is possible. For example:
  1081. int tablesize;
  1082. /* main() plus some other code */
  1083. tablesize = sizeof(number) / sizeof(number[0]);
  1084. for (i = 0; i < tablesize; i++) {
  1085. printf("\n%d", number[i]);
  1086. }
  1087. There are several advantages to using this construct instead of the #define.
  1088. First, any changes to the number of elements in the number[] array no longer
  1089. require further editing the source file. (Be honest. Have you ever miscounted
  1090. the number of elements in an array?) The program automatically adjusts to the
  1091. new size of the number[] array. Second, because tablesize is a variable, it
  1092. may be more useful with a source code debugger. Symbolic constants are often
  1093. lost to the debugger.
  1094. Another portability problem arises with macros used as bit masks. While the
  1095. macro
  1096. #define INTEGER_MASK 0x7fff
  1097. works fine on one system, it may fail miserably on a different system. One
  1098. reason is because the size of an integer may vary between machines. A second
  1099. reason might be due to the ordering of the integer in memory. (Is the high or
  1100. low byte stored first?) Related problems may occur with any bitwise operator
  1101. or manipulation of a bit field data item. I'm not sure if there is a totally
  1102. portable means to cope with such problems. You should, however, document such
  1103. items clearly in the source code.
  1104.  
  1105.  
  1106. Improper Use Of #define
  1107.  
  1108.  
  1109. In some cases, bad coding practices only show up after a port. One potential
  1110. problem is using a #define when a typedef is more appropriate. We know that
  1111. making the source code more readable is always a good idea. With that in mind,
  1112. we try something like:
  1113. #define INTERGER_POINTER int *
  1114. /* ....some code */
  1115. INTEGER_POINTER table;
  1116. After the preprocessor pass, the definition for table becomes:
  1117. int *table;
  1118. and the code works fine. However, in the process of making the port, you find
  1119. you need a second pointer in the new environment and you change the definition
  1120. statement to:
  1121. INTEGER_POINTER table, delta;
  1122. However, because a macro is a simple textual substitution, the statement
  1123. actually becomes:
  1124. int *table, delta;
  1125. The variable table is defined as a pointer, but delta remains a straight
  1126. integer. The proper solution is a typedef:
  1127. typedef int *INTEGER_POINTER;
  1128. Now the code works the way you intended it to work, even with multiple
  1129. identifiers.
  1130.  
  1131.  
  1132. String Constants
  1133.  
  1134.  
  1135. Some time ago we were asked if we wanted to have our statistics package
  1136. translated into a foreign language. While the benefits of the translation
  1137. probably would have been substantial, it required giving out the program
  1138. source code. Had we thought about foreign translations during the design
  1139. stage, we would have altered the way we handled string constants in the
  1140. program.
  1141. We should have written all of the string constants to be defined as an array
  1142. of pointers to char. For example:
  1143. char *message[] = {
  1144. "Select variable name:", /* Message # 0 */
  1145. "All or Subset (A, S):", /* 1 */
  1146. "Printer or File (P, F):" /* 2 */
  1147. /* More in the list */
  1148. "Out of memory" /* N */
  1149. };
  1150. First, this approach uses memory more efficiently because you can have
  1151. multiple occurrences of a constant without using additional memory. (If you
  1152. plan to port to Windows, this method is consistent with placing string
  1153. constants in a resource file.) Second, you can use the "tablesize" approach
  1154. discussed earlier to determine the size of the string table without using a
  1155. macro. Third, anyone who wanted to translate the program need have only a copy
  1156. of the string table, not the code itself. Finally, if a constant does need to
  1157. be changed, a single edit is all that is necessary to change every instance of
  1158. the constant throughout the program.
  1159. As you may know, many foreign countries use different formats for dates, time,
  1160. currency, and similar information. Although our packages don't have these
  1161. particular formatting requirements, they may well apply to your application.
  1162. If that is the case, you should examine the locale.h header file to see if any
  1163. of the symbolic constants defined there can be used to advantage in your code.
  1164. It may help make the port a bit easier.
  1165.  
  1166.  
  1167.  
  1168. Program Input
  1169.  
  1170.  
  1171. Another fortuitous design decision we made was to use a single function to get
  1172. all input from the keyboard. We felt we had to do this because we needed not
  1173. only to read ASCII keystrokes but also to detect the simultaneous pressing of
  1174. the function and shift keys. Porting the code to an environment like Windows
  1175. won't be easy, but at least all of the keyboard input is isolated to one
  1176. function.
  1177. While we're on the subject, it's been our experience that scanf() is not a
  1178. good function to use for data input. Not only is scanf() a huge function and
  1179. difficult to use properly, the function makes it difficult for your program to
  1180. sense input errors. Further, functions that rely on a terminating newline
  1181. character don't translate well in some environments. In Windows, for example,
  1182. pressing the Enter key is the same as clicking on the OK button.
  1183. Another problem is that the behavior of scanf() may vary among compilers. For
  1184. example, ANSI states that the e and g conversion characters are not case
  1185. sensitive, while System V doesn't specify how these conversion characters are
  1186. viewed. On the other hand, ANSI specifies that the l (for "long") modifier in
  1187. scanf() is case sensitive. For example, "%lf" differs from "%Lf". The latter
  1188. form is used to get a long double. In some environments, however, a long
  1189. double won't even be available in scanf() because that data type is not
  1190. supported (e.g., System V). Little details like these need to be investigated
  1191. if you still wish to use scanf().
  1192. A related problem arises when testing for the end of a user's input from the
  1193. keyboard, regardless of the function used to capture the input. Some compilers
  1194. use the newline character ('\n') to terminate input while others use the
  1195. carriage return character ('\r'). For example, the statement
  1196. if (buff[0] == '\n') {
  1197. /* The user didn't enter anything */
  1198. }
  1199. is often used to see if the user entered anything from the keyboard. The code
  1200. will work fine with one compiler but fail with a different compiler. If your
  1201. code tests for either of these character constants to sense end of input, you
  1202. may want to #define a symbolic constant rather than test for a specific
  1203. character. That is, if your compiler uses the carriage return to terminate
  1204. user input from the keyboard, the code would be more portable if you write
  1205. #define ENDOFINPUT '\r'
  1206. if (buff[0] == ENDOFINPUT) {
  1207. /* The user didn't
  1208. enter anything */
  1209. }
  1210. If data input is coming from a disk file, keep in mind that data sizes may
  1211. vary among environments. For example,
  1212. #define RECORD 50
  1213. fread(buf, RECORD, 1, fpin);
  1214. may not work properly. First, the data types themselves may not be the same
  1215. size. (Does an int require two or four bytes of storage?) Second, the system
  1216. may require the data items to be aligned in some special way. This may mean
  1217. that the data must be padded with extra bytes to fulfill the alignment
  1218. requirements. The moral here is: Don't use a symbolic constant when the sizeof
  1219. operator can accomplish the same task.
  1220.  
  1221.  
  1222. Numbers
  1223.  
  1224.  
  1225. We have already mentioned several instances where a difference in the size of
  1226. a data item can have an impact on the program. Most of these considerations
  1227. centered on the storage requirements (such as the size of an int). However,
  1228. the storage requirement for a data item is not the only way such differences
  1229. show up in a program. Clearly, a difference in storage requirements implies
  1230. that the numbers are capable of different numeric ranges. Indeed, even data
  1231. items with the same storage requirements can have a differing ranges of
  1232. values. For example, is the default for a char a signed or unsigned quantity?
  1233. If you are moving to an ANSI compliant compiler, the limits.h header file
  1234. should be helpful in answering such questions. If your present compiler does
  1235. not have the limits.h header file, you might want to consider writing your
  1236. own. Most compilers supply enough information about the data types that
  1237. creating your own limits.h header file is not very difficult. (If you haven't
  1238. done so already, you should examine the symbolic constants defined in this
  1239. header file.)
  1240. Another header file that may prove useful is float.h. It defines almost
  1241. everything you need to know about floating point variables (e.g., number of
  1242. digits of precision, floating point exceptions, exponent limits, etc.). One
  1243. potential problem area for programs that use floating point variables is the
  1244. epsilon factor for a floating point number. (This factor is called DBL_EPSILON
  1245. for type double.) The epsilon factor is the smallest value that can be added
  1246. to 1.0 and satisfy the condition:
  1247. 1.0 + DBL_EPSILON != 1.0
  1248. The value of epsilon can vary widely among compilers. Such wide variation can
  1249. produce bugs that are very difficult to track down. Most C programmers know
  1250. that testing a floating point variable against 0.0 can be dangerous because of
  1251. the epsilon factor. You should check the epsilon factor for both compilers to
  1252. see if they are similar in magnitude. If they are not, you may have to
  1253. incorporate the DBL_EPSILON constant into your code.
  1254.  
  1255.  
  1256. Odds And Ends
  1257.  
  1258.  
  1259. Without thinking about it very much, we developed the habit of documenting our
  1260. code, often using nested comments. In other cases, we often leave test code in
  1261. the source file and simply surround it with comment characters. However,
  1262. because we tend to comment the test code as well, we end up with nested
  1263. comments.
  1264. Some compilers do not allow nested comments. It is a real pain to "uncomment"
  1265. nested comments. We have since moved away from commenting out test code and
  1266. now use preprocessor directives to conditionally include the test code in the
  1267. program. For example, we used to comment out the code in the following manner:
  1268. /*
  1269. /* Inspect the values for
  1270. table[] */
  1271. for (i = 0; i < MAXSIZE; i++) {
  1272. printf("table[%d] = %g", i, table[i]);
  1273. }
  1274. */
  1275. This approach results in a nested comment. Still, we feel that the comments
  1276. are often useful and didn't want to simply throw them away. Now, we would
  1277. write the same debug code as:
  1278. #ifdef DEBUG
  1279. /* Inspect the values for
  1280. table[] */
  1281. for (i = 0; i < MAXSIZE; i++) {
  1282. printf("table[%d] = %g", i, table[i]);
  1283. }
  1284. #endif
  1285. If we need to turn on the test code, we can simple insert a #define DEBUG and
  1286. recompile the program to activate the debug code.
  1287. Finally, I'd like to present a brief laundry list of don'ts that can cause
  1288. problems in the middle of a code port.
  1289. Don't use lowercase l as a modifier to a long constant; it looks too much like
  1290. the digit 1. Use the uppercase L, as in
  1291. a = 20L;
  1292. Don't use a long double if you don't really need to. Some compilers don't
  1293. support it yet, plus is can add significantly to the data segment
  1294. requirements. While you're at it, check to see if float arithmetic is
  1295. supported. If your application can use the lower precision, it might prove
  1296. useful.
  1297. Don't assume a pointer is a fixed length, especially in a mixed-model
  1298. environment. Sometimes all data pointers are only two bytes, but function
  1299. pointers are four bytes.
  1300.  
  1301. Don't assume wildcard characters are universal across environments. A question
  1302. mark and asterisk may mean nothing on a different system.
  1303. Don't assume a filename is limited to a certain length.
  1304. Don't assume a NULL pointer means all bits are set to 0; it is implementation
  1305. defined. All you can safely assume is that the test for a NULL pointer behaves
  1306. in the normal way (even though the bits may be nonzero).
  1307. Don't use environment variables if you can avoid them. If you can't live
  1308. without them, make sure you mark those functions that use them.
  1309. Don't create your own symbolic constants if ANSI already provides for them
  1310. (e.g., DBL_EPSILON, SEEK_CUR, etc.).
  1311. As a final rule, if you follow most of the suggestions presented here, do take
  1312. the estimated time required for the port, double it, and try to live within
  1313. that time frame. C is not the perfect portable language, but it's way out in
  1314. front of whatever is in second place.
  1315. Table 1 The Five Basic Program Elements
  1316. 1. Initialization
  1317. 2. Input
  1318. 3. Processing
  1319. 4. Output
  1320. 5. Termination
  1321. Table 2 Sideways Refinement of the Output Element
  1322. Table 3 Disk Organization
  1323.  
  1324. Listing 1 Function Definitions for K&R and ANSI
  1325. #define ANSI 1
  1326. #include <stdio.h>
  1327. #include <myheader.h>
  1328.  
  1329. int main()
  1330. {
  1331. int i;
  1332.  
  1333. for (i = 0; i < 10; i++)
  1334. printf("\n%d", func1(i));
  1335. }
  1336.  
  1337. #ifdef ANSI
  1338.  
  1339. int func1(int i) /* For an ANSI compiler */
  1340.  
  1341. #else
  1342.  
  1343. int func1(i) /* For a K&R compiler */
  1344. int i;
  1345.  
  1346. #endif
  1347. {
  1348. return i * i;
  1349. }
  1350.  
  1351.  
  1352. Listing 2 A K&R and ANSI Header File
  1353. #ifdef ANSI
  1354.  
  1355. int func1(int i);
  1356.  
  1357. #else
  1358.  
  1359. int func1();
  1360.  
  1361. #endif
  1362.  
  1363.  
  1364.  
  1365.  
  1366.  
  1367.  
  1368.  
  1369.  
  1370.  
  1371.  
  1372.  
  1373.  
  1374.  
  1375.  
  1376.  
  1377.  
  1378.  
  1379.  
  1380.  
  1381.  
  1382.  
  1383.  
  1384.  
  1385.  
  1386.  
  1387.  
  1388.  
  1389.  
  1390.  
  1391.  
  1392.  
  1393.  
  1394.  
  1395.  
  1396.  
  1397.  
  1398.  
  1399.  
  1400.  
  1401.  
  1402.  
  1403.  
  1404.  
  1405.  
  1406.  
  1407.  
  1408.  
  1409.  
  1410.  
  1411.  
  1412.  
  1413.  
  1414.  
  1415.  
  1416.  
  1417.  
  1418.  
  1419.  
  1420.  
  1421.  
  1422.  
  1423.  
  1424.  
  1425.  
  1426.  
  1427.  
  1428.  
  1429.  
  1430.  
  1431.  
  1432. Portability Across MS-DOS C Compilers
  1433.  
  1434.  
  1435. Scott Robert Ladd
  1436.  
  1437.  
  1438. Scott Robert Ladd is a full-time free-lance writer with over 15 years computer
  1439. programming experience. Scott works mostly in C and C++ when he's not hiking
  1440. and shooting photos with his wife and daughter. He can be contacted at 3652
  1441. County Road 730 Gunnison, CO 81230. (303) 641-4219.
  1442.  
  1443.  
  1444.  
  1445.  
  1446. How Portable Is Portable?
  1447.  
  1448.  
  1449. Programs written in C are often touted as being more portable than those
  1450. written in other languages. To be very portable, a program should use only the
  1451. C language as defined by ANSI. After all, the ANSI standard exists to promote
  1452. portability.
  1453. Most MS-DOS programs, though, use low-level facilities that are not part of
  1454. the ANSI standard. Still, MS-DOS C compilers are remarkably similar. Language
  1455. extensions, such as the near and far keywords, are universally accepted with
  1456. only minor differences in meaning. It's the non-ANSI extensions such as
  1457. directory searching and hardware port I/O that are different from compiler to
  1458. compiler. Experience has taught me that these differences range from the minor
  1459. to the major.
  1460. Is portability among MS-DOS C compilers a mirage, then? No. When I publish C
  1461. programs, they need to be as generic as possible. Presenting a program that
  1462. will, say, compile only with Microsoft's QuickC will generate a torrent of
  1463. letters from Borland and Zortech enthusiasts who want a version for their
  1464. compiler. Necessity is the mother of invention. In this case, my invention was
  1465. a header file that allows me to create programs that automatically adjust to
  1466. the specifics of the C compiler being used. Using portable.h, I can write
  1467. system-level programs that will compile using C compilers from Microsoft,
  1468. Borland, Zortech, and WATCOM.
  1469.  
  1470.  
  1471. The Header portable.h
  1472.  
  1473.  
  1474. Listing 1 shows portable.h, a header file containing macros that ease porting
  1475. C programs among MS-DOS compilers. The file consists almost exclusively of
  1476. macro definitions that alias or replace compiler dependencies. There are four
  1477. sections in portable.h, each concerned with a different aspect of portability.
  1478. Every MS-DOS C compiler predefines a macro identifying itself. Microsoft C
  1479. defines the _MSC_VER macros, beginning with v6.0. Microsoft QuickC v2.5 and
  1480. later create the _QC macro. WATCOM C defines the __WATCOMC__ macro. Borland's
  1481. Turbo C and C++ define the __TURBOC__ macro. Zortech C and C++ use the __ZTC__
  1482. macros. You can use these macros in conditional preprocessor statements to
  1483. select compiler-specific characteristics.
  1484. The first macro definition conditionally creates a proper version of the MK_FP
  1485. macro. You use this macro to create a far (32-bit) pointer from 16-bit segment
  1486. and offset values. MK_FP is found in most compiler libraries, but Microsoft's
  1487. C compilers do not define it. If portable.h does not find a MK_FP macro, it
  1488. creates one.
  1489.  
  1490.  
  1491. Searching Directories
  1492.  
  1493.  
  1494. The next section of the include file defines macros and types used in
  1495. searching MS-DOS file directories. Working with directory search functions
  1496. under MS-DOS can be frustrating. Each compiler vendor uses unique function and
  1497. structure identifiers. Macros to the rescue! The MS-DOS file data structure
  1498. has the same format for all of the compilers, even if the names of the
  1499. structure and its members are different. I therefore created my own data
  1500. structure, DOSFileData, that can be used in place of the structures peculiar
  1501. to each compiler. The function macros that follow cast a pointer to a
  1502. DOSFileData structure to the specific structure type supported by a specific
  1503. compiler.
  1504. How does a program work with the compiler-specific function names and
  1505. parameter lists? The solution is to define macros that translate between
  1506. compilers. The FIND_FIRST macro hides a compiler's implementation of the
  1507. MS-DOS 0x4E (find first file) function. FIND_NEXT is an alias for the function
  1508. call to the MS-DOS 0x4F (find next file) function.
  1509.  
  1510.  
  1511. Accessing I/O Ports
  1512.  
  1513.  
  1514. Aliases also help you perform I/O to ports. Bytes and words can be written to
  1515. and read from the I/O ports in your PC. Access to I/O ports is required for
  1516. video, serial, and other hardware-level programming tasks. The names for port
  1517. I/O functions are consistent across most compilers -- only Borland uses unique
  1518. names. So I created the macros IN_PORT, IN_PORTW, OUT_PORT, and OUT_PORTW as
  1519. aliases for the actual function names.
  1520. Borland's Turbo C and C++ support pseudoregister variables and inline
  1521. interrupt calls. The assignment _AX = 1 directly assigns 1 to the AX processor
  1522. register. The geninterrupt function compiles to an inline INT instruction.
  1523. Obviously, Borland's ability to directly load registers and call system BIOS
  1524. and MS-DOS services is a performance booster. Other MS-DOS C compilers don't
  1525. support these language extensions.
  1526. That would normally preclude the use of pseudoregisters in "portable"
  1527. programs. But again, macros can be used to solve the problem. In the last
  1528. section of portable.h, a macro is defined for each pseudoregister variable
  1529. when a non-Borland compiler is begin used. The pseudoregister is replaced by
  1530. an assignment to a member of a struct REGS named CPURegs. Calls to
  1531. geninterrupt are replaced with calls to int86. I've successfully used these
  1532. macros in several Turbo C-specific programs to allow the code to migrate to
  1533. other C compilers.
  1534.  
  1535.  
  1536. Conclusion
  1537.  
  1538.  
  1539. I've found portable.h to be useful both in presenting articles and doing
  1540. consulting work. I can develop a generic program using one compiler and be
  1541. able to use that program with another C compiler. I'm sure you'll find
  1542. extensions that can cover other compiler inconsistencies. Have fun!
  1543.  
  1544. Listing 1
  1545. /*======================================================================
  1546. portable.h v1.00 Written by Scott Robert Ladd.
  1547.  
  1548. _MSC_VER Microsoft C 6.0 and later
  1549. _QC Microsoft Quick C 2.51 and later
  1550. __TURBOC__ Borland Turbo C and Turbo C++
  1551. __ZTC_ Zortech C and C++
  1552. __WATCOMC__ WATCOM C
  1553. =========================================================================*/
  1554.  
  1555.  
  1556. /* prevent multiple inclusions of this header file*/
  1557. #if !defined(PORTABLE_H)
  1558. #define PORTABLE_H
  1559.  
  1560. /*-----------------------------------------------------------------------
  1561. Pointer-related macros
  1562.  
  1563. MK_FP creates a far pointer from segment and offset values
  1564. ------------------------------------------------------------------------*/
  1565.  
  1566. #if !defined(MK_FP)
  1567. #define MK_FP(seg,off) ((void far *)(((long)(seg) << 16)(unsigned)(off)))
  1568. #endif
  1569.  
  1570. /*-----------------------------------------------------------------------
  1571. Directory search macros and data structures
  1572.  
  1573. DOSFileData MS-DOS file data structure
  1574. FIND_FIRST MS-DOS function 0x4E -- find first file matching spec
  1575. FIND_NEXT MS-DOS function 0x4F -- find subsequent files
  1576. -----------------------------------------------------------------------*/
  1577.  
  1578. /* make sure the structure is packed on byte boundary */
  1579. #if defined(_MSC_VER) defined(_QC) defined(__WATCOMC__)
  1580. #pragma pack(1)
  1581. #elif defined(__ZTC__)
  1582. #pragma align 1
  1583. #elif defined(__TURBOC__)
  1584. #pragma option -a-
  1585. #endif
  1586.  
  1587. /* use this structure in place of compiler-defined file structure */
  1588. typedef struct
  1589. {
  1590. char reserved[21];
  1591. char attrib;
  1592. unsigned time;
  1593. unsigned date;
  1594. long size;
  1595. char name[13];
  1596. }
  1597. DOSFileData;
  1598.  
  1599. /* set structure alignment to default */
  1600. #if defined (_MSC_VER) defined(_QC) defined(__WATCOMC__)
  1601. #pragma pack()
  1602. #elif defined(__ZTC__)
  1603. #pragma align
  1604. #elif defined(__TURBOC__)
  1605. #pragma option -a.
  1606. #endif
  1607.  
  1608. /* include proper header files and create macros */
  1609. #if defined(_MSC_VER) defined(_QC) defined(__WATCOMC__)
  1610. #include "direct.h"
  1611. #define FIND_FIRST(spec, attr, buf) \
  1612. _dos_findfirst(spec, attr, (struct find_t *)buf) \
  1613. #define FIND_NEXT(buf) _dos_findnex((struct find_t *)buf)
  1614.  
  1615. #elif defined(__TURBOC__)
  1616. #include "dir.h"
  1617. #define FIND_FIRST(spec, attr, buf)\
  1618. findfirst(spec, (struct ffblk *)buf, attr)
  1619. #define FIND_NEXT(buf) findnext((struct ffblk *)buf)
  1620. #elif defined(__ZTC__)
  1621. #include "dos.h"
  1622. #define FIND_FIRST(spec, attr, buf) \
  1623. dos_findfirst(spec, attr, (struct DOS_FIND *)buf)
  1624. #define FIND_NEXT(buf) dos_findnext((struct DOS_FIND *)buf)
  1625. #endif
  1626.  
  1627. /*-----------------------------------------------------------------------
  1628. I/O Port Macros
  1629.  
  1630. IN_PORT read byte from I/O port
  1631. IN_PORTW read word from I/O port
  1632. OUT_PORT write byte to I/O port
  1633. OUT_PORTW write word to I/O port
  1634. -----------------------------------------------------------------------*/
  1635.  
  1636. #if defined(__TURBOC__)
  1637. #include "dos.h"
  1638. #define IN_PORT(port) inportb(port)
  1639. #define IN_PORTW(port) inport(port)
  1640. #define OUT_PORT(port,val) noutportb(port,val)
  1641. #define OUT_PORTW(port,val) outport(port,val)
  1642. #else
  1643. #include "conio.h"
  1644.  
  1645. #define IN_PORT(port) inp(port)
  1646. #define IN_PORTW(port) inpw(port)
  1647. #define OUT_PORT(port,val) outp(port,val)
  1648. #define OUT_PORTW(port,val) outpw(port,val)
  1649. #endif
  1650.  
  1651. /*-----------------------------------------------------------------------
  1652. Borland psuedo register macros
  1653.  
  1654. These macros replace references to Borland's psuedo register
  1655. variables and geninterrupt() function with traditional struct
  1656. REGS/int86 references.
  1657. -----------------------------------------------------------------------*/
  1658.  
  1659. #if !defined(__TURBOC__)
  1660. #include "dos.h"
  1661.  
  1662. struct REGS CPURegs;
  1663.  
  1664. #define _AX CPURegs.x.ax
  1665. #define _BX CPURegs.x.bx
  1666. #define _CX CPURegs.x.cx
  1667. #define _DX CPURegs.x.dx
  1668.  
  1669. #define _AH CPURegs.h.ah
  1670. #define _AL CPURegs.h.al
  1671. #define _BH CPURegs.h.bh
  1672. #define _BL CPURegs.h.bl
  1673. #define _CH CPURegs.h.ch
  1674.  
  1675. #define _CL CPURegs.h.cl
  1676. #define _DH CPURegs.h.dh
  1677. #define _DL CPURegs.h.dl
  1678.  
  1679. #define geninterrupt(n) int86(n,&CPURegs,&CPURegs);
  1680. #endif
  1681.  
  1682. #endif
  1683.  
  1684.  
  1685.  
  1686.  
  1687.  
  1688.  
  1689.  
  1690.  
  1691.  
  1692.  
  1693.  
  1694.  
  1695.  
  1696.  
  1697.  
  1698.  
  1699.  
  1700.  
  1701.  
  1702.  
  1703.  
  1704.  
  1705.  
  1706.  
  1707.  
  1708.  
  1709.  
  1710.  
  1711.  
  1712.  
  1713.  
  1714.  
  1715.  
  1716.  
  1717.  
  1718.  
  1719.  
  1720.  
  1721.  
  1722.  
  1723.  
  1724.  
  1725.  
  1726.  
  1727.  
  1728.  
  1729.  
  1730.  
  1731.  
  1732.  
  1733.  
  1734.  
  1735.  
  1736.  
  1737.  
  1738. Pennies In Long Double
  1739.  
  1740.  
  1741. Timothy Prince
  1742.  
  1743.  
  1744. Timothy Prince has a B.A. in physics from Harvard and a Ph.D. in mechanical
  1745. engineering from the University of Cincinnati. He has 25 years of experience
  1746. in aerodynamic design and computation. He can be contacted at 39 Harbor Hill
  1747. Dr., Grosse Pointe Farms, MI 48236.
  1748.  
  1749.  
  1750.  
  1751.  
  1752. Coprocessor Problems
  1753.  
  1754.  
  1755. In a letter to The C Users Journal some time ago, Paul Wexler pointed out some
  1756. confusing results that developed while dealing with dollars and cents on
  1757. numeric coprocessors with extended double precision registers. He showed that
  1758. the Microsoft C v5.1 and Turbo C compilers treated the code:
  1759. double d = 0.99;
  1760. int i = (d *= 100.0);
  1761. as if the second line were written:
  1762. register long double dtemp = (long
  1763. double) d*100.0;
  1764. d = dtemp;
  1765. int i = dtemp;
  1766. with the results d==99.0 and i==98!
  1767. According to K&R or ANSI C, the original code should give the same results as:
  1768. d *= 100.0;
  1769. int i = d;
  1770. So the most popular C compilers conform neither to K&R nor to ANSI! Paul tried
  1771. the same code on some other systems and got correct results.
  1772. I tried it on the Sun 4.0 compiler and found that
  1773. f77 -f68881
  1774. gave the same non-K&R results as Paul had obtained with the Microsoft and
  1775. Turbo compilers, but
  1776. f77 -f68881 -02
  1777. generated a code expansion equivalent to:
  1778. i = d = (long double)d*100.0;
  1779. which gives the correct results.
  1780. The value of 0.99 rounded to the IEEE standard double precision format happens
  1781. to be slightly less than 99/100. (long double)d*100 is less than 99, but if
  1782. rounded again to standard double precision, the result is exactly 99.
  1783.  
  1784.  
  1785. Normal Surprises In Floating Point
  1786.  
  1787.  
  1788. Most of us at some time have written something like
  1789. for(x=0.01; x!=l.0; x+=0.01)
  1790. .....
  1791. Because there is no exact binary equivalent to 0.01, the conditional is always
  1792. true. The code might work on a system without extended precision, but is
  1793. likely to hang on 80X87 or 68881 processors. If the condition is changed to
  1794. x<=1.0, the number of iterations will vary by 1 among various systems. Change
  1795. the counter to integral values:
  1796. for(xx=1.; xxd; ++xx) {
  1797. x=xx*0.01;
  1798. ....}
  1799. and it will run portably on all systems with correct floating point
  1800. implementation, no matter what type is given for xx. On many systems, it will
  1801. run faster if the types of x and xx are the same.
  1802.  
  1803.  
  1804. Try It On Your Own Machine
  1805.  
  1806.  
  1807. I carried Paul's example a bit further. In every case, the result for i (in
  1808. his example) is either the same as the result j or the result k (when
  1809. compilers take liberties with the standards,) but the k may be less than i
  1810. when varying precisions of floating point arithmetic are used. In single
  1811. precision or PDP-11 style double precision, the value of 0.99 is greater than
  1812. 99/100, and a series of other values must be tried to verify the adherence of
  1813. the compiler to the standard.
  1814. C libraries should be able to convert output to the 17 or 18 digits of
  1815. precision that are required to show the difference between the binary
  1816. approximation and the original decimal value. Any binary floating point number
  1817. may be expressed exactly in decimal, but there is no practical reason or means
  1818. for going beyond the number of digits required to specify a unique binary
  1819. value. Converting the other way, most fractional decimal values have no exact
  1820. equivalent binary fractional value. If properly programmed, the numeric
  1821. coprocessors can perform the conversions reversibly to the extent demanded by
  1822. the IEEE standards. In plain double precision there will be small deviations.
  1823. The inexact converson from decimal to binary is sometimes taken as an argument
  1824. for using decimal arithmetic in applications such as financial calculations.
  1825. However, practical applications of this kind generally have a fixed number of
  1826. fractional decimal places. It should make no difference to the result whether
  1827. binary or decimal arithmetic is used.
  1828. You may wish to see the hexadecimal pattern of the binary floating point
  1829. number. Since the %x conversion is not available for double, you must use
  1830. unions or pointer casts. Converting the value byte by byte avoids some of the
  1831. confusion introduced when the byte storage order is reversed for long or
  1832. double. K&R compilers may have no unsigned char, so the 0xff mask is used to
  1833. compensate. %lx conversion would be expected to work on float if
  1834. sizeof(float)==sizeof(long) and byte orders are not of concern. When you run
  1835. the code, look for the repeating patterns in the hex representation for
  1836. evidence of rounding. When the pattern that is chopped off is greater than
  1837. 0x80..., there should be upward rounding.
  1838.  
  1839. If your system does not have extended precision, double rounding (after divide
  1840. and again after subtract) occasionally will give an incorrect rounding. This
  1841. occurred only once in the hex output on my test using Z80 software floating
  1842. point. The same effect reduces the number of different values that printf()
  1843. can produce, making numbers appear to be exact numbers of cents when they
  1844. aren't. This violates the IEEE standard but is normal without extended
  1845. precision.
  1846.  
  1847.  
  1848. Why Do The Gurus Do This To Us?
  1849.  
  1850.  
  1851. In the design of the 8087 and 68881 numeric coprocessor families, the number
  1852. of instructions required was reduced by choosing one data type (register long
  1853. double) for the results of all floating point operations. This was a logical
  1854. extension of the traditional C scheme of promoting all float arguments and
  1855. expressions to double. In normal operation, it often gives an extra decimal
  1856. digit of accuracy and avoids most accidents caused by exceeding the range
  1857. provided for double, which was small enough to require special precautions on
  1858. older hardware.
  1859. Since the coprocessors give an incentive in accuracy as well as speed for
  1860. allocation of variables to registers, numerical results may be expected to
  1861. differ on these machines from the values obtained on other architectures. In
  1862. fact, by literal interpretation of the IEEE floating point standard, these
  1863. coprocessors may give incorrect results in double precision. All operations
  1864. are rounded first to 64 bits precision (plus a 15-bit exponent and a sign) and
  1865. then to 53 bits precision (with 11-bit exponent and hidden bit allowing room
  1866. for the sign).
  1867. In effect, the coprocessor rounding threshold varies from about 0.4998 to
  1868. 0.5002 of the unit in the last place (ULP) of a double. Theoretically, this
  1869. could cause the same kind of non-standard behavior, but the possibility is
  1870. remote and over-shadowed by effects of the magnitude of 0.25 ULP such as those
  1871. which we have been discussing. Typical Z80 floating point software routines
  1872. have a rounding threshold which varies from 0.49 to 0.51, and still give
  1873. excellent accuracy.
  1874. You'll hardly notice whether a value that is assigned and then used for
  1875. subsequent calculation is rounded off, unless you push your luck. Meanwhile,
  1876. with the benchmark wars continuing, the compiler writers are looking for every
  1877. chance to maximize the use of register variables. Certain optimizing
  1878. compilers, given Paul's example code, will perform all the calculations at
  1879. compile time, leaving nothing but the printf calls, and no record of the
  1880. sequence of operations used to obtain the constant results.
  1881.  
  1882.  
  1883. Are The Standards Correct Or Is My Compiler Right?
  1884.  
  1885.  
  1886. There might be a case for allowing compilers to ignore casts and assignments
  1887. that reduce the width of expressions, but when I write code such as
  1888. int ix;
  1889. double x;
  1890. x -= (ix = ((x=0.0) ? 0.5 : -0.5) + x);
  1891. I expect x to come out such that x += ix would restore the original x. This
  1892. wouldn't work if the double expression were allowed to leapfrog over the
  1893. assignment to ix. When something from the integer family is involved, most of
  1894. us would agree with the standard treatments. The Motorola coprocessors can
  1895. evaluate this expression without reading data back from memory, so there is
  1896. less likelihood of cheating to obtain faster code. On the Intel processors,
  1897. the result that I intended could be produced by
  1898. register long double xtemp = RINT(x);
  1899. /* old Intel FORTRAN function */
  1900. ix = xtemp;
  1901. x - = xtemp;
  1902. but the compiler can't be sure of this. RINT() was Intel's invention. It would
  1903. round a long double to an integral value in accordance with IEEE standards.
  1904. When various precisions of floating point are involved, opinions are not
  1905. unanimous. In the matrix factorization code (3), I assigned the value that was
  1906. to be used in immediately subsequent calculations to a local double variable,
  1907. before assigning it to a float array element, to save the time taken by
  1908. compilers that would either read it back from memory or cast it back to
  1909. double. If I knew that the compiler would generate fast code by leapfrogging
  1910. an assignment, (some do), I wouldn't write it that way.
  1911. Certain compilers will ignore the evaluation implied by an intermediate
  1912. assignment if the assigned value is not actually used later on. I assume that
  1913. this is contrary to the ANSI standard, but I'm sure that others, whose
  1914. opinions count more, will assume differently. This is a gray area, since one
  1915. must be prepared for a compiler to promote a variable to register long double,
  1916. for instance, as long as it does so with reasonable consistency. If the
  1917. variable is never used, by this logic, the compiler could choose to promote it
  1918. to the extent that the implied cast is eliminated. Using the same name for
  1919. another purpose later on doesn't count either, since renaming is an accepted
  1920. technique for optimizing compilers.
  1921. On the other hand, I might want to round an expression off to a precision
  1922. consistent with the numbers from which it came, to avoid problems analogous to
  1923. Paul Wexler's. If the compiler were allowed to pass over assignments and
  1924. casts, it couldn't be done. Besides, it might be difficult to remember if the
  1925. compiler treated double differently from its treatment of int.
  1926. It's easier to write code that guarantees that the rounding will be passed
  1927. over than to prevent the compiler from making a surprise interpretation. The
  1928. standard definitions give more flexibility, control, and self-consistency in
  1929. the language definition.
  1930.  
  1931.  
  1932. How To Pinch Pennies
  1933.  
  1934.  
  1935. Paul Wexler feels that C is inadequate to control the operations of numeric
  1936. coprocessors. In particular, he suggests that he would like full control of
  1937. the rounding mode. Naturally, he is dismayed by the occasional theft of 99.44%
  1938. of a penny by the computational troll.
  1939. Several compilers supply system-dependent functions that allow the rounding
  1940. mode to be changed. I suggest that there are valid reasons for their
  1941. unpopularity. First, of course, they don't exist on many compilers and there
  1942. isn't any accepted syntax, so you must accept lack of portability. Secondly,
  1943. the idea of having to break your source code to insert such function calls,
  1944. and take the performance penalties involved in preventing compiler
  1945. optimizations or code pipelining across such calls, is unappealing.
  1946. The need for a way to specify rounding to an integral value has been
  1947. recognized in the definition of many languages and in the IEEE floating point
  1948. standard. For instance, Pascal has the ROUND() function, which was adopted by
  1949. FORTRAN as NINT(). At the same time, FORTRAN adopted the even more important
  1950. rounding without type casting called ANINT(). If it were not for the
  1951. disagreement between the IEEE and the language standards committees over the
  1952. need for rounding to even in the half-way case, the situation would be
  1953. satisfactory in these languages.
  1954. In C, the Pascal ROUND() can be simulated by a macro involving adding or
  1955. subtracting 0.5 before applying (int). Situations remain where it is
  1956. preferable not to use a cast, either for performance or to avoid narrowing the
  1957. range.
  1958. Financial calculations are an example. The way to perform calculations down to
  1959. the cent is to use integral numbers of cents and to round off to the nearest
  1960. cent after each operation that could introduce a fractional value, including
  1961. conversions from dollars to cents. If we always use double precision, numbers
  1962. of cents up to 253-1, or nearly 16 digits (for standard IEEE double
  1963. precision), may be calculated exactly.
  1964. The 8087 appears to have been intended for the integral value floating point
  1965. method of decimal support, as it has an instruction for storing directly from
  1966. register long double to an 18-digit decimal integer. In fact, if the atof()
  1967. function makes use of the 8087, it must convert the input decimal dollar
  1968. figure to cents internally before converting to binary and changing it back to
  1969. dollars by multiplying by 0.01. We would have been better off to convert the
  1970. decimal string to cents ourselves to begin with. Then we could use the
  1971. "inexact" flag to verify that atof() performs an exact conversion.
  1972. The designers of C have been stubborn in not providing a standard floating
  1973. round function. Many processors have one (e.g., the Intel FRNDINT), and the
  1974. inability to have it compiled into the code when it is needed will hurt
  1975. performance. There is no portable way to do it in C. The best that can be done
  1976. is to use a macro with provisions for change to support specific machines.
  1977. Rounding to an integer can be done portably, and conceivable optimizing
  1978. compilers could recognize code such as the ix=x example above and generate
  1979. more efficient code when appropriate.
  1980. Rounding may be performed in IEEE double arithmetic by
  1981. #include <float.h>
  1982. #define ROUNDER 1./DBL_EPSILON
  1983. #define FRNDINT(x) \
  1984. (((x) <ROUNDER) && ((x) >-ROUNDER) \
  1985. ? ((x)>=0.0 ? ((x)+ROUNDER)-
  1986. ROUNDER \
  1987. : ((x)-ROUNDER)+ROUNDER) : (x))
  1988. which normally would be simplified by omitting the cases for absolute value of
  1989. x greater than ROUNDER. Without this precaution, larger numbers are rounded to
  1990. multiples of 2. The constant ROUNDER has the value 252 for standard IEEE
  1991. double precision.
  1992. This code will take over twice as long as the hardware round instruction. It's
  1993. usually faster than the FORTRAN ANINT() functions, which are written to
  1994. overrule the IEEE style rounding of most modern hardware designs. You will
  1995. need to check that your compiler treats the parentheses in accordance with the
  1996. draft ANSI standard. At least one vendor (Convex) has chosen to violate this
  1997. feature of the language standards in search of better benchmark speed. The
  1998. whole macro could be optimized away!
  1999. Lovers of the obscure will notice that a slight rearrangement permits all of
  2000. the comparisons to be performed with integer instructions, if the programmer
  2001. knows the formats actually used for int and double data. A compiler for a
  2002. pipelined or multiple adder machine should produce code such as
  2003. register double temp1,temp2;
  2004. register int tmp1,tmp2;
  2005. temp1 = x+ROUNDER;
  2006. temp2 = x-ROUNDER;
  2007. tmp1 = x==0.0;
  2008. tmp2 = x+ROUNDER;
  2009. temp1 -= ROUNDER;
  2010.  
  2011. temp2 += ROUNDER;
  2012. tmp2 &= x-ROUNDER;
  2013. temp1 = tmp1?temp1:temp2;
  2014. temp1 = tmp2?temp1:x;
  2015. which keeps the total elapsed machine time under control and does wonders for
  2016. the megaflops rating of the code. If the number of stages in the pipeline
  2017. times the number of adders is at least four, there is no incentive to try
  2018. integer comparison.
  2019.  
  2020.  
  2021. Conclusion
  2022.  
  2023.  
  2024. Considering the amount I have had to say on a seemingly simple topic, one can
  2025. sympathize with C programmers who avoid grappling with floating point. I have
  2026. a little less sympathy with compiler and run time library writers who compound
  2027. the programmer's problems by exceeding the legal limits of optimization or
  2028. just don't think about the reliability issues.
  2029. Normal precautions in the use of floating point may help avoid problems with
  2030. compilers that take shortcuts and deviate from the rounding behavior required
  2031. by the language and hardware standards. Most such situations are recognizable
  2032. and may be taken care of by explicit specification of rounding. Similar
  2033. situations exist where code cannot be expected to be reliably portable without
  2034. such precautions, even among compilers that adhere to the language standard.
  2035. In C, a problem is presented by the lack of a portable way to specify rounding
  2036. without truncating to int.
  2037. References
  2038. Ritchie, Dennis M., The C Programming Language -- Reference Manual, Bell
  2039. Laboratories, Murray Hill, N.J. 1978.
  2040. Harbison, S. P., Steele, G. L. C: A Reference Manual, Second Edition,
  2041. Prentice-Hall 1987.
  2042. Prince, T.C., "Efficient Matrix Coding in C," The C Users Journal, May 1989,
  2043. p. 59.
  2044.  
  2045.  
  2046.  
  2047.  
  2048.  
  2049.  
  2050.  
  2051.  
  2052.  
  2053.  
  2054.  
  2055.  
  2056.  
  2057.  
  2058.  
  2059.  
  2060.  
  2061.  
  2062.  
  2063.  
  2064.  
  2065.  
  2066.  
  2067.  
  2068.  
  2069.  
  2070.  
  2071.  
  2072.  
  2073.  
  2074.  
  2075.  
  2076.  
  2077.  
  2078.  
  2079.  
  2080.  
  2081.  
  2082.  
  2083.  
  2084.  
  2085.  
  2086.  
  2087.  
  2088.  
  2089.  
  2090.  
  2091. Using Large Arrays In Turbo C
  2092.  
  2093.  
  2094. Stuart T. Baird
  2095.  
  2096.  
  2097. Stuart Baird first learned to program on an IBM 1620 in 1966, going on to
  2098. study computer science at the University of Illinois. Now a General Electric
  2099. employee, he has long since abandoned professional programming for
  2100. administration, but remains an avid recreational programmer at home, using C.
  2101.  
  2102.  
  2103.  
  2104.  
  2105. Declaration Of Large Arrays
  2106.  
  2107.  
  2108. Declaring large arrays within a function such as main() is something
  2109. programmers brought up on other programming languages are used to doing
  2110. without extra thought. For example,
  2111. int a[20000],b[20000];
  2112. is the C equivalent of the DIM or DIMENSION statements that a programmer would
  2113. use in BASIC or FORTRAN. It's a perfectly acceptable declaration in C, and it
  2114. compiles in Turbo C (Borland International's C compiler) without comment. So,
  2115. it comes as a shock when a simple program using that declaration doesn't work.
  2116. Turbo C, of course, is written for the Intel 8086 and related microprocessors,
  2117. which have a segmented memory architecture. These microprocessors normally
  2118. address only one segment, 64K bytes of memory, at a time. The designer of a
  2119. compiler that produces code to execute on such a machine must decide whether
  2120. to insulate the programmer from the idiosyncrasies of the hardware or to cling
  2121. closely to the architecture for greatest control and maximum speed. Given the
  2122. nature of C, the latter is the natural choice. Unfortunately, that choice
  2123. presents unexpected problems to the programmer using large arrays. ("Pointer
  2124. Arithmetic at Memory Segment Boundaries" by Daniel and Nancy Saks, The C Users
  2125. Journal, October 1989, discusses other aspects of this problem.)
  2126. One way Turbo C solves the addressing problem is by using different models of
  2127. the compiler and libraries, depending on the anticipated size of the program.
  2128. Turbo C's documentation disccusses the models. The small model is recommended
  2129. for most programs; it allows 64K bytes of code and 64K bytes of static data.
  2130. This article refers to the small model unless it specifies otherwise. Since I
  2131. discuss only large data, not large code, I will also use the term small memory
  2132. model to refer to any model that limits data to 64K, namely the tiny, small,
  2133. and medium models. The term large memory model will refer to any model that
  2134. lifts that restriction, namely the compact, large, and huge models.
  2135. In the array declarations at the beginning of this article, the Turbo C
  2136. compiler assigns both arrays to the same data segment (of 64K bytes). This
  2137. action is understandable, since in a small memory model a near pointer, only
  2138. two bytes long, is used to address the data. It's impossible for two-byte
  2139. pointers to represent more than 64K different addresses. However, if the
  2140. declared arrays are large enough, they overlap, but the compiler issues no
  2141. warning message. In the example, 40,000 two-byte integers cannot all fit into
  2142. 64K (that is, 65,536) bytes. At some point, one of the arrays wraps around to
  2143. the beginning of the data segment, so some of b's elements "share" memory
  2144. locations with elements of a, which is not good. (Incidentally, you cannot
  2145. assume that b is the array that wraps around, nor can you assume that you have
  2146. a full 64K to work with.) Assignment to the same segment is true even in the
  2147. large memory models, which seems to be a bug.
  2148. The solution must be to use dynamic memory allocation. See Listing 1.
  2149. It's clear that near pointers, which address only within 64K, are
  2150. insufficient, hence the far modifiers. Alas, this doesn't work, either. The
  2151. type of pointer malloc() returns in a small memory model is a near pointer. It
  2152. may gladly return a non-NULL pointer, seemingly indicating memory is
  2153. available, but the address space is still within 64K, and the arrays still
  2154. overlap. Turbo C does remind you of the non-portable pointer assignment (that
  2155. is, the conversion of malloc ()'s near pointer return value to the far pointer
  2156. a) at compile time. You can eliminate the message with a cast, but the problem
  2157. itself remains.
  2158. In the Compact model (one of the large memory models), the previous example
  2159. does work, with or without the far modifiers, which are the default pointer
  2160. type. Other reasons for not using this model will be mentioned later.
  2161. In a small memory model, I must use farmalloc() instead of malloc ():
  2162. int far *a, far *b;
  2163. if ((a = farmalloc(20000L*sizeof(int))) ... etc.
  2164. The library function farmalloc() returns a far pointer, and everything now
  2165. works fine. In particular, I can continue to refer to these allocations as
  2166. arrays, for example
  2167. a[n] = b[n] + 1;
  2168. which of course means the same as
  2169. *(a+n) = *(b+n) + 1;
  2170. A disadvantage of using farmalloc() is that allocations may fail if Turbo C is
  2171. loaded into memory when you run the program. (The complier seems to occupy the
  2172. area of memory where farmalloc() goes looking.) In that case, execution will
  2173. have to be deferred until you exit Turbo C. Standalone execution forfeits
  2174. Turbo C's useful run-time debugging features.
  2175.  
  2176.  
  2177. Global Data
  2178.  
  2179.  
  2180. Global data (that is, data elements declared outside of and accessible to all
  2181. functions in a program) seems to be restricted to 64K in all Turbo C's memory
  2182. models - even huge, which, according to the documentation, lifts restrictions
  2183. on static data. Yet it is often useful for a large array to be accessible
  2184. throughout a program.
  2185. Again, the solution is to allocate the large array(s) dynamically in main() as
  2186. above, and keep only pointers to the data in the global area. For example, if
  2187. you need a number of arrays with dimension 8,000, rather than depleting the
  2188. global data area by a multiple of 8,000 bytes per array, store only the
  2189. pointers (at two or four bytes apiece) in the global area. The memory
  2190. allocated for the arrays will be somewhere else. See Listing 2. (The error
  2191. returns from malloc() and farmalloc() are not checked in Listing 2 but should
  2192. be in an actual program.) With 40,000 bytes allocated beforehand, the last
  2193. array cannot possibly fit within the same 64K as the other arrays, so it needs
  2194. to use farmalloc() and the modifier far (or huge).
  2195. Global items, of course, make the programmer honor-bound to use names and
  2196. types consistently in all functions. Unintentional conflicts are difficult to
  2197. catch. Furthermore a function that uses global data can't be lifted out and
  2198. used in another program without rework. A language such as BASIC has no other
  2199. choice, but C does. As an alternative to using global data, you can pass
  2200. pointers as arguments to those functions that need them. Although incurring
  2201. some additional overhead, this method is often regarded as "cleaner" since the
  2202. function is then self-contained.
  2203.  
  2204.  
  2205. Scope Of Modifiers In Declarations
  2206.  
  2207.  
  2208. In the declaration
  2209. extern int i,j,k;
  2210. all three variables are understood to be both external and integers. However,
  2211. in a Turbo C declaration
  2212. int far *p,*q,*r;
  2213. only p is a far pointer. q and r (if compiled in a small data model) have the
  2214. default attribute near. If q and r are supposed to be far pointers to int as
  2215. well, you must use
  2216. int far *p, far *q, far *r;
  2217. as you've already done in previous examples, or use a typedef:
  2218. typedef int far * fpi; 
  2219. fpi p,q,r;
  2220. This might seem to be a quirk of the non-standard modifiers near, far, and
  2221. huge, but there is a precedent in Standard C. The indirection symbol * also
  2222. binds to the variable. [So do the type qualifiers const and volatile. -pjp] In
  2223. the declaration
  2224. int * p,q,r;
  2225. p is a pointer to int, while q and r are just ints.
  2226. In general, the scope of the elements in a declaration (that is, to how much
  2227. of the complete declaration they apply) does not seem to be well documented in
  2228. C - the documentation, if it exists, is not obvious. I couldn't find this
  2229. topic specifically addressed in K&R, in Standard C, or in Turbo C's
  2230. documentation. It's clear what syntax is legal, but it's not immediately clear
  2231. just what the construction means, until you try it in a program. Lack of
  2232. documentation is unfortunate, but the dialect resulting from having to use
  2233. non-standard features (the modifiers) is the real culprit.
  2234.  
  2235.  
  2236.  
  2237. Arrays Greater Than 64K
  2238.  
  2239.  
  2240. Allocating an array of greater than 64K bytes using farmalloc() is easy:
  2241. allocate the memory using a long integer expression (for example 100000L),
  2242. assign the return value (the starting address) to a pointer, and use the
  2243. pointer as an array name.
  2244. What kind of pointer? Clearly not a near pointer, which can only address
  2245. within a 64K segment. It is not a far pointer, either. While far pointers are
  2246. capable of addressing anywhere in memory, far pointer arithmetic does not work
  2247. beyond 64K. Suppose you have a large array and pointers to it named a and b,
  2248. respectively:
  2249. char far *a, far *b;
  2250. long count = 100000L;
  2251. a = farmalloc(count);
  2252. (Normally you would check for a NULL return indicating allocation failed, of
  2253. course.) Suppose you try to initialize the array to blanks:
  2254. b = a;
  2255. while (count- -) *b++ = ' ';
  2256. You would find that only the first 65,536 or so characters of array a were
  2257. initialized properly; the remainder would be untouched. That is because when b
  2258. has a value of a+65535 and is then incremented, the next value becomes not
  2259. a+65536 but a+0. In the interest of speed, far pointers are not checked for
  2260. possible overflow, leading to wraparound and erroneous results such as this.
  2261. Since Turbo C's documentation is clear on this point, the arithmetic error
  2262. cannot be considered a bug. Less clear from the documentation is what happens
  2263. if you use subscripting instead
  2264. long i;
  2265. for (i=0L; i<count; i++) a[i] = ' ';
  2266. Unfortunately, this works exactly the same as the pointer version above. The
  2267. address of a[65536] is translated to the pointer a+65536, which (not checking
  2268. for overflow, long arithmetic notwithstanding) becomes just a (or &a[0]);
  2269. a[65537] becomes a[1], etc. All the elements beyond 65535 are therefore
  2270. inaccessible. The same problem surfaces earlier with larger data types: a
  2271. two-byte int array declared as far can't go beyond element 32767, for example.
  2272. The numbers above have been quoted as though exactly 65,536 bytes are
  2273. available. In practice, Turbo C seems to require a few bytes of overhead. The
  2274. offset portion of the allocated addresses are 8, not 0. This implies that the
  2275. wraparound takes place earlier: at element 65527 or 32763, for instance.
  2276. The solution is to declare such pointers as huge rather than far. Both far and
  2277. huge pointers are capable of representing the same range of addresses, and
  2278. both take up the same amount of space (four bytes). The difference is that
  2279. huge pointers are normalized after every arithmetic operation. Normalization
  2280. yields correct results, though at some cost in speed. (far and huge pointers
  2281. and normalization are discussed in Turbo C's documentation and need not be
  2282. covered in detail here.)
  2283.  
  2284.  
  2285. Piecemeal Allocation
  2286.  
  2287.  
  2288. Why allocate everything at once? In some applications, it makes sense to
  2289. allocate memory only as it is required. Perhaps the program can be designed to
  2290. be flexible. Accumulate data and fill as much memory as is available. Then if
  2291. eventually an allocation request fails, process what is there and continue on.
  2292. One might wish to use this approach for a word processor or sort utility, for
  2293. example.
  2294. There's a significant difference between piecemeal allocation and allocating
  2295. everything at once, however. The single large allocation
  2296. a = malloc(10000);
  2297. is guaranteed to give the address of a block of 10,000 bytes of memory in a
  2298. row (unless there is an error return or wraparound, of course). That block can
  2299. be treated as an array. Alas, there is no such guarantee with individual small
  2300. allocations. Examine the following piece of code, which allocates memory one
  2301. byte at a time and stores individual characters as they are read:
  2302. char *a,*b;
  2303. int count=0;
  2304. a = b = malloc(1);
  2305. while ((*b = getchar()) != EOF) {
  2306. b = malloc(1);
  2307. count++;
  2308. }
  2309. So far, this ought to work (again assuming no error returns). Characters are
  2310. read and stored. This subsequent code, however, would not work:
  2311. for (i = 0; i<count; i++)
  2312. putchar (a [i ] );
  2313. This code would not work because it is a mistake to assume that the addresses
  2314. that successive allocations assigned to b are contiguous. You could not
  2315. blithely refer to the second character obtained by getchar() as a[1], unless
  2316. somehow you had ascertained that the second value that malloc() assigns to b
  2317. were indeed larger than the first by exactly sizeof(*a). In Turbo C, using
  2318. either malloc() or farmalloc(), it definitely isn't.
  2319. If what you really want is one large array, piecemeal allocation is not
  2320. practical.
  2321.  
  2322.  
  2323. Pointers To Pointers
  2324.  
  2325.  
  2326. Suppose you have an array a declared as a huge pointer:
  2327. char huge *a;
  2328. a = farmalloc(100000L);
  2329. Perhaps it contains a number of strings with variable lengths. If you wanted
  2330. to refer to one of those strings, you would need another huge pointer to do
  2331. so:
  2332. char huge *aptr;
  2333. You might want to refer to all of them as a set, for example to maintain an
  2334. index:
  2335. char huge *aindex[NSTRINGS];
  2336. Suppose that the first three elements of aindex refer to strings which begin
  2337. at a, a+47000L, and a+93333L. With 100,000 elements allocated, and huge
  2338. pointers, these are all valid addresses. Rather than make this example
  2339. completely dry and numerical, let's say that the second of these strings is
  2340. Skipping rivulet and fountain.
  2341. Now suppose you want to use a pointer to refer to the various elements of
  2342. aindex, perhaps just to step through the index or (in a typical application)
  2343. to sort it. What kind of pointer do you need? The array aindex could be quite
  2344. small, perhaps even just the three elements previously defined. So in that
  2345. case, an ordinary near pointer would suffice. The task at hand is to declare
  2346. that pointer. While a compiler ought to be able to parse such a declaration,
  2347. it would be confusing to the ordinary reader, since it would contain both the
  2348. huge and near modifiers. (In a small memory model, only the huge modifier,
  2349. even though the resulting pointer type is near.) I wouldn't even attempt it
  2350. without using a typedef:
  2351. typedef char huge * hugecp;
  2352. hugecp aindex[NSTRINGS];
  2353. hugecp near *pindex;
  2354. In a small memory model, the near modifier is unnecessary, but why not make
  2355. things explicit for clarity?
  2356. On the other hand, you might need aindex to contain very many elements. Since
  2357. a huge pointer is four bytes long, any value of NSTRINGS over 16,383 (or so)
  2358. would mean you have to declare pindex as
  2359.  
  2360. hugecp huge *pindex;
  2361. In fact, declaring aindex as an array would no longer work, since it would be
  2362. larger than 64K. You would need to allocate it dynamically:
  2363. hugecp huge *aindex;
  2364. aindex = farmalloc((long)
  2365. NSTRINGS*sizeof(hugecp));
  2366. (Again, better check for an error return in actual practice.) If you used this
  2367. method of allocating aindex even if its size were small, you would still need
  2368. to declare pindex huge (or far) just to allow it to point to the correct data
  2369. segment, since the far heap where aindex is allocated is somewhere different
  2370. from where automatic variables are allocated.
  2371. Let us now look at the resulting pointer pindex. Suppose
  2372. pindex = &aindex[1];
  2373. What is *pindex? It is the contents of element number 1 of aindex, which is
  2374. the address a+47000L. Regardless of whether pindex is near, far, or huge,
  2375. *pindex is a huge pointer, referring to the string "Skipping rivulet and
  2376. fountain." How about **pindex? It is the contents of address a+47000L, the
  2377. character 'S'. The value of *(*pindex + 1) would be 'k', etc. The type of
  2378. **pindex is char. Given all this, you'd think we could just declare pindex as
  2379. follows:
  2380. char **pindex;
  2381. but that would (presumably) make both *pindex and pindex the default type of
  2382. pointer, namely near in the small data models. That wouldn't do, in our
  2383. example. If you declared
  2384. char huge **pindex;
  2385. then (presumably) *pindex would be a huge pointer, but pindex would still be
  2386. near - or is it the other way around? This wouldn't do, either. Besides being
  2387. unclear, it would probably not have the effect of making both of them huge.
  2388. In summary, declarations for pointers to pointers need extra care when large
  2389. arrays are involved. Though we haven't discussed it, arrays with double
  2390. subscripts require the same care.
  2391.  
  2392.  
  2393. Mixed Pointers
  2394.  
  2395.  
  2396. Turbo C allows you to mix near, far, and huge pointers in any of the memory
  2397. models, with some restrictions noted in the documentation. Mixed pointers are
  2398. perhaps slightly difficult to keep untangled in one's own code and functions,
  2399. but with care it can be done. Using the standard library with mixed pointers,
  2400. however, is much more of a problem.
  2401. Some standard functions, such as printf(), have built-in ways to deal with
  2402. large pointers in a small data model. For example, to print as an address the
  2403. value of an ordinary (near) pointer in a small data model, you use the format
  2404. specification %p, but it's possible to print the value of a far (or huge)
  2405. pointer using %Fp. The options are limited, however, and it's easy to make
  2406. mistakes. One such mistake is supplying pointers of one size when printf() is
  2407. expecting pointers of a different size. There is generally no compile-time
  2408. warning, and the mismatch between format specification and parameter list
  2409. gives unpredictable results.
  2410. Turbo C calls in separate libraries suitable for the different memory models,
  2411. so that in a large data model, such as compact, the version of strcpy()
  2412. contained in the library accepts far pointers, which are the default in that
  2413. model. For most programs, which do not need to mix pointers, this is a boon.
  2414. The programmer does not need to worry about a different set of standard
  2415. functions with unique names, let alone write new ones. Most standard functions
  2416. cannot cope with non-default pointers, however. If you need a capability
  2417. provided by a standard function, you are forced to program around the
  2418. limitation, such as by writing a new function or performing an intermediate
  2419. conversion.
  2420. In the huge memory model, far pointers (not huge pointers) are still the
  2421. default. The standard library in that case also uses far pointers. A function
  2422. that accepts far pointers will also accept huge pointers. The format is the
  2423. same, and the value of a huge pointer is just one of many far pointer values
  2424. that refer to a particular address. However, the limitations of far pointers
  2425. previously discussed also apply to the internal workings of such functions.
  2426. The standard library functions may not work properly with large arrays. The
  2427. function qsort(), for example, which works so beautifully most of the time,
  2428. fails for arrays larger than 64K. You can provide it with huge pointers as
  2429. arguments, but you can't make it use huge pointers internally.
  2430. Incidentally, there seems to be a small bug in printf() in the huge model.
  2431. Even though far pointers are the default, an obscure linker error message
  2432. complains if you try to use %p to print a far pointer's value. Use %Fp and the
  2433. message goes away.
  2434. In other words, be wary of mixing near, far, and huge pointer types,
  2435. especially when using the standard library. You may end up having to write
  2436. functions for your particular pointer needs.
  2437.  
  2438.  
  2439. Summary
  2440.  
  2441.  
  2442. Although my experience with C on an 8086-style microprocessor is limited to
  2443. Borland's Turbo C, it seems reasonable to assume that other C compilers for
  2444. segmented memory micros will have similar limitations when large arrays must
  2445. be used. Arrays whose sizes are large enough to cross a segment boundary
  2446. require much more understanding and planning on the part of the programmer.
  2447. Those of us who need to use large arrays in C look forward to the time when
  2448. improved software or hardware in personal computers makes the size of an array
  2449. transparent!
  2450.  
  2451. Listing 1
  2452. int far *a, far *b;
  2453. if ((a = malloc(20000*sizeof(int))) == NULL) {
  2454. printf("cannot allocate a\n");
  2455. exit(1);
  2456. }
  2457. if ((b = malloc ... etc.
  2458.  
  2459.  
  2460. Listing 2
  2461. char *a;
  2462. int *b,*c;
  2463. double far *d;
  2464. main()
  2465. {
  2466. a = malloc(8000*sizeof(char));
  2467. b = malloc(8000*sizeof(int));
  2468. c = malloc(8000*sizeof(int));
  2469. d = farmalloc(8000L*sizeof(double));
  2470. fct(); ...
  2471. }
  2472. fct()
  2473. {
  2474. a[3] = ...; /* global arrays accessible */
  2475. }
  2476.  
  2477.  
  2478.  
  2479.  
  2480.  
  2481.  
  2482.  
  2483.  
  2484.  
  2485.  
  2486.  
  2487.  
  2488.  
  2489.  
  2490.  
  2491.  
  2492.  
  2493.  
  2494.  
  2495.  
  2496.  
  2497.  
  2498.  
  2499.  
  2500.  
  2501.  
  2502.  
  2503.  
  2504.  
  2505.  
  2506.  
  2507.  
  2508.  
  2509.  
  2510.  
  2511.  
  2512.  
  2513.  
  2514.  
  2515.  
  2516.  
  2517.  
  2518.  
  2519.  
  2520.  
  2521.  
  2522.  
  2523.  
  2524.  
  2525.  
  2526.  
  2527.  
  2528.  
  2529.  
  2530.  
  2531.  
  2532.  
  2533.  
  2534.  
  2535.  
  2536.  
  2537.  
  2538.  
  2539.  
  2540.  
  2541.  
  2542. Multi-Threaded C Functions
  2543.  
  2544.  
  2545. AJM Beddow
  2546.  
  2547.  
  2548. Mr. Beddow is an electrical engineer with 15 years experience in electronic
  2549. and computer system design. His current project involves real-time distributed
  2550. databases. You may contact him at P.O. Box 36 Wantage, Oxon, OX12 8LL, United
  2551. Kingdom.
  2552.  
  2553.  
  2554. OS/2 incorporates lightweight threads to implement parallel control with
  2555. efficient use of system resources. You can construct functions that create the
  2556. context for multi-threaded execution, control the execution of the threads,
  2557. and remove the multi-thread context when the thread functions terminate. You
  2558. can do this with the standard C library under any operating system.
  2559. The example of multi-threading uses an explicit function call to change thread
  2560. context to the next thread of execution. This switch is normally made when an
  2561. I/O resource is blocked and a thread is required to wait on the resource. The
  2562. disadvantage of this non-preemptive thread switching is that you must add
  2563. explicit thread switches within a thread's computation intensive parts to
  2564. prevent its hogging the CPU. The advantage of non-preemptive thread switching
  2565. is that reentrant calls to DOS can't run. The result is generally more
  2566. efficient than the pre-emptive context switching for a non-reentrant operating
  2567. system.
  2568. All thread functions are loaded as part of the one program. You create the
  2569. threads by reserving stack space for each thread, creating a dispatch table,
  2570. and dispatching the first thread. If all threads terminate, then the stack
  2571. context space is recovered and single thread execution continues where it left
  2572. off. The thread creation function is non-reentrant. It cannot be called from
  2573. inside a spawned thread.
  2574.  
  2575.  
  2576. How It Is Done
  2577.  
  2578.  
  2579. To accomplish multi-threading in C, use the standard functions longjmp() and
  2580. setjmp(). The setjmp() function takes an environment pointer and creates an
  2581. environment for the current machine state, returning the value zero when it
  2582. has done so. A longjmp() with the environment pointer as its parameter causes
  2583. the program thread to return once more from a non-zero return value. The stack
  2584. pointer and frame pointer are adjusted to the values saved in the environment
  2585. when the setjmp() was originally called. This is the basis of the mechanism
  2586. for switching thread context.
  2587. Listing 1 is an example of setjmp() and longjmp() functions for small model C
  2588. programs. To return to a thread with its stack context intact, you must
  2589. reserve a stack area for each thread. You achieve this by recursively calling
  2590. a thread creation function with a large automatic array, and modifying the
  2591. stack pointer in a thread environment to point to the top of the array.
  2592. Listing 2 shows my multi-threading functions. The function thread() takes a
  2593. null terminated list of function pointers and copies the pointers into the
  2594. threaddata structure array. Thread() then sets up a root environment and calls
  2595. threadrun(). Threadrun() calls itself recursively creating an environment for
  2596. each thread on the stack and storing a pointer to the environment in the
  2597. threaddata structure array. It also modifies each stack pointer in the
  2598. environment to point to the top of its automatic array stack-space.
  2599. Figure 1 shows the resulting stack arrangement of individual thread
  2600. environments and stacks. Threadrun() then makes a longjmp() call to the first
  2601. thread and returns to the setjmp point in threadrun() with a return value of
  2602. -1. The first thread function pointer is then dispatched and the first thread
  2603. runs. You make subsequent thread switches by calling threadswitch(), which
  2604. sets the thread state flag to waiting, saves the thread environment, and calls
  2605. longjmp() with the next thread environment to dispatch the next thread. When a
  2606. function terminates, it returns to threadrun() which sets the thread state to
  2607. terminated and calls threadswitch() to run the next available thread. When
  2608. threadrun() finds all threads have terminated, it calls longjmp() with a
  2609. pointer to the root environment, the thread stack spaces are recovered, and
  2610. single (normal) thread execution resumes.
  2611. Note that threadswitch() calls have no effect in single thread execution and
  2612. can be embedded into functions with no ill effects to single thread execution.
  2613. The macro SPINDX defines the offset in words of the stack pointer parameter in
  2614. the environment space. Use DEBUG on a setjmp() call to determine the offset
  2615. for your own C compiler.
  2616.  
  2617.  
  2618. Example And Uses
  2619.  
  2620.  
  2621. In Listing 3, the main() function calls thread() with a list of three function
  2622. pointers. Each function has two printf() function calls with a thread switch
  2623. between them. Each function executes in turn to print one statement at a time.
  2624. Aside from this example, multi-threading allows execution sharing between
  2625. program function components to be separated from the function control flow. A
  2626. state-sequencing function for example, may process multiple character streams
  2627. using a state-sequencer thread for each stream and thread switching when a
  2628. input stream is blocked. In real- time applications this is a low overhead
  2629. method of resource sharing between non-time critical tasks.
  2630. Figure 1
  2631.  
  2632. Listing 1
  2633. ; Unless declared otherwise all
  2634. ; values are type short
  2635. ; setjmp(array pointer) : set
  2636. ; environment of 3 integers,
  2637. ; returns 0
  2638. ; array -> bp of calling function
  2639. ; sp current
  2640. ; address of setjmp return
  2641. PUBLIC setjmp
  2642. set jmp PROC NEAR
  2643. ;
  2644. mov bx,sp
  2645. mov ax,ss:[bx]
  2646. mov bx,ss:[bx+2]
  2647. mov ss:[bx],bp
  2648. mov ss:[bx+2],sp
  2649. mov ss:[bx+4],ax
  2650. xor ax,ax
  2651. ret
  2652. ;
  2653. setjmp ENDP
  2654. ; longjmp(array pointer, constant) :
  2655. ; non local goto, returns constant
  2656. PUBLIC longjmp
  2657. longjmp PROC NEAR
  2658. ;
  2659.  
  2660. xor ax,ax
  2661. mov bp,sp
  2662. mov bx,[bp+2]
  2663. mov ax,[bp+4]
  2664. or ax,ax
  2665. jne L1
  2666. inc ax
  2667. L1:
  2668. mov bp,ss:[bx]
  2669. mov sp,ss:[bx+2]
  2670. mov cx,ss:[bx+4]
  2671. mov bx,sp
  2672. mov ss:[bx],cx
  2673. ret
  2674. ;
  2675. longjmp ENDP
  2676.  
  2677.  
  2678. Listing 2
  2679. /* ************************** */
  2680. /* Multi-threading functions */
  2681.  
  2682. #include <setjmp.h>
  2683. #define TSTACKSIZE 200 /* Stack space reseved in each thread in words */
  2684. #define MAXTHREAD 20 /* Max No of concurrent threads */
  2685. #define SPINDX 1 /* Index of stack pointer into env */
  2686. #define NULL 0
  2687. static struct threaddat {
  2688. int *env ; /* Environment pointer */
  2689. int (*tfp)() ; /* Thread function pointer */
  2690. int state ; /* 0 Waiting 1 running -1 terminated */
  2691. } threaddata[MAXTHREAD] ;
  2692. /* Globals for thread() */
  2693. static jmp buf envroot ; /* root environment */
  2694. static int threadnum = -1 ; /* Number of threads */
  2695. static int threadindx ; /* Thread indexer */
  2696. /* Takes a NULL terminated list of function pointers, sets up
  2697. the global parameters above and the root environment and then
  2698. calls threadrun() to define and run the function threads. */
  2699. thread(fplist)
  2700. int (*fplist)() ;
  2701. {
  2702. int (**fplptr)() ;
  2703. void threadrun() ;
  2704. if (threadnum == -1)
  2705. { fplptr = &fplist ;
  2706. threadnum = 0 ;
  2707. while (*fplptr != NULL)
  2708. threaddata(threadnum++).tfp = *fplptr++ ;
  2709. threadindx = 0 ;
  2710. if (!setjmp(envroot))
  2711. threadrun() ; /* define thread environments recursivly */
  2712. /* then run them to extinction */
  2713. }
  2714. }
  2715. /* recusivly defines thread environments then runs in round robin */
  2716. void threadrun()
  2717. {
  2718. jmp_buf envthread ;
  2719.  
  2720. int stackspace[TSTACKSIZE) ;
  2721. if (threadindx < threadnum)
  2722. { if (setjmp(envthread))
  2723. { /* Will arrive here by longjmp for function dispatch */
  2724. (*threaddata[threadindx].tfp)() ; /* Start thread */
  2725. threaddata[threadindx].state = -1 ; /* terminated */
  2726. threadswitch() ;
  2727. printf("Multi-threading error - terminating ) ;
  2728. exit(1) ;
  2729. }
  2730. else
  2731. {
  2732. /* Global pointer to thread env */
  2733. threaddata[threadindx].env = envthread ;
  2734. /* Thread state to waiting to run */
  2735. threaddata[threadindx].state = 0 ;
  2736. /* Modify env value for stack with a little margin for luck */
  2737. envthread[SPINDX] = (int) &stackspace[TSTACKSIZE-5] ;
  2738. threadrun(++threadindx) ; /* Recusive call for next thread env */
  2739. }
  2740. }
  2741. else /* Run threads in round robin */
  2742. {
  2743. /* Start the ball rolling with thread 1 */
  2744. threadindx = 0 ;
  2745. longjmp(threaddata[threadindx].env, -1) ;
  2746. }
  2747. }
  2748. /* Thread switcher : Will switch to next available function thread in list.
  2749. If function threads have all terminated will terminate the threads
  2750. environment and return to the root enviroment */
  2751. threadswitch()
  2752. { int i ;
  2753. if (threadnum != -1)
  2754.  
  2755. {
  2756. if (threaddata[threadindx].state != -1)
  2757. threaddata[threadindx] .state = 0 ;
  2758. if (!setjmp(threaddata[threadindx].env))
  2759. {
  2760. for (i=1; i <= threadnum; i++)
  2761. { if (++threadindx >= threadnum)
  2762. threadindx = 0 ;
  2763. if (!threaddata[threadindx].state)
  2764. { threaddata[threadindx].state = 1 ;
  2765. longjmp(threaddata[threadindx].env, -1) ;
  2766. }
  2767. }
  2768. threadnum = -1 ;
  2769. longjmp(envroot, -1 ) ;
  2770. }
  2771. }
  2772. }
  2773.  
  2774.  
  2775. Listing 3
  2776. /* ************************************************* */
  2777. /* Demonstration of technique for multi-threading C */
  2778.  
  2779.  
  2780. #define NULL 0
  2781. main()
  2782. {
  2783. extern case0(), case1(), case2() ;
  2784. thread( case0, case1, case2, NULL) ;
  2785. }
  2786. int case0()
  2787. {
  2788. printf("** First thread part 1 ** ) ;
  2789. threadswitch() ;
  2790. printf("** First thread part 2 ** ) ;
  2791. }
  2792. int case1()
  2793. {
  2794. printf("** Second thread part 1 ** ) ;
  2795. threadswitch() ;
  2796. printf("** Second thread part 2 ** ) ;
  2797. }
  2798. int case2()
  2799. {
  2800. printf("** Third thread part 1 ** ) ;
  2801. threadswitch() ;
  2802. printf("** Third thread part 2 ** ) ;
  2803. }
  2804.  
  2805.  
  2806.  
  2807.  
  2808.  
  2809.  
  2810.  
  2811.  
  2812.  
  2813.  
  2814.  
  2815.  
  2816.  
  2817.  
  2818.  
  2819.  
  2820.  
  2821.  
  2822.  
  2823.  
  2824.  
  2825.  
  2826.  
  2827.  
  2828.  
  2829.  
  2830.  
  2831.  
  2832.  
  2833.  
  2834.  
  2835.  
  2836.  
  2837.  
  2838.  
  2839.  
  2840.  
  2841.  
  2842.  
  2843. Some Tips For QuickSort
  2844.  
  2845.  
  2846. Joe Celko
  2847.  
  2848.  
  2849. Joe Celko is a college teacher and consultant based in Los Angeles, as well as
  2850. a member of the ANSI X3H2 Database Standards Committee. He wrote a biweekly
  2851. column on software design in Information Systems News for four years and has
  2852. taught structured methods in industry and colleges.
  2853.  
  2854.  
  2855. Mark Nelson's article on QuickSort (CUJ, August 1990)[1] examined tuning an
  2856. algorithm to fit a particular need. In this article, I will supply you with
  2857. more tricks to improve QuickSoft performance.
  2858. The choice of the pivot (also called the key value) is the most important
  2859. factor in the performance of each partition in the sort. The statistical
  2860. distribution of the data to be sorted is the most important factor in picking
  2861. the pivot.
  2862. Another helpful point not mentioned in previous QuickSort articles is that the
  2863. pivot value does not have to be an actual value in the array being sorted. The
  2864. code changes a little bit, but the result is that everything in the left
  2865. partition is less than the pivot, and everything in the right partition is
  2866. greater. Now you can construct a pivot from a sample of the values in the
  2867. partition without actually looking for an occurrence of it.
  2868. If you have a uniform probability distribution in the data, as you would
  2869. sorting on a unique key, then using the first array element of each partition
  2870. (the first naive sort given in Nelson's article) will work as well or better
  2871. than any fancier method.
  2872. Using the middle position of the partition as the pivot also works well in
  2873. practice since many files already have some order to them from previous
  2874. sorting. The code for the middle point, pivot = (first + last)/2, will be
  2875. optimized to a shift operation instead of a divide by most C compilers.
  2876. If you have a normal probability distribution (the so-called bell curve) with
  2877. any skew (tilt to one side) in the data, then the "median of three" algorithm
  2878. for picking a pivot will work much better. Using three values reduces
  2879. comparisons by 14 percent, but more than three does not seem to give much
  2880. improvement.
  2881. QuickSort is a non-stable sort. A stable sort preserves the original order in
  2882. the file for records with duplicate sort keys. Stable sorts are nice in one
  2883. sense because they can be applied one after the other. If I need to sort a
  2884. file by counties for one report, and by counties within states for another
  2885. report, and I use a stable sort, then I can run the first report followed by a
  2886. stable sort on the states alone for the second report.
  2887. Unfortunately, stable sorts tend to be slower than non-stable sorts. On the
  2888. bright side, you can modify the QuickSort to be a stable sort. You do this by
  2889. scanning from left to right, partitioning the records into two separate files
  2890. in their original order and then appending them together in order. The
  2891. internal sorting phase is also done with a stable sort.
  2892. Recursion is not the enemy. William Hutchison of Exton, PA collects versions
  2893. of QuickSort written in C to use for compiler and hardware testing on a
  2894. standard set of data files. He reports in private correspondence that he found
  2895. that recursive versions of QuickSort often ran faster than non-recursive
  2896. versions of the same variation. Modern stack hardware makes an important
  2897. difference.
  2898. Using a different sort for partitions smaller than some size M is a good
  2899. trick, since many sorts run faster than QuickSort for small arrays. An even
  2900. better way is to ignore the small subfiles during the first phase of the sort,
  2901. then go back through the entire files and sort it with a procedure tuned for
  2902. handling M or fewer records. You save the overhead of invoking the small
  2903. partition sort over and over while the QuickSort is running. The best size for
  2904. M is 9 or 10, but anything between 6 and 15 will work about as well, according
  2905. to analysis [2].
  2906. Insertion sort is a good choice, but you can also use the Bose-Nelson sort.
  2907. This algorithm does not sort directly, but it generates the minimum number of
  2908. swap pairs for a fixed size array. A swap pair is a pair of array elements
  2909. that are to be compared and then swapped if they are out of order [3].
  2910. Another trick is to see if a partition is already sorted and to then leave it
  2911. alone. If the file is already in near sorted order, this can save a lot of
  2912. time. You do this by adding Boolean flags to the left and right pointer
  2913. control loops, which are set when an out-of-place element is scanned.
  2914. If equal keys are present, then Nelson's first program will keep moving the
  2915. high and low pointers so that all the duplicates are left in their proper
  2916. position in the middle. Surprisingly, analysis shows that it is better to stop
  2917. when the pointers are equal to the pivot element's position.
  2918. Wainwright [2] measured the performance of several modifications of QuickSort
  2919. on the same sets of test and got some interesting results. The best
  2920. performance seems to be from what he called Bsort -- a QuickSort with a check
  2921. for sorted order in the partitions.
  2922. References
  2923. [1] Nelson, Mark, "Writing Your Own QuickSort," The C Users Journal, Aug 1990,
  2924. pp 63-73.
  2925. [2] Wainwright, Roger L., "A Class Of Sorting Algorithms Based On QuickSort,"
  2926. Communications of the ACM, Apr 1985, Vol 28, No 4, pp 396-402.
  2927. [3] Celko, Joe, "Bose-Nelson Sort," Dr. Dobb's Journal, September 1985.
  2928. Hoare, C. A. R., "Algorithm 64: QuickSort," Communications of the ACM, April
  2929. 1961, Vol 4, No 7, p 321.
  2930. Sedgewick, Robert, Implementing QuickSort Programs, Communications of the ACM,
  2931. Oct 1978, Vol 21, No 10 pp 847-857.
  2932. Sedgewick, Robert, "QuickSort With Equal Keys," SIAM Journal of Computing, Jun
  2933. 1977, Vol 6, No 2, pp 240-257.
  2934. vanEmdan. M.N., "Algorithm 402: Increasing The Efficiency Of QuickSort,"
  2935. Communications of the ACM, Nov 1970, Vol 13, No 11, pp 693-694.
  2936.  
  2937.  
  2938.  
  2939.  
  2940.  
  2941.  
  2942.  
  2943.  
  2944.  
  2945.  
  2946.  
  2947.  
  2948.  
  2949.  
  2950.  
  2951.  
  2952.  
  2953.  
  2954.  
  2955.  
  2956.  
  2957.  
  2958.  
  2959.  
  2960.  
  2961.  
  2962.  
  2963.  
  2964.  
  2965.  
  2966. Balanced Binary Trees In C++
  2967.  
  2968.  
  2969. Bob Jarvis
  2970.  
  2971.  
  2972. This article is not available in electronic form.
  2973.  
  2974.  
  2975.  
  2976.  
  2977.  
  2978.  
  2979.  
  2980.  
  2981.  
  2982.  
  2983.  
  2984.  
  2985.  
  2986.  
  2987.  
  2988.  
  2989.  
  2990.  
  2991.  
  2992.  
  2993.  
  2994.  
  2995.  
  2996.  
  2997.  
  2998.  
  2999.  
  3000.  
  3001.  
  3002.  
  3003.  
  3004.  
  3005.  
  3006.  
  3007.  
  3008.  
  3009.  
  3010.  
  3011.  
  3012.  
  3013.  
  3014.  
  3015.  
  3016.  
  3017.  
  3018.  
  3019.  
  3020.  
  3021.  
  3022.  
  3023.  
  3024.  
  3025.  
  3026.  
  3027. The iRMX Family Of Operating Systems
  3028.  
  3029.  
  3030. Richard Carver
  3031.  
  3032.  
  3033. Richard Carver is a software engineer with six years experience programming in
  3034. C on iRMX systems. He received his B.S. in computer science from Akron
  3035. University (Akron, Ohio). He has worked on projects involving financial
  3036. transaction processing, security systems and medical diagnostics equipment. He
  3037. may be reached on CompuServe (71061,1754) at the Real-Time Forum (GO REALTIME)
  3038. or by phone at (708) 249-0633.
  3039.  
  3040.  
  3041. iRMX is a "layered real-time object-oriented multi-user multitasking
  3042. interrupted-driven priority-based preemptive operating system." iRMX I, iRMX
  3043. II and iRMX III are a family of operating systems designed for the Intel 80x86
  3044. family of microprocessors. iRMX I uses "Real Mode" addressing, which accesses
  3045. up to 1 megabyte of system memory. iRMX II uses the "Protected Virtual Address
  3046. Mode" (PVAM) of the 80286 and 80386/486 processors, which permits addressing
  3047. up to 16 megabytes. In addition, the protected mode supports segment boundary
  3048. protection, stack-overflow detection, invalid segment detection and segment
  3049. access rights (read, write, or execute). With iRMX III, the 32-bit addressing
  3050. of the 80386/486 allows accessing up to 4 gigabytes using PVAM.
  3051. The iRMX operating systems are supported for three major industry-standard
  3052. 80x86-based bus architectures: Intel's MULTIBUS I & II and IBM's PC/AT
  3053. architecture.
  3054. In almost all cases, applications written for a lower member of the OS family
  3055. can be easily ported to a higher member (e.g., iRMX I to iRMX II). In fact, by
  3056. using the features of the 80386/486 processor for supporting 16-bit and 32-bit
  3057. code, almost all iRMX II applications (executable code) can be run directly
  3058. under iRMX III.
  3059.  
  3060.  
  3061. Basics Of The iRMX Nucleus Layer
  3062.  
  3063.  
  3064. At the heart of the iRMX OS is the Nucleus Layer. This layer of the operating
  3065. system supports scheduling, controls access to system resources, provides
  3066. communication between processes and processors, and enables the system to
  3067. respond to external events. It provides the basic "objects" for all other
  3068. layers (Basic I/O, Extended I/O, Application Loader, and Human Interface) and
  3069. applications. The objects provided by the Nucleus include: Tasks, Jobs,
  3070. Segments, Mailboxes, Semaphores, Regions, Extension Objects, and Composite
  3071. Objects. These objects may be created, destroyed and manipulated by programs.
  3072. When an object is created, a token for the object is returned. This object
  3073. token is used for all references to the object.
  3074. Within the iRMX OS, work is performed by tasks. The tasks operate within the
  3075. environment provided by the job. This includes the tasks, objects used by the
  3076. tasks, a directory for tasks to catalog objects and a memory pool. Every job
  3077. has at least one task to refer to as the "initial" task. This task may create
  3078. other tasks to accomplish the work of the program. Tasks may also create new
  3079. jobs (child jobs) with their own environments. Creating new jobs results in a
  3080. Job Tree with parent/child connections. Tasks creating other tasks do not
  3081. result in parent/child relationships. Tasks within a single job are part of
  3082. the job as a whole.
  3083. Tasks may communicate with each other using the iRMX "Exchange Objects." The
  3084. three types of exchange objects are mailboxes, semaphores, and regions.
  3085. Mailboxes can be either data mailboxes, used for sending and receiving data,
  3086. or object mailboxes, which are used for sending and receiving objects.
  3087. Mailboxes are created and deleted using the rqcreatemailbox and
  3088. rqdeletemailbox system calls. Data mailboxes are used with the rqsenddata and
  3089. rqreceivedata system calls to transfer data (up to 128 bytes) from one task to
  3090. another. Data is physically copied from the sender to the receiver using the
  3091. mailbox as a transfer buffer. Object mailboxes are used with the rqsendmessage
  3092. and rqreceivemessage system call to pass object tokens between tasks. The
  3093. object token may be for any valid iRMX object (e.g., tasks, jobs, segments,
  3094. mailboxes, semaphores, regions).
  3095. Semaphores are the "keepers of units." A unit is an abstract object that can
  3096. be sent to or received from a semaphore. Semaphores are often used for
  3097. controlling access to shared system resources (e.g., data, devices) or for
  3098. synchronizing tasks. iRMX semaphores can also be used as "counting"
  3099. semaphores, which means that they are capable of holding more than a single
  3100. unit. Semaphores are supported through the rqcreatesemaphore,
  3101. rqdeletesemaphore, rqsendunits, and rqreceiveunits system calls.
  3102. Regions are special forms of semaphores. Tasks may receive or release control
  3103. of regions. In this manner, regions work like "single unit" semaphores.
  3104. However, when a task receives control of a region, its priority is increased
  3105. to allow the task to run until it releases the region. Also, the task cannot
  3106. be suspended or deleted until the region is released. Because of this, regions
  3107. are only used in situations where semaphores do not provide enough control.
  3108. Regions are supported through the rqcreateregion, rqdeleteregion,
  3109. rqsendcontrol, and rqreceivecontrol system calls.
  3110.  
  3111.  
  3112. Sharing Through Tasks
  3113.  
  3114.  
  3115. You can accomplish access to shared resources, such as data or devices, by
  3116. using semaphores. However, sometimes it makes more sense to implement resource
  3117. control through tasks. For a given resource, such as a terminal, a task is
  3118. written to provide the services of this resource. The task accepts requests
  3119. from other tasks for operations to be performed, such as printing a message to
  3120. the display.
  3121. The example program consists of five tasks. The first task is the initial task
  3122. which it responsible for creating all other tasks and program termination. The
  3123. clock and crt tasks act as resource managers for the clock and crt,
  3124. respectively. The count and timer tasks are the resource consumers.
  3125. This program uses only semaphores and object mailboxes for communication.
  3126. Regions are rarely used and are specifically not recommended for use in
  3127. operator-executed programs. Data mailboxes are not used because they only
  3128. provide for one-way communications. Typically, two-way communication, such as
  3129. that provided by rqsendmessage and rqreceivemessage with object mailboxes,
  3130. provides task synchronization.
  3131.  
  3132.  
  3133. The Initial Task
  3134.  
  3135.  
  3136. The initial task is the main() program. It creates many of the objects used
  3137. during the program and catalogs these objects so that the other tasks may use
  3138. them. This task then creates one task at a time, each time waiting for a task
  3139. to indicate that it has completed initialization by sending a unit, using
  3140. rqsendunit, to the initialization semaphore (init_sem). When the last task,
  3141. timer_task(), signals its completion, the shutdown process begins. Each
  3142. remaining task is sent a unit to its respective shutdown semaphores. When all
  3143. tasks indicate they have shutdown, all objects are cleaned up (uncatalogued
  3144. and deleted) and the program is terminated.
  3145.  
  3146.  
  3147. The Clock Task
  3148.  
  3149.  
  3150. The clock_task() provides access to the hardware clock and implements the
  3151. software timers. All timers are maintained using the single hardware interrupt
  3152. from the clock board. This task also maintains the display time/date by
  3153. communicating with the crt_task().
  3154. The clock_task has been set-up to service an interrupt. It became an interrupt
  3155. task when it called the rqsetinterrupt system call. This call specifies the
  3156. interrupt to be serviced and the address of an interrupt handler. The handler
  3157. is not the task, it is a function that receives control when the interrupt
  3158. occurs. The interrupt handler passes control to the interrupt task by using
  3159. rqsignal interrupt. The interrupt task is waiting to receive control through
  3160. rqetimedinterrupt. The main difference between the handler and the task is
  3161. that all interrupts are disabled when the handler is in control. Only the
  3162. interrupt being serviced is disabled when the task in control. Also, interrupt
  3163. handlers are restricted to interrupt-related system calls. Since this
  3164. implementation needs to access semaphores and mailboxes, it must use an
  3165. interrupt task.
  3166. When an interrupt occurs, the task first requests access to the shared
  3167. time/date data segment. It then checks to see if the hardware clock needs to
  3168. be changed (supported but not used in example). If no change is required, the
  3169. current time/date is advanced by one second. Next, all software timers, if
  3170. any, are advanced by one second. If a timer's timeout value is reached, a unit
  3171. is sent to the timer semaphore and the timer restarts. Finally, the time/date
  3172. is formatted and sent to the crt_task() for display. This task communication
  3173. is synchronous, but the send and receive do not occur one after the other. A
  3174. display message is not sent unless the response to the last request has been
  3175. received. This prevents the task from being delayed during interrupt
  3176. processing and prevents a build up of request messages when the crt_task() is
  3177. busy (which could occur since the crt_task() has a lower priority). The minor
  3178. side effect is that the time/date display may skip a second when then
  3179. crt_task() is busy.
  3180. When the task receives the shutdown signal, it waits for the outstanding
  3181. display response and calls rqresetinterrupt to disable itself as an interrupt
  3182. processing task. It will then suspend itself to await deletion.
  3183.  
  3184.  
  3185. The Crt Task
  3186.  
  3187.  
  3188. The crt_task() provides shared access to the video display. This is a basic
  3189. implementation, providing only a means of printing a string at a specific row
  3190. and column of the display. The task receives request messages that indicate a
  3191. message to be displayed and the location to display the message. After
  3192. displaying the message, the task determines if a response is expected. If the
  3193. calling task indicated a "response mailbox" in the rqsendmessage call, then it
  3194. will be expecting a response. This facility is used for synchronizing the task
  3195. communication. The response will be the original message segment. If the task
  3196. did not expect a response, the "response mailbox" value will be the null token
  3197. (NUL_TKN). In this case, this task will free the request message segment.
  3198. When the task receives the shutdown signal, it will go into an infinite loop
  3199. of receiving requests and returning response messages (if a response is
  3200. required). Since many tasks send a request and then wait for the response, you
  3201. don't want tasks getting stuck waiting for responses. This could happen if the
  3202. crt_task() quits responding after a shutdown signal. Requests messages will
  3203. pile up in the request mailbox and will never be processed. Minimum processing
  3204. to send the response is needed.
  3205.  
  3206.  
  3207. The Count Task
  3208.  
  3209.  
  3210.  
  3211. The count_task() continuously counts from 0-65535 and displays the value in
  3212. the middle of the screen by communicating with the crt_task(). This task's
  3213. purpose is to help demonstrate how multiple tasks can use the crt_task() to
  3214. safely share the video display.
  3215. Upon receiving a unit at its shutdown semaphore, the task suspends itself to
  3216. await deletion.
  3217.  
  3218.  
  3219. The Timer Task
  3220.  
  3221.  
  3222. The timer_task() uses a software timer, maintained by the clock_task(), to
  3223. determine when the program should be terminated. The task creates a one-second
  3224. timer and waits for the timer to be signalled 20 times (20 seconds). The timer
  3225. is actually a semaphore that has been set up so that the clock_task() sends a
  3226. unit to the semaphore every second. The timer_task() calls rqreceiveunits to
  3227. wait for 20 units to be sent to the semaphore. (A single unit from a 20-second
  3228. timer could also be used.) When 20 seconds have elapsed, the task sends a unit
  3229. to the initialization semaphore (init_sem). In this case, receiving a unit at
  3230. init_sem not only indicates that the task has initialized but that it is also
  3231. time to terminate the program. Since it initiates the shutdown signal process,
  3232. it suspends itself.
  3233.  
  3234.  
  3235. Summary
  3236.  
  3237.  
  3238. The clock_task() is a fairly complete task. There isn't much more useful
  3239. functionality that would be added to service a hardware clock. On the other
  3240. hand, the crt_task() is only the core of what could be a sophisticated display
  3241. servicing task supporting many features (e.g., video attributes, hotkeys,
  3242. windows).
  3243. Remember, the example program is just that -- an example of task
  3244. communications. It is by no means the only way to implement and organize tasks
  3245. within an application program. Because multitasking operating systems are
  3246. usually very complex, you should use good design and modular programming
  3247. techniques. But don't let this scare you, once you become familiar with a
  3248. multitasking operating system, such as iRMX, you'll wonder know how you ever
  3249. got along without it. 
  3250. iRMX C Code For 80x86 Processors
  3251. The ANSI C compiler for the iRMX operating system provides many features for
  3252. working with the 80x86 family of processors. The selector data type is used to
  3253. designate tokens that basically correspond to the addresses of object
  3254. descriptors. The selector type could be replaced with the unsigned int type.
  3255. The example uses the selector type because it is used by all system call
  3256. prototypes in RMXC. H. The #pragma interrupt directive tells the compiler to
  3257. generate the proper prologue and epilogue (e.g., IRET) for an interrupt
  3258. function. The are also many built-in functions for dealing directly with the
  3259. 80x86 processor. In this example, inbyte and outbyte functions are used for
  3260. reading and writing I/O ports.
  3261.  
  3262. Listing 1 (clockint.c)
  3263. /*0*********************************************************
  3264. * Richard Carver July 1990
  3265. *
  3266. * This program demonstrates interprocess communication
  3267. * between several tasks using the iRMX II Operating
  3268. * System.
  3269. *
  3270. * Two tasks are used for resource control. The
  3271. * clock_task() controls use of the hardware clock and the
  3272. * crt_task() controls use of the display screen.
  3273. *
  3274. * The count_task() competes with the clock_task() for
  3275. * use of the crt_task() services. The timer_task() uses
  3276. * the services of the clock_task() for a software timer.
  3277. *
  3278. * The initial task, main(), starts every thing up and,
  3279. * when the timer_task() finishes, shuts everything down.
  3280. ***********************************************************/
  3281.  
  3282. #include <stdio.h>
  3283. #include <stdlib.h>
  3284. #include <string.h>
  3285. #include <rmxc.h> /* iRMX OS calls & structures */
  3286. #include <rmxerr.h> /* iRMX status codes (defines) */
  3287. #include <delay.h> /* Hardcoded delay loop macros */
  3288.  
  3289. #define ERROR (0xFFFF) /* Error Condition */
  3290.  
  3291. #define FALSE (0)
  3292. #define TRUE (!FALSE)
  3293.  
  3294. #define MAX_CLOCK_RETRIES (4)
  3295.  
  3296. /* The NULL token (similar to NULL pointer) */
  3297.  
  3298. #define NUL_TKN ((selector)0)
  3299.  
  3300. /* Options for rqgettasktokens() */
  3301.  
  3302. #define THIS_TASK (0x00)
  3303.  
  3304. #define THIS_JOB (0x01)
  3305. #define PARAM_OBJ (0x02)
  3306. #define ROOT_JOB (0x03)
  3307.  
  3308. /* Options for rqcreatemailbox() */
  3309.  
  3310. #define FIFO_OBJ_MBX (0x0000)
  3311. #define PRIORITY_OBJ_HBX (0x0001)
  3312. #define FIFO_DATA_MBX (0x0020)
  3313. #define PRIORITY_DATA_MBX (0x0021)
  3314.  
  3315. /* Options for rqcreatesemaphore() */
  3316.  
  3317. #define FIFO_SEM (0x0000)
  3318. #define PRIORITY_SEM (0x0001)
  3319.  
  3320. #define SEM_INIT_0 (0)
  3321. #define SEM_MAX_1 (1)
  3322.  
  3323. /* Common wait times for iRMX System Calls */
  3324.  
  3325. #define NO_WAIT (0x0000)
  3326. #define WAIT_FOREVER (0xFFFF)
  3327.  
  3328. /* Options for rqcreatetask() */
  3329.  
  3330. #define NO_FLOAT (0x0000)
  3331. #define DEFAULT_STACK_SIZE ((unsigned int)2048)
  3332.  
  3333. /* Task Priorities */
  3334.  
  3335. #define CLOCK_TASK_PRIORITY ((unsigned char)48)
  3336. #define TIMER_TASK_PRIORITY ((unsigned char)150)
  3337. #define CRT_TASK_PRIORITY ((unsigned char)200)
  3338. #define COUNT_TASK_PRIORITY ((unsigned char)200)
  3339.  
  3340. /* Defines For Hardware Clock */
  3341. /* Clock Interrupt Level */
  3342.  
  3343. #define CLOCK_INT_LEVEL (0x26)
  3344.  
  3345. /* Clock Ports */
  3346.  
  3347. #define ADR_PORT ((unsigned short)0xA060)
  3348. #define DAT_PORT ((unsigned short)0xA062)
  3349. #define CNT_PORT ((unsigned short)0xA064)
  3350. #define PIO_PORT ((unsigned short)0xA066)
  3351.  
  3352. /* Clock Port I/O Operations */
  3353.  
  3354. #define PIO_READ ((unsigned char)0x82)
  3355. #define PIO_WRITE ((unsigned char)0x80)
  3356.  
  3357. /* Clock Registers */
  3358.  
  3359. #define SEC01_REG (0x00)
  3360. #define SEC10_REG (0x01)
  3361. #define MIN01_REG (0x02)
  3362. #define MIN10_REG (0x03)
  3363.  
  3364. #define HRS01_REG (0x04)
  3365. #define HRS10_REG (0x05)
  3366. #define DAT01_REG (0x07)
  3367. #define DAT10_REG (0x08)
  3368. #define MON01_REG (0x09)
  3369. #define MON10_REG (0x0A)
  3370. #define YRS01_REG (0x0B)
  3371. #define YRS10_REG (0x0C)
  3372.  
  3373. /* Clock Control Lines */
  3374.  
  3375. #define CNT_RST ((unsigned char)0x00)
  3376. #define INT_ENB ((unsigned char)0x20)
  3377. #define HOLD_HIGH ((unsigned char)0x10)
  3378. #define HOLD_READ ((unsigned char)0x30)
  3379. #define HOLD_WRITE ((unsigned char)0x50)
  3380.  
  3381. /* Defines for months */
  3382.  
  3383. #define JANUARY (1)
  3384. #define FEBRUARY (2)
  3385. #define MARCH (3)
  3386. #define DECEMBER (12)
  3387.  
  3388. /* Time/Date and Timers Data Structure */
  3389.  
  3390. #define MAX_TIMERS (10)
  3391.  
  3392. struct SYS_TIME_STR
  3393. {
  3394. unsigned char change_flg;
  3395. unsigned char second;
  3396. unsigned char minute;
  3397. unsigned char hour;
  3398. unsigned char date;
  3399. unsigned char month;
  3400. unsigned char year;
  3401.  
  3402. struct TIMER_STR
  3403. {
  3404. unsigned char in_use;
  3405. unsigned int count;
  3406. unsigned int timeout;
  3407. selector sem_tkn;
  3408. }timer[MAX_TIMERS];
  3409. };
  3410.  
  3411. /* Request Message format for the Display Task */
  3412.  
  3413. struct CRT_REQ_STR
  3414. {
  3415. unsigned char *msg_ptr;
  3416. unsigned int row;
  3417. unsigned int col;
  3418. };
  3419.  
  3420. /* Object Catalog Names */
  3421.  
  3422. const unsigned char
  3423.  
  3424. clock_sem_name[] = "\011CLOCK_SEM",
  3425. init_sem_name[] = "\010INIT_SEM";
  3426.  
  3427. const unsigned char
  3428. time_seg_name[] = "\010TIME_SEG";
  3429.  
  3430. const unsigned char
  3431. crt_req_mbx_name[] = "\007CRT_MBX";
  3432.  
  3433. const unsigned char
  3434. crt_shtdwn_sem_name[] = "\012CRT_SHTDWN",
  3435. clock_shtdwn_sem_name[] = "\014CLOCK_SHTDWN",
  3436. count_shtdwn_sem_name[] = "\014COUNT_SHTDWN";
  3437.  
  3438. /* Days Per Month */
  3439.  
  3440. const unsigned char
  3441. days_in_month[12] =
  3442. {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  3443.  
  3444. /* Global System Time/Date Structure */
  3445.  
  3446. selector sys_time_seg;
  3447. struct SYS_TIME_STR *sys_time;
  3448.  
  3449. /* Task declarations */
  3450.  
  3451. void clock_task(void);
  3452. void crt_task(void);
  3453. void timer_task(void);
  3454. void count_task(void);
  3455.  
  3456. /* Task objects (global for debugging purposes) */
  3457.  
  3458. selector clock_tsk;
  3459. selector crt_tsk;
  3460. selector timer_tsk;
  3461. selector count_tsk;
  3462.  
  3463. /*1*********************************************************
  3464. * Task: main() (the initial task)
  3465. *
  3466. * Summary: Starts it all up and shuts it all down.
  3467. *
  3468. * Caveats: None
  3469. ***********************************************************/
  3470.  
  3471. void main(void)
  3472. {
  3473. selector current_job; /* current job */
  3474. selector init_sem; /* initilaization sem */
  3475. selector clock_sem; /* time/date shared memory*/
  3476. selector clock_shtdwn_sem; /* clock_task() shutdown */
  3477. selector count_shtdwn_sem; /* count_task() shutdown */
  3478. selector crt_shtdwn_sem; /* crt_task() shutdown */
  3479. selector sys_time_seg; /* time/date data segment */
  3480.  
  3481. unsigned int status; /* iRMX condition code */
  3482.  
  3483.  
  3484. struct EXCEPTIONSTRUCT exceptioninfo;
  3485.  
  3486. /* Disable current task's exception handler */
  3487.  
  3488. rqgetexceptionhandler(&exceptioninfo, &status);
  3489. exceptioninfo.exceptionmode = 0;
  3490. rqsetexceptionhandler(&exceptioninfo, &status);
  3491. /* Get Token For Current Job */
  3492.  
  3493. current_job = rqgettasktokens(THIS_JOB, &status);
  3494.  
  3495. /* Create and catalog the semaphore used for */
  3496. /* accessing the shared clock data. */
  3497.  
  3498. clock_sem = rqcreatesemaphore(SEM_INIT_0,
  3499. SEM_MAX_1, PRIORITY_SEM, &status);
  3500. rqcatalogobject(current_job,
  3501. clock_sem, &clock_sem_name, &status);
  3502.  
  3503. /* Create and catalog the semaphore used for */
  3504. /* synchronizing tasks during initialization */
  3505. /* and shutdown. */
  3506.  
  3507. init_sem = rqcreatesemaphore(SEM_INIT_0,
  3508. 99, FIFO_SEM, &status);
  3509. rqcatalogobject(current_job,
  3510. init_sem, &init_sem_name, &status);
  3511.  
  3512. /* Create and catalog the segment of memory */
  3513. /* used to hold the shared time/date data. */
  3514.  
  3515. sys_time_seg = rqcreatesegment(
  3516. sizeof(struct SYS_TIME_STR), &status);
  3517. rqcatalogobject(current_job,
  3518. sys_time_seg, &time_seg_name, &status);
  3519.  
  3520. /* Create and catalog the semaphores used to */
  3521. /* signal task shutdown. */
  3522.  
  3523. clock_shtdwn_sem = rqcreatesemaphore(SEM_INIT_0,
  3524. SEM_MAX_1, FIFO_SEM, &status);
  3525. rqcatalogobject(current_job,
  3526. clock_shtdwn_sem, &clock_shtdwn_sem_name, &status);
  3527.  
  3528. crt_shtdwn_sem = rqcreatesemaphore(SEM_INIT_0,
  3529. SEM_MAX_1, FIFO_SEM, &status);
  3530. rqcatalogobject(current_job,
  3531. crt_shtdwn_sem, &crt_shtdwn_sem_name, &status);
  3532.  
  3533. count_shtdwn_sem = rqcreatesemaphore(SEM_INIT_0,
  3534. SEM_MAX_1, FIFO_SEM, &status);
  3535. rqcatalogobject(current_job,
  3536. count_shtdwn_sem, &count_shtdwn_sem_name, &status);
  3537.  
  3538. /* Create the crt_task(). Wait for the task to */
  3539. /* indicate it has completed initialization. */
  3540.  
  3541. crt_tsk = rqcreatetask(CRT_TASK_PRIORITY, &crt_task,
  3542. NUL_TKN, NULL, DEFAULT_STACK_SIZE, NO_FLOAT, &status);
  3543.  
  3544.  
  3545. rqreceiveunits(init_sem, 1, WAIT_FOREVER, &status);
  3546.  
  3547. /* Create the clock_task(). Wait for the task */
  3548. /* to indicate it has completed initialization. */
  3549.  
  3550. clock_tsk = rqcreatetask(CLOCK_TASK_PRIORITY, &clock_task,
  3551. NUL_TKN, NULL, DEFAULT_STACK_SIZE, NO_FLOAT, &status);
  3552.  
  3553. rqreceiveunits(init_sem, 1, WAIT_FOREVER, &status);
  3554.  
  3555. /* Create the count_task(). Wait for the task */
  3556. /* to indicate it has completed initialization. */
  3557.  
  3558. count_tsk = rqcreatetask(COUNT_TASK_PRIORITY, &count_task,
  3559. NUL_TKN, NULL, DEFAULT_STACK_SIZE, NO_FLOAT, &status);
  3560.  
  3561. rqreceiveunits(init_sem, 1, WAIT_FOREVER, &status);
  3562.  
  3563. /* Create the timer_task(). Wait for the task */
  3564. /* to indicate it has completed initialization. */
  3565. /* This also signals the shutdown process. */
  3566. timer_tsk = rqcreatetask(TIMER_TASK_PRIORITY, &timer_task,
  3567. NUL_TKN, NULL, DEFAULT_STACK_SIZE, NO_FLOAT, &status);
  3568.  
  3569. rqreceiveunits(init_sem, 1, WAIT_FOREVER, &status);
  3570.  
  3571. /* Begin shutdown by first disabling any further */
  3572. /* interrupts from the hardware clock. This task */
  3573. /* suspends itself for 2/100th of a second to */
  3574. /* ensure interrupt is disabled. */
  3575.  
  3576. rqdisable(CLOCK_INT_LEVEL, &status);
  3577. rqsleep(2, &status);
  3578.  
  3579. /* Send shutdown signals to all tasks and wait */
  3580. /* for all tasks to complete shutdown. */
  3581.  
  3582. rqsendunits(count_shtdwn_sem, 1, &status);
  3583. rqsendunits(crt_shtdwn_sem, 1, &status);
  3584. rqsendunits(clock_shtdwn_sem, 1, &status);
  3585.  
  3586. rqreceiveunits(init_sem, 3, WAIT_FOREVER, &status);
  3587.  
  3588. /* Cleanup before terminating. This includes */
  3589. /* deleting tasks and uncataloging/deleteing */
  3590. /* all objects created. */
  3591.  
  3592. rqdeletetask(crt_tsk, &status);
  3593. rqdeletetask(clock_tsk, &status);
  3594. rqdeletetask(count_tsk, &status);
  3595. rqdeletetask(timer_tsk, &status);
  3596.  
  3597. rquncatalogobject(current_job,
  3598. &clock_sem_name, &status);
  3599. rquncatalogobject(current_job,
  3600. &init_sem_name, &status);
  3601. rquncatalogobject(current_job,
  3602. &time_seg_name, &status);
  3603.  
  3604. rquncatalogobject(current_job,
  3605. &crt_req_mbx_name, &status);
  3606.  
  3607. rquncatalogobject(current_job,
  3608. &clock_shtdwn_sem_name, &status);
  3609. rquncatalogobject(current_job,
  3610. &count_shtdwn_sem_name, &status);
  3611. rquncatalogobject(current_job,
  3612. &crt_shtdwn_sem_name, &status);
  3613.  
  3614. rqdeletesemaphore(clock_sem, &status);
  3615. rqdeletesemaphore(init_sem, &status);
  3616.  
  3617. rqdeletesemaphore(clock_shtdwn_sem, &status);
  3618. rqdeletesemaphore(count_shtdwn_sem, &status);
  3619. rqdeletesemaphore(crt_shtdwn_sem, &status);
  3620.  
  3621. rqdeletesegment(sys_time_seg, &status);
  3622.  
  3623. cursor_on();
  3624. printf("\nAll Done!\n");
  3625.  
  3626. exit(0);
  3627. }
  3628.  
  3629. /*1*********************************************************
  3630. * Function: start_timer
  3631. *
  3632. * Summary: Create and start a timer.
  3633. *
  3634. * Invocation: timer_sem = start_timer(timeout);
  3635. *
  3636. * Inputs: timeout (unsigned int) - timer interval
  3637. * in seconds
  3638. *
  3639. * Outputs: timer_sem (selector) - the semaphore that will
  3640. * receive a unit at each timer interval
  3641. *
  3642. * Caveats: Accesses the time/date data segment that is
  3643. * shared with the clock_task().
  3644. ***********************************************************/
  3645.  
  3646. selector start_timer(unsigned int timeout)
  3647. {
  3648. unsigned int status;
  3649. unsigned int indx;
  3650. unsigned int done;
  3651. unsigned int no_more_timers;
  3652.  
  3653. selector timer_sem;
  3654. selector current_job;
  3655. selector clock_sem;
  3656. selector sys_time_seg;
  3657.  
  3658. struct SYS_TIME_STR *sys_time;
  3659.  
  3660. indx = 0;
  3661. done = FALSE;
  3662. no_more_timers = TRUE;
  3663.  
  3664. timer_sem = NUL_TKN;
  3665.  
  3666. /* Get objects for access semaphore and */
  3667. /* time/date data segment */
  3668.  
  3669. current_job = rqgettasktokens(THIS_JOB, &status);
  3670.  
  3671. clock_sem = rqlookupobject(current_job,
  3672. &clock_sem_name, NO_WAIT, &status);
  3673. sys_time_seg = rqlookupobject(current_job,
  3674. &time_seg_name, NO_WAIT, &status);
  3675.  
  3676. sys_time = buildptr(sys_time_seg, 0);
  3677.  
  3678. /* Access time/date memory */
  3679.  
  3680. rqreceiveunits(clock_sem, 1, WAIT_FOREVER, &status);
  3681.  
  3682. /* Find an empty timer slot */
  3683.  
  3684. while (!done)
  3685. {
  3686. if (!sys_time->timer[indx].in_use)
  3687. {
  3688. done = TRUE;
  3689. no_more_timers = FALSE;
  3690. }
  3691. else
  3692. {
  3693. indx++;
  3694.  
  3695. if (indx == MAX_TIMERS)
  3696. {
  3697. done = TRUE;
  3698. }
  3699. }
  3700. }
  3701.  
  3702. /* Establish a timer */
  3703.  
  3704. if (no_more_timers == FALSE)
  3705. {
  3706. if (timeout ! = 0)
  3707. {
  3708. timer_sem = rqcreatesemaphore(SEM_INIT_0,
  3709. 999, FIFO_SEM, &status);
  3710. sys_time->timer[indx].in_use = TRUE;
  3711. sys_time->timer[indx].count = 0;
  3712. sys_time->timer[indx].timeout = timeout;
  3713. sys_time->timer[indx].sem_tkn = timer_sem;
  3714. }
  3715. }
  3716. /* Release time/date memory */
  3717.  
  3718. rqsendunits(clock_sem, 1, &status);
  3719.  
  3720. /* Return timer semaphore to caller */
  3721.  
  3722. return(timer_sem);
  3723.  
  3724. }
  3725.  
  3726. /*1*********************************************************
  3727. * Function: stop_timer
  3728. *
  3729. * Summary: Delete a timer.
  3730. *
  3731. * Invocation: timer_sem = start_timer(timeout);
  3732. *
  3733. * Inputs: timeout (unsigned int) - timer interval
  3734. * in seconds
  3735. *
  3736. * Outputs: timer_sem (selector) - the semaphore that will
  3737. * receive a unit at each timer interval
  3738. *
  3739. * Caveats: Accesses the time/date data segment that is
  3740. * shared with the clock_task().
  3741. ***********************************************************/
  3742.  
  3743. unsigned int stop_timer(selector timer_sem)
  3744. {
  3745. unsigned int status;
  3746. unsigned int indx;
  3747. unsigned int done;
  3748. unsigned int invalid_timer;
  3749.  
  3750. selector current_job;
  3751. selector clock_sem;
  3752. selector sys_time_seg;
  3753.  
  3754. struct SYS_TIME_STR *sys_time;
  3755.  
  3756. indx = 0;
  3757. done = FALSE;
  3758. invalid_timer = TRUE;
  3759.  
  3760. /* Get objects for access semaphore and */
  3761. /* time/date data segment */
  3762.  
  3763. current_job = rqgettasktokens(THIS_JOB, &status);
  3764.  
  3765. clock_sem = rqlookupobject(current_job,
  3766. &clock_sem_name, NO_WAIT, &status);
  3767. sys_time_seg = rqlookupobject(current_job,
  3768. &time_seg_name, NO_WAIT, &status);
  3769.  
  3770. sys_time = buildptr(sys_time_seg, 0);
  3771.  
  3772. /* Access time/date memory. */
  3773.  
  3774. rqreceiveunits(clock_sem, 1, WAIT_FOREVER, &status);
  3775.  
  3776. /* Locate and remove this timer */
  3777.  
  3778. while (!done)
  3779. {
  3780. if (sys_time->timer[indx].in_use &&
  3781. (sys_time->timer[indx].sem_tkn == timer_sem))
  3782. {
  3783.  
  3784. rqdeletesemaphore(timer_sem, &status);
  3785. sys_time->timer[indx].in_use = FALSE;
  3786. sys_time->timer[indx].sem_tkn = NUL_TKN;
  3787. sys_time->timer[indx].count = 0;
  3788. sys_time->timer[indx].timeout = 0;
  3789. done = TRUE;
  3790. invalid_timer = FALSE;
  3791. }
  3792. else
  3793. {
  3794. indx++;
  3795.  
  3796. if (indx == MAX_TIMERS)
  3797. {
  3798. done = TRUE;
  3799. }
  3800. }
  3801. }
  3802.  
  3803. /* Release time/date memory */
  3804.  
  3805. rqsendunits(clock_sem, 1, &status);
  3806.  
  3807. /* Return with result of call */
  3808.  
  3809. return (invalid_timer ? ERROR : EOK);
  3810. }
  3811.  
  3812. /*1*********************************************************
  3813. * Task: count_task
  3814. *
  3815. * Summary: Continuously counts and displays a value from
  3816. * 0-65535.
  3817. *
  3818. * Caveats: Communicates with the crt_task() for displaying
  3819. * counter values.
  3820. ***********************************************************/
  3821.  
  3822. void count_task(void)
  3823. {
  3824. unsigned char counter_buf[6];
  3825.  
  3826. selector current_job;
  3827. selector rsp_mbx;
  3828. selector crt_req_mbx;
  3829. selector init_sem;
  3830. selector count_shtdwn_sem;
  3831. selector req_seg;
  3832.  
  3833. unsigned int counter;
  3834. unsigned int status;
  3835.  
  3836. struct CRT_REQ_STR *req_msg;
  3837.  
  3838. struct EXCEPTIONSTRUCT exceptioninfo;
  3839.  
  3840. /* Disable this task's exception handler */
  3841.  
  3842. rqgetexceptionhandler(&exceptioninfo, &status);
  3843.  
  3844. exceptioninfo.exceptionmode = 0;
  3845. rqsetexceptionhandler(&exceptioninfo, &status);
  3846.  
  3847. /* Lookup/Create needed objects */
  3848.  
  3849. current_job = rqgettasktokens(THIS_JOB, &status);
  3850.  
  3851. init_sem = rqlookupobject(current_job,
  3852. &init_sem_name, NO_WAIT, &status);
  3853.  
  3854. count_shtdwn_sem = rqlookupobject(current_job,
  3855. &count_shtdwn_sem_name, NO_WAIT, &status);
  3856.  
  3857. rsp_mbx = rqcreatemailbox(FIFO_OBJ_MBX, &status);
  3858.  
  3859. crt_req_mbx = rqlookupobject(current_job,
  3860. &crt_req_mbx_name, NO_WAIT, &status);
  3861.  
  3862. /* Initialize request message */
  3863.  
  3864. req_seg = rqcreatesegment(
  3865. sizeof(struct CRT_REQ_STR), &status);
  3866.  
  3867. req_msg = buildptr(req_seg, 0);
  3868.  
  3869. req_msg->msg_ptr = counter_buf;
  3870. req_msg->row = 12;
  3871. req_msg->col = 38;
  3872.  
  3873. /* Singal that initialization is completed. */
  3874.  
  3875. rqsendunits(init_sem, 1, &status);
  3876.  
  3877. /* Continuously count 0-65535 and display the */
  3878. /* value until signalled to stop. */
  3879.  
  3880. counter = 0;
  3881.  
  3882. rqreceiveunits(count_shtdwn_sem, 1, NO_WAIT, &status);
  3883.  
  3884. while (status == ETIME) /* while no signal received */
  3885. {
  3886. sprintf(counter_buf, "%d", counter);
  3887. rqsendmessage(crt_req_mbx,
  3888. req_seg, rsp_mbx, &status);
  3889. req_seg = rqreceivemessage(rsp_mbx,
  3890. WAIT_FOREVER, NUL_TKN, &status);
  3891. counter++;
  3892. rqreceiveunits(count_shtdwn_sem, 1, NO_WAIT, &status);
  3893. }
  3894.  
  3895. /* Cleanup objects created by this task. */
  3896.  
  3897. rqdeletemailbox(rsp_mbx, &status);
  3898. rqdeletesegment(req_seg, &status);
  3899.  
  3900. /* Signal shutdown complete and suspend execution. */
  3901.  
  3902. rqsendunits(init_sem, 1, &status);
  3903.  
  3904.  
  3905. rqsuspendtask(NUL_TKN, &status); /* suspend this task */
  3906. }
  3907.  
  3908. /*1************************************************************
  3909. * Task: timer_task
  3910. *
  3911. * Summary:
  3912. *
  3913. * Caveats:
  3914. **************************************************************/
  3915.  
  3916. void timer_task(void)
  3917. {
  3918. selector current_job;
  3919. selector init_sem;
  3920. selector clock_sem;
  3921. selector timer_sem;
  3922.  
  3923. unsigned int status;
  3924.  
  3925. struct EXCEPTIONSTRUCT exceptioninfo;
  3926.  
  3927. /* Disable this task's exception handler */
  3928.  
  3929. rqgetexceptionhandler(&exceptioninfo, &status);
  3930. exceptioninfo.exceptionmode = 0;
  3931. rqsetexceptionhandler(&exceptioninfo, &status);
  3932.  
  3933. /* Lookup needed objects */
  3934.  
  3935. current_job = rqgettasktokens(THIS_JOB, &status);
  3936.  
  3937. init_sem = rqlookupobject(current_job,
  3938. &init_sem_name, NO_WAIT, &status);
  3939.  
  3940. /* Start a one-second timer. If the timer was */
  3941. /* created, wait for 20 seconds (units) then stop */
  3942. /* stop the timer. */
  3943. timer_sem = start_timer(1);
  3944.  
  3945. if (timer_sem != NUL_TKN)
  3946. {
  3947. rqreceiveunits(timer_sem, 20, WAIT_FOREVER, &status);
  3948. stop_timer(timer_sem);
  3949. }
  3950.  
  3951. /* Signal task finished and suspend execution. */
  3952.  
  3953. rqsendunits(init_sem, 1, &status);
  3954.  
  3955. rqsuspendtask(NUL_TKN, &status); /* suspend this task */
  3956. }
  3957.  
  3958. /*1**********************************************************
  3959. * Functions: clrscrn, cursor_on, cursor_off, print_at
  3960. *
  3961. * Summary: Functions for basic display control.
  3962. *
  3963.  
  3964. * Invocation: clrscrn( );
  3965. * curson_on( );
  3966. * cursoff_on( );
  3967. *
  3968. * Invocation: print_at(row, col, msg_ptr)
  3969. *
  3970. * Inputs: row (unsigned int) - row position
  3971. * col (unsigned int) - column position
  3972. * msg_ptr (unsigned char *) - message string pointer
  3973. *
  3974. * Caveats: Escape sequences are for a WYSE60 terminal.
  3975. **************************************************************/
  3976.  
  3977. void clrscrn(void)
  3978. {
  3979. fprintf(stdout, "\x1B+");
  3980. fflush(stdout);
  3981. return;
  3982. }
  3983.  
  3984. void cursor_on(void)
  3985. {
  3986. fprintf(stdout, "\x1B`1");
  3987. fflush(stdout);
  3988. return;
  3989. }
  3990.  
  3991. void cursor_off(void)
  3992. {
  3993. fprintf(stdout, "\x1B'0");
  3994. fflush(stdout);
  3995. return;
  3996. }
  3997.  
  3998. void print_at(unsigned int row,
  3999. unsigned int col, unsigned char *msg_ptr)
  4000. {
  4001. fprintf(stdout, "\x1B=%c%c%s",
  4002. (row + 31), (col + 31), msg_ptr);
  4003. fflush(stdout);
  4004. return;
  4005. }
  4006.  
  4007. /*1*********************************************************
  4008. * Task: crt_task
  4009. *
  4010. * Summary:
  4011. *
  4012. * Caveats:
  4013. ***********************************************************/
  4014.  
  4015. void crt_task(void)
  4016. {
  4017. selector current_job;
  4018. selector req_mbx;
  4019. selector rsp_mbx;
  4020. selector rec_seg;
  4021. selector init_sem;
  4022. selector crt_shtdwn_sem;
  4023.  
  4024.  
  4025. unsigned int status;
  4026.  
  4027. struct CRT_REQ_STR *req_msg;
  4028.  
  4029. struct EXCEPTIONSTRUCT exceptioninfo;
  4030.  
  4031. /* Disable this task's exception handler */
  4032.  
  4033. rqgetexceptionhandler(&exceptioninfo, &status);
  4034. exceptioninfo.exceptionmode = 0;
  4035. rqsetexceptionhandler(&exceptioninfo, &status);
  4036.  
  4037. /* Lookup/Create needed objects */
  4038.  
  4039. current_job = rqgettasktokens(THIS_JOB, &status);
  4040.  
  4041. init_sem = rqlookupobject(current_job,
  4042. &init_sem_name, WAIT_FOREVER, &status);
  4043.  
  4044. crt_shtdwn_sem = rqlookupobject(current_job,
  4045. &crt_shtdwn_sem_name, WAIT_FOREVER, &status);
  4046.  
  4047. req_mbx = rqcreatemailbox(FIFO_OBJ_MBX, &status);
  4048. rqcatalogobject(current_job,
  4049. req_mbx, &crt_req_mbx_name, &status);
  4050.  
  4051. /* Initialize display */
  4052.  
  4053. cursor_off( );
  4054. clrscrn( );
  4055.  
  4056. /* Signal initialization complete */
  4057.  
  4058. rqsendunits(init_sem, 1, &status);
  4059.  
  4060.  
  4061. rqreceiveunits(crt_shtdwn_sem, 1, NO_WAIT, &status);
  4062.  
  4063. while (status == ETIME)
  4064. {
  4065. req_seg = rqreceivemessage(req_mbx,
  4066. WAIT_FOREVER, &rsp_mbx, &status);
  4067.  
  4068. req_msg = buildptr(req_seg, 0);
  4069.  
  4070. print_at(req_msg->row,
  4071. req_msg->col, req_msg->msg_ptr);
  4072.  
  4073. if (rsp_mbx != NUL_TKN)
  4074. {
  4075. rqsendmessage(rsp_mbx, req_seg, NUL_TKN, &status);
  4076. }
  4077. else
  4078. {
  4079. rqdeletesegment(req_seg, &status);
  4080. }
  4081.  
  4082. rqreceiveunits(crt_shtdwn_sem, 1, NO_WAIT, &status);
  4083.  
  4084. }
  4085.  
  4086. rqsendunits(init_sem, 1, &status);
  4087.  
  4088. for(;;)
  4089. {
  4090. req_seg = rqreceivemessage(req_mbx,
  4091. WAIT_FOREVER, &rsp_mbx, &status);
  4092.  
  4093. if (rsp_mbx != NUL_TKN)
  4094. {
  4095. rqsendmessage(rsp_mbx, req_seg, NUL_TKN, &status);
  4096. }
  4097. else
  4098. {
  4099. rqdeletesegment(req_seg, &status);
  4100. }
  4101. }
  4102. }
  4103.  
  4104. /*1*********************************************************
  4105. * Function: read_time
  4106. *
  4107. * Summary: Read a specified time/date register of the
  4108. * hardware clock.
  4109. *
  4110. * Invocation: in_byte = read_time(reg);
  4111. *
  4112. * Inputs: reg (unsigned char) - the hardware time/date
  4113. * register to be read
  4114. *
  4115. * Outputs: in_byte (unsigned char) - the value of the
  4116. * register read
  4117. *
  4118. * Caveats: None
  4119. ***********************************************************/
  4120.  
  4121. unsigned char read_time(unsigned char reg)
  4122. {
  4123. unsigned char in_byte;
  4124.  
  4125. outbyte(CNT_PORT, HOLD_HIGH);
  4126. delay(3);
  4127. outbyte(CNT_PORT, HOLD_READ);
  4128. delay(1);
  4129. outbyte(ADR_PORT, reg);
  4130. delay(1);
  4131. in_byte = inbyte(DAT_PORT);
  4132. outbyte(CNT_PORT, CNT_RST);
  4133. delay(1);
  4134.  
  4135. return (in_byte);
  4136. }
  4137.  
  4138. /*1*********************************************************
  4139. * Function: write_time
  4140. *
  4141. * Summary: Write a byte to the specified time/date register
  4142. * of the hardware clock.
  4143.  
  4144. *
  4145. * Invocation: write_time(reg, out_byte);
  4146. *
  4147. * Inputs: reg (unsigned char) - the hardware time/date
  4148. * register
  4149. *
  4150. * out_byte (unsigned char) - the value to be
  4151. * written to the hardware clock register
  4152. *
  4153. * Caveats: None
  4154. ***********************************************************/
  4155.  
  4156. void write_time(unsigned char reg, unsigned char out_byte)
  4157. {
  4158. outbyte(CNT_PORT, HOLD_HIGH);
  4159. delay(3);
  4160. outbyte(ADR_PORT, reg);
  4161. delay(1);
  4162. outbyte(DAT_PORT, out_byte);
  4163. delay(1);
  4164. outbyte(CNT_PORT, HOLD_WRITE);
  4165. delay(1);
  4166. outbyte(CNT_PORT, CNT_RST);
  4167. delay(1);
  4168.  
  4169. return;
  4170. }
  4171.  
  4172. /*1*********************************************************
  4173. * Function: read_clock
  4174. *
  4175. * Summary: Read the time/date from the hardware clock and
  4176. * stores it in the global time/date structure.
  4177. *
  4178. * Invocation: read_clock();
  4179. *
  4180. * Caveats: Requires the caller to obtain access the shared
  4181. * time/date data segment.
  4182. ***********************************************************/
  4183.  
  4184. void read_clock(void)
  4185. {
  4186. unsigned char time_buf[13];
  4187. unsigned int i;
  4188.  
  4189. /* Set mode */
  4190.  
  4191. outbyte(PIO_PORT, PIO_READ);
  4192. delay(1);
  4193.  
  4194. /* Set control lines to disable interrupts */
  4195.  
  4196. outbyte(CNT_PORT, CNT_RST);
  4197. delay(1);
  4198.  
  4199. /* Read time/date */
  4200.  
  4201. for (i = 0 ; (i <= 12); i++)
  4202. {
  4203.  
  4204. time_buf[i] = read_time(i);
  4205. }
  4206.  
  4207. sys_time->second =
  4208. (10 * (time_buf[SEC10_REG] & 0x07)) +
  4209. (time_buf[SEC01_REG] & 0x0F);
  4210. sys_time->minute =
  4211. (10 * (time_buf[MIN10_REG] & 0x07)) +
  4212. (time_buf[MIN01_REG] & 0x0F);
  4213. sys_time->hour =
  4214. (10 * (time_buf[HRS10_REG] & 0x03)) +
  4215. (time_buf[HRS01_REG] & 0x0F);
  4216. sys_time->date =
  4217. (10 * (time_buf[DAT10_REG] & 0x03)) +
  4218. (time_buf[DAT01_REG] & 0x0F);
  4219. sys_time->month =
  4220. (10 * (time_buf[MON10_REG] & 0x01)) +
  4221. (time_buf[MON01_REG] & 0x0F);
  4222. sys_time->year =
  4223. (10 * (time_buf[YRS10_REG] & 0x0F)) +
  4224. (time_buf[YRS01_REG] & 0x0F);
  4225.  
  4226. /* Reset control lines to enable interrupts */
  4227.  
  4228. outbyte(CNT_PORT, INT_ENB);
  4229. delay(1);
  4230.  
  4231. outbyte(ADR_PORT, 0x0F);
  4232. delay(1);
  4233.  
  4234. return;
  4235. }
  4236.  
  4237. /*1*********************************************************
  4238. * Function: write_clock
  4239. *
  4240. * Summary: Uses the global time/date structure to write a
  4241. * new time/date to the hardware clock.
  4242. *
  4243. * Invocation: write_clock();
  4244. *
  4245. * Caveats: Requires the caller to obtain access the shared
  4246. * time/date data segment.
  4247. ************************************************************/
  4248.  
  4249. void write_clock(void)
  4250. {
  4251. unsigned char temp_byte;
  4252.  
  4253. /* Set mode */
  4254.  
  4255. outbyte(PIO_PORT, PIO_WRITE);
  4256. delay(1);
  4257.  
  4258. /* Set control lines to disable interrupts */
  4259.  
  4260. outbyte(CNT_PORT, CNT_RST);
  4261. delay(1);
  4262.  
  4263.  
  4264. /* Write time/date. Seconds always set to 0 */
  4265.  
  4266. sys_time->second = 0;
  4267. write_time(SEC01_REG, 0);
  4268. write_time(SEC10_REG, 0);
  4269.  
  4270. temp_byte =
  4271. sys_time->minute - (10 * (sys_time->minute / 10));
  4272. write_time(MIN01_REG, temp_byte);
  4273. temp_byte = sys_time->minute / 10;
  4274. write_time(MIN10_REG, temp_byte);
  4275.  
  4276. temp_byte =
  4277. sys_time->hour - (10 * (sys_time->hour / 10));
  4278. write_time(HRS01_REG, temp_byte);
  4279. temp_byte =
  4280. (sys_time->hour / 10) 0x08; /* 24hr format */
  4281. write_time(HRS10_REG, temp_byte);
  4282.  
  4283. temp_byte =
  4284. sys_time->date - (10 * (sys_time->date / 10));
  4285. write_time(DAT01_REG, temp_byte);
  4286. temp_byte = sys_time->date / 10;
  4287. write_time(DAT10_REG, temp_byte);
  4288.  
  4289. temp_byte =
  4290. sys_time->month - (10 * (sys_time->month / 10));
  4291. write_time(MON01_REG, temp_byte);
  4292. temp_byte = sys_time->month / 10;
  4293. write_time(MON10_REG, temp_byte);
  4294.  
  4295. temp_byte =
  4296. sys_time->year - (10 *(sys_time->year / 10));
  4297. write_time(YRS01_REG, temp_byte);
  4298. temp_byte = sys_time->year / 10;
  4299. write_time(YRS10_REG, temp_byte);
  4300.  
  4301. /* Reset mode*/
  4302.  
  4303. outbyte(PIO_PORT, PIO_READ);
  4304. delay(1);
  4305.  
  4306. /* Reset control lines to enable interrupts */
  4307.  
  4308. outbyte(CNT_PORT, INT_ENB);
  4309. delay(1);
  4310.  
  4311. outbyte(ADR_PORT, 0x0F);
  4312. delay(1);
  4313.  
  4314. return;
  4315. }
  4316.  
  4317. /*1*********************************************************
  4318. * Function: reset_timers
  4319. *
  4320. * Summary: Initializes (zero out) all timer slots of the
  4321. * shared time/date data segment.
  4322. *
  4323.  
  4324. * Invocation: reset_timers();
  4325. *
  4326. * Caveats: Requires the caller to obtain access the shared
  4327. * time/date data segment.
  4328. ************************************************************/
  4329.  
  4330. void reset_timers(void)
  4331. {
  4332. unsigned int i;
  4333.  
  4334. for (i = 0 ; i < MAX_TIMERS; i++)
  4335. {
  4336. sys_time->timer[i].in_use = FALSE;
  4337. sys_time->timer[i].sem_tkn = NUL_TKN;
  4338. sys_time->timer[i].count = 0;
  4339. sys_time->timer[i].timeout = 0;
  4340. }
  4341.  
  4342. return;
  4343. }
  4344.  
  4345. /*1*********************************************************
  4346. * Function: advance_timers
  4347. *
  4348. * Summary: Increments all active timers in the shared
  4349. * time/date data segment by one second.
  4350. *
  4351. * Invocation: advance_timers();
  4352. *
  4353. * Caveats: Requires the caller to obtain access the shared
  4354. * time/date data segment.
  4355. ***********************************************************/
  4356.  
  4357. void advance_timers(void)
  4358. {
  4359. unsigned int i;
  4360. unsigned int status;
  4361.  
  4362.  
  4363. for (i = 0 ; i < MAX_TIMERS; i++)
  4364. {
  4365. if (sys_time->timer[i].in_use)
  4366. {
  4367. sys_time->timer[i].count++;
  4368. if (sys_time->timer[i].count >=
  4369. sys_time->timer[i].timeout)
  4370. {
  4371. rqsendunits(
  4372. sys_time->timer[i].sem_tkn, 1, &status);
  4373. sys_time->timer[i].count = 0;
  4374. }
  4375. }
  4376. }
  4377.  
  4378. return;
  4379. }
  4380.  
  4381.  
  4382. /*1**********************************************************
  4383.  
  4384. * Function: advance_time_date
  4385. *
  4386. * Summary: Increments the time/date (hh:mm:ss mm/dd/yy) in
  4387. * the shared time/date data segment by one second.
  4388. *
  4389. * Invocation: advance_time_date();
  4390. *
  4391. * Caveats: Requires the caller to obtain access the shared
  4392. * time/date data segment.
  4393. ************************************************************/
  4394.  
  4395.  
  4396. void advance_time_date(void)
  4397. {
  4398. if (sys_time->second < 59)
  4399. {
  4400. sys_time->second++;
  4401.  
  4402. }
  4403. else
  4404. {
  4405. sys_time->second = 0;
  4406.  
  4407. if (sys_time->minute < 59)
  4408. {
  4409. sys_time->minute++;
  4410. }
  4411. else
  4412. {
  4413. sys_time->minute = 0;
  4414.  
  4415. if (sys_time->hour < 23)
  4416. {
  4417. sys_time->hour++;
  4418. }
  4419. else
  4420. {
  4421. sys_time->hour = 0;
  4422.  
  4423. if (sys_time->date <
  4424. days_in_month[sys_time->month - 1])
  4425. {
  4426. sys_time->date++;
  4427. }
  4428. else
  4429. {
  4430. if (sys_time->month == FEBRUARY)
  4431. {
  4432. /* Check for leap year (only good through 2100) */
  4433.  
  4434. if (((sys_time->year % 4) == 0) &&
  4435. (sys_time->date == 28))
  4436. {
  4437. sys_time->date = 29;
  4438. }
  4439. else
  4440. {
  4441. sys_time->month = MARCH;
  4442. sys_time->date = 1;
  4443.  
  4444. }
  4445. }
  4446. else
  4447. {
  4448. sys_time->date = 1;
  4449.  
  4450. if (sys_time->month < DECEMBER)
  4451. {
  4452. sys_time->month++;
  4453. }
  4454. else
  4455. {
  4456. sys_time->month = JANUARY;
  4457.  
  4458. if (sys_time->year < 99)
  4459. {
  4460. sys_time->year++;
  4461. }
  4462. else
  4463. {
  4464. sys_time->year = 0;
  4465. }
  4466. }
  4467. }
  4468. }
  4469. }
  4470. }
  4471. }
  4472.  
  4473. return;
  4474. }
  4475.  
  4476.  
  4477. /*1*********************************************************
  4478. * Function: start_timer
  4479. *
  4480. * Summary: Create and start a timer.
  4481. *
  4482. * Invocation: timer_sem = start_timer(timeout);
  4483. *
  4484. * Inputs: timeout (unsigned int) - timer interval
  4485. * in seconds
  4486. *
  4487. * Outputs: timer_sem (selector) - the semaphore that will
  4488. * receive a unit at each timer interval
  4489. *
  4490. * Caveats: The interrupt handler will signal the
  4491. * interrupt task to finish servicing the
  4492. * interrupt.
  4493. ************************************************************/
  4494.  
  4495. /* Tell the compiler that this function is an interrupt */
  4496. /* routine so it can generate proper cede for entering */
  4497. /* and exiting the interrupt routine. */
  4498.  
  4499. #pragma interrupt("clockint_handler")
  4500.  
  4501. void clockint_handler(void)
  4502. {
  4503.  
  4504. unsigned int status;
  4505.  
  4506. /* Signal the interrupt task */
  4507.  
  4508. rqsignal interrupt(CLOCK_INT_LEVEL, &status);
  4509.  
  4510. return;
  4511. }
  4512.  
  4513. /*1*********************************************************
  4514. * Task: clock_task
  4515. *
  4516. * Summary: Services interrupts from the hardware clock.
  4517. * Also maintains/updates the global time/date
  4518. * data and services software timers.
  4519. *
  4520. * Caveats: Communicates with the crt_task() to display the
  4521. * current time/date.
  4522. ***********************************************************/
  4523.  
  4524. void clock_task(void)
  4525. {
  4526. char time_string[19];
  4527.  
  4528. unsigned int status;
  4529. unsigned int retry;
  4530.  
  4531. selector current_job;
  4532. selector init_sem;
  4533. selector clock_sem;
  4534. selector clock_shtdwn_sem;
  4535.  
  4536. selector rsp_seg;
  4537. selector req_seg;
  4538. selector crt_req_mbx;
  4539. selector rsp_mbx;
  4540.  
  4541. struct CRT_REQ_STR *req_msg;
  4542.  
  4543. struct EXCEPTIONSTRUCT exceptioninfo;
  4544.  
  4545. /* Disable this task's exception handler */
  4546.  
  4547. rqgetexceptionhandler(&exceptioninfo, &status);
  4548. exceptioninfo.exceptionmode = 0;
  4549. rqsetexceptionhandler(&exceptioninfo, &status);
  4550.  
  4551. /* Disable any current interrupt task */
  4552.  
  4553. rqresetinterrupt(CLOCK_INT_LEVEL, &status);
  4554.  
  4555. /* Lookup/Create needed objects */
  4556.  
  4557. current job = rqgettasktokens(THIS_JOB, &status);
  4558.  
  4559. init_sem = rqlookupobject(current_job,
  4560. &init_sem_name, NO_WAIT, &status);
  4561.  
  4562. clock_sem = rqlookupobject(current_job,
  4563.  
  4564. &clock_sem_name, NO_WAIT, &status);
  4565.  
  4566. sys_time_seg = rqlookupobject(current_job,
  4567. &time_seg_name, NO_WAIT, &status);
  4568.  
  4569. clock_shtdwn_sem = rqlookupobject(current_job,
  4570. &clock_shtdwn_sem_name, NO_WAIT, &status);
  4571.  
  4572. crt_req_mbx = rqlookupobject(current_job,
  4573. &crt_req_mbx_name, NO_WAIT, &status);
  4574.  
  4575. sys_time = buildptr(sys_time_seg, 0);
  4576.  
  4577. rsp_mbx = rqcreatemailbox(FIFO_OBJ_MBX, &status);
  4578.  
  4579. /* Initialize time string buffer */
  4580.  
  4581. memset(&time_string, ' ' ,sizeof(time_string));
  4582.  
  4583. /* Initialize crt_task() request message and */
  4584. /* send it to the response mailbox to setup */
  4585. /* for the processing loop. */
  4586.  
  4587. req_seg = rqcreatesegment(
  4588. sizeof(struct CRT_REQ_STR), &status);
  4589. req_msg = buildptr(req_seg, 0);
  4590.  
  4591. req_msg->msg_ptr = &time_string;
  4592. req_msg->row = 1;
  4593. req_msg->col = 60;
  4594.  
  4595. rqsendmessage(rsp_mbx, req_seg, NUL_TKN, &status);
  4596.  
  4597. /* Initialize retry counter and timers */
  4598.  
  4599. retry = 0;
  4600. sys_time->change_flg = FALSE;
  4601. reset_timers();
  4602.  
  4603. /* Initialize and read hardware clock */
  4604.  
  4605. outbyte(PIO_PORT, PIO_READ);
  4606. delay(1);
  4607.  
  4608. outbyte(CNT_PORT, INT_ENB);
  4609. delay(1);
  4610.  
  4611. outbyte(ADR_PORT, 0x0F);
  4612. delay(1);
  4613.  
  4614. read_clock();
  4615.  
  4616. /* Allow access to time/date memory */
  4617.  
  4618. rqsendunits(clock_sem, 1, &status);
  4619.  
  4620. /* Set interrupt vector */
  4621.  
  4622. rqsetinterrupt(CLOCK_INT_LEVEL, 1,
  4623.  
  4624. &clockint_handler, NUL_TKN, &status);
  4625.  
  4626. /* Signal initialization complete */
  4627.  
  4628. rqsendunits(init_sem, 1, &status);
  4629.  
  4630. /* Process interrupt signals until shutdown */
  4631.  
  4632. rqreceiveunits(clock_shtdwn_sem, 1, NO_WAIT, &status);
  4633.  
  4634. while (status == ETIME) /* while no signal received */
  4635. {
  4636. /* Wait no longer than 2 seconds for interrupt signal */
  4637.  
  4638. rqetimedinterrupt(CLOCK_INT_LEVEL, 200, &status);
  4639.  
  4640. /* If an interrupt is NOT received, attempt to reset */
  4641. /* the hardware clock. */
  4642.  
  4643. if (status == ETIME) /* if no interrupt */
  4644. {
  4645. retry++;
  4646.  
  4647. /* Once the maximum number of retries is reached, */
  4648. /* give up. Retrieve outstanding display request */
  4649. /* and wait for shutdown. When shutdown signal is */
  4650. /* received, disable this interrupt task, signal */
  4651. /* shutdown complete and suspend execution. */
  4652.  
  4653. if (retry == MAX_CLOCK_RETRIES)
  4654. {
  4655. req_seg = rqreceivemessage(rsp_mbx,
  4656. WAIT_FOREVER, NUL_TKN, &status);
  4657. rqreceiveunits(clock_shtdwn_sem, 1,
  4658. WAIT_FOREVER, &status);
  4659. rqresetinterrupt(CLOCK_INT_LEVEL, &status);
  4660. rqsendunits(init_sem, 1, &status);
  4661. rqsuspendtask(NUL_TKN, &status);
  4662. }
  4663.  
  4664. /* Reinitialize hardware clock */
  4665.  
  4666. outbyte(PIO_PORT, PIO_READ);
  4667. delay(1);
  4668.  
  4669. outbyte(CNT_PORT, INT_ENB);
  4670. delay(1);
  4671.  
  4672. outbyte(ADR_PORT, 0x0F);
  4673. delay(1);
  4674.  
  4675. /* Re-read hardware crock */
  4676.  
  4677. rqreceiveunits(clock_sem, 1,
  4678. WAIT_FOREVER, &status);
  4679.  
  4680. read_clock();
  4681.  
  4682. rqsendunits(clock_sem, 1, &status);
  4683.  
  4684. }
  4685. else
  4686. {
  4687. /* Interrupt signal received, reset retry count */
  4688.  
  4689. retry = 0;
  4690.  
  4691. /* Access time/date memory */
  4692.  
  4693. rqreceiveunits(clock_sem, 1,
  4694. WAIT_FOREVER, &status);
  4695.  
  4696. /* If needed, update hardware clock with new */
  4697. /* time/date data. Otherwise, advance the */
  4698. /* time/data data by one second. */
  4699.  
  4700. if (sys_time->change_flg)
  4701. {
  4702. write_clock();
  4703. sys_time->change_flg = 0;
  4704. }
  4705. else
  4706. {
  4707. advance_time_date();
  4708. }
  4709.  
  4710. /* Advance any timers by one second */
  4711.  
  4712. advance_timers();
  4713.  
  4714. /* Prepare time/date data for display */
  4715.  
  4716. sprintf(time_string, "%02d:%02d:%02d %02d/%02d/%O2d",
  4717. sys_time->hour, sys_time->minute, sys_time->second,
  4718. sys_time->month, sys_time->date, sys_time->year);
  4719.  
  4720. /* Release time/date memory */
  4721.  
  4722. rqsendunits(clock_sem, 1, &status);
  4723.  
  4724. /* If previous display request has finished(segment */
  4725. /* returned), then request new time/date be displayed */
  4726.  
  4727. rsp_seg = rqreceivemessage(rsp_mbx,
  4728. NO_WAIT, NUL_TKN, &status);
  4729.  
  4730. if (status != ETIME)
  4731. {
  4732. rqsendmessage(crt_req_mbx,
  4733. req_seg, rsp_mbx, &status);
  4734. }
  4735. }
  4736. /* Check for shutdown */
  4737.  
  4738. rqreceiveunits(clock_shtdwn_sem, 1, NO_WAIT, &status);
  4739. }
  4740.  
  4741. /* Retrieve outstanding display request, disable */
  4742. /* this interrupt task, signal shutdown complete */
  4743.  
  4744. /* and suspend execution. */
  4745.  
  4746. req_seg = rqreceivemessage(rsp_mbx,
  4747. WAIT_FOREVER, NUL_TKN, &status);
  4748.  
  4749. rqsendunits(init_sem, 1, &status);
  4750.  
  4751. rqresetinterrupt(CLOCK_INT_LEVEL, &status);
  4752.  
  4753. rqsuspendtask(NUL_TKN, &status); /* suspend this task */
  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.  
  4800.  
  4801.  
  4802.  
  4803.  
  4804.  
  4805.  
  4806.  
  4807. Standard C
  4808.  
  4809.  
  4810. The Header <float.h>
  4811.  
  4812.  
  4813.  
  4814.  
  4815. P.J. Plauger
  4816.  
  4817.  
  4818. P.J. Plauger is senior editor of The C Users Journal. He is secretary of the
  4819. ANSI C standards committee, X3J11, and convenor of the ISO C standards
  4820. committee, WG14. His latest book is Standard C, which he co-authored with Jim
  4821. Brodie. You can reach him at pjp@plauger.uunet.
  4822.  
  4823.  
  4824.  
  4825.  
  4826. Introduction
  4827.  
  4828.  
  4829. Floating point arithmetic is complicated stuff. Many small processors don't
  4830. even support floating point arithmetic with hardware instructions. Others
  4831. require a separate coprocessor to handle such arithmetic. Only the most
  4832. complex computers include floating point support in the standard instruction
  4833. set.
  4834. There's a pragmatic reason why chip designers often omit floating point. It
  4835. takes about the same amount of microcode to implement floating add, subtract,
  4836. multiply, and divide as it does all the rest of the instructions combined. You
  4837. can essentially halve the complexity of a microprocessor by leaving out
  4838. floating point support.
  4839. Many applications don't need floating point arithmetic at all. Others can
  4840. tolerate reasonably poor performance, and a few kilobytes at extra code, by
  4841. doing the arithmetic in software. The few that need high-performance
  4842. arithmetic often make other expensive demands on the hardware, so the extra
  4843. cost of a coprocessor is an acceptable perturbation.
  4844. C spent its early years on a PDP-11/45 computer. That strongly colored the
  4845. treatment of floating point arithmetic in C. For instance, the types float
  4846. (for 32-bit format) and double (for 64-bit format) have been in the language
  4847. from the earliest days. (Those were the two formats the PDP-11 supported.)
  4848. That is a bit unusual for a system-implementation language, and a "small" one
  4849. at that. Who knows what would have happened had that machine not been equipped
  4850. with the optional Floating Point Processor (FPP)?
  4851. The PDP-11/45 FPP could be placed in one of two modes. It did all arithmetic
  4852. either with 32-bit operands or with 64-bit operands. You had to execute a
  4853. oneword instruction to switch modes. On the other hand, you could load and
  4854. convert an operand of the wrong size just as easily as you could load one of
  4855. the expected size. That strongly encouraged leaving the FPP in one mode. It is
  4856. no surprise that C for many years promised to produce a double result for any
  4857. operator involving floating point operands, even one with two float operands.
  4858. Not even FORTRAN was so generous.
  4859. As C migrated to other machines, this heritage sometimes became a nuisance.
  4860. Those of us who felt obliged to supply the full language had to write floating
  4861. point software for some pretty tiny machines. I put up PDP-11 style floating
  4862. point on the Intel 8080 (plus Intel 8085 and Zilog Z80). I can assure you that
  4863. it wasn't easy.
  4864. Machines that supported floating point as standard hardware present a
  4865. different set of problems. Chances are, the formats are slightly different.
  4866. That makes writing portable code much more challenging. You need to write math
  4867. functions and conversion algorithms to retain varying ranges of values and
  4868. varying amounts of precision. I learned more than I ever wanted to know about
  4869. portability while debugging this code.
  4870. Machines that provide floating point as an option combine the worst of both
  4871. worlds, at least to us compiler implementors. We had to provide software
  4872. support for those machines that lacked the option. We had to make use of the
  4873. machine instructions when the option was present. And we had to deal with
  4874. confused customers who linked two flavors of code, or the wrong version of the
  4875. library.
  4876.  
  4877.  
  4878. Standards Issues
  4879.  
  4880.  
  4881. From a linguistic standpoint, however, most of these issues are irrelevant.
  4882. The main problem the drafters of the C Standard had to deal with was excess
  4883. variety. It is a longstanding tradition in C to take what the machine gives
  4884. you. A right shift operator does whatever the underlying hardware does most
  4885. rapidly. So, too, does a floating add operator. Neither result may please a
  4886. mathematician.
  4887. With floating point arithmetic, you have the obvious issues of overflow and
  4888. underflow. A result may be too large to represent on one machine, but not on
  4889. another. The resulting overflow may cause a trap, may generate a special code
  4890. value, or may produce garbage that is easily mistaken for a valid result. A
  4891. result may be too small to represent on one machine, but not on another. The
  4892. resulting underflow may cause a trap or may be quietly replaced with an exact
  4893. zero. Such a "zero fixup" is often a good idea, but not always.
  4894. Novices tend to write code that is susceptible to overflow and underflow. The
  4895. broad range of values supported by floating point lures the innocent into a
  4896. careless disregard. Your first lesson is to estimate magnitudes and avoid
  4897. silly swings in value.
  4898. You also have the more subtle issue of significance loss. Floating point
  4899. arithmetic lets you represent a tremendously broad range of values, but at a
  4900. cost. A value can be represented to only a fixed precision. Multiply two
  4901. values that are exact and you can keep only half the significance you might
  4902. like. Subtract two values that are very close together and you can lose most
  4903. or all of the significance you were carrying around.
  4904. Workaday programmers most often run afoul of unexpected significance loss.
  4905. That formula that looks so elegant in a textbook is an ill-behaved pig when
  4906. reduced to code. It is hard to see the danger in those alternating signs in
  4907. adjacent terms of a series. Until you get burned, that is, and learn to do the
  4908. subtractions on paper instead of at runtime.
  4909. Overflow, underflow, and significance loss are intrinsic to floating point
  4910. arithmetic. They are hard enough to deal with on a given machine. Writing code
  4911. that can move across machines is harder. Writing a standard that tells you how
  4912. to write portable code is harder still. But another problem makes the matter
  4913. even worse.
  4914. Two machines can use the same representation for floating point values. Yet
  4915. you can add the same two values on each machine and get different answers! The
  4916. result can depend, reasonably enough, on the way the two machines round
  4917. results that cannot be represented exactly. You can make a case for truncating
  4918. toward zero, rounding to the nearest representable value, or doing a few other
  4919. similar but subtly different operations.
  4920. Or you can just plain get the wrong answer. In some circles, getting a quick
  4921. answer is considered much more virtuous than getting one that is as accurate
  4922. as it could be. Seymour Cray has built several successful computer companies
  4923. catering to this constituency. These machines saw off precision somewhere in
  4924. the neighborhood of the least significant bit that is retained. Sometimes that
  4925. curdles a bit or two having even more significance. There are even some
  4926. computers (not designed by Cray) that scrub the four least significant bits
  4927. when you multiply by one!
  4928. If the C Standard had tried to outlaw this behavior, it would never have been
  4929. approved. Too many machines still use quick and dirty floating point
  4930. arithmetic. Too many people still use these machines. To deny them the cachet
  4931. of supporting conforming C compilers would be commercially unacceptable.
  4932. As a result, the Standard is mostly descriptive in the area of floating point
  4933. arithmetic. It endeavors to define enough terms to talk about the parameters
  4934. of floating point. But it says little that is prescriptive about getting the
  4935. right answer.
  4936. Committee X3J11 added the standard header <float.h> as a companion to
  4937. <limits.h>. The latter is historically misnamed. It should more properly be
  4938. called <integer.h>, since it deals with the properties of integer data
  4939. representations and arithmetic. Rather than muck up an existing header (that
  4940. we share with the POSIX Standard), we added yet another.
  4941. We put into <float.h> essentially every parameter that we thought might be of
  4942. use to a serious numerical programmer. From these macros, you can learn enough
  4943. about the properties of the target machine, presumably, to code your numerical
  4944. algorithms wisely. (Notwithstanding my earlier slurs, the major push to help
  4945. this class of programmers came from Cray Research.)
  4946.  
  4947.  
  4948. What The Standard Says
  4949.  
  4950.  
  4951. The Library section of the Standard has little to say about <float.h>:
  4952.  
  4953.  
  4954. 4.1.4 Limits <float.h> and <limits.h>
  4955.  
  4956.  
  4957. The headers <float.h> and <limits.h> define several macros that expand to
  4958. various limits and parameters.
  4959. The macros, their meanings, and the constraints (or restrictions) on their
  4960. values are listed in 2.2.4.2. [end of quote]
  4961.  
  4962. You have to backtrack to the Environment section to get the real meat:
  4963.  
  4964.  
  4965. 2.2.4.2.2 Characteristics of Floating Types <float.h>
  4966.  
  4967.  
  4968. The characteristics of floating types are defined in terms of a model that
  4969. describes a representation of floating-point numbers and values that provide
  4970. information about an implementation's floating-point arithmetic.10 The
  4971. following parameters are used to define the model for each floating-point
  4972. type:
  4973. s sign (±1)
  4974. b base or radix of exponent representation (an integer > 1)
  4975. e exponent (an integer between a minimum emin and a maximum emax)
  4976. p precision (the number of base-b digits in the significand)
  4977. fk nonnegative integers less than b (the significand digits)
  4978. A normalized floating-point number x (â1 > 0 if x 0) is defined by the
  4979. following model:
  4980. Click Here for Equation
  4981. Of the values in the <float.h> header, FLT_RADIX shall be a constant
  4982. expression suitable for use in #if preprocessing directives; all other values
  4983. need not be constant expressions. All except FLT_RADIX and FLT_ROUNDS have
  4984. separate names for all three floating-point types. The floating-point model
  4985. representation is provided for all values except FLT_ROUNDS.
  4986. The rounding mode for floating-point addition is characterized by the value of
  4987. FLT_ROUNDS :
  4988. -1 indeterminable
  4989. 0 toward zero
  4990. 1 to nearest
  4991. 2 toward positive infinity
  4992. 3 toward negative infinity
  4993. All other values for FLT_ROUNDS characterize implemention-defined rounding
  4994. behavior.
  4995. The values given in the following list shall be replaced by
  4996. implementation-defined expressions that shall be equal or greater in magnitude
  4997. (absolute value) to those shown, with the same sign:
  4998. radix of exponent representation, b
  4999. FLT_RADIX 2
  5000. number of base-FLT_RADIX digits in the floating-point significand, p
  5001. FLT_MANT_DIG
  5002. DBL_MANT_DIG
  5003. LDBL_MANT_DIG
  5004. number of decimal digits, q, such that any floating-point number with q
  5005. decimal digits can be rounded into a floating-point number with p radix b
  5006. digits and back again without change to the q decimal digits,
  5007. Click Here for Equation
  5008. FLT_DIG 6
  5009. DBL_DIG 10
  5010. LDBL_DIG 10
  5011. minimum negative integer such that FLT_RADIX raised to that power minus 1 is a
  5012. normalized floating-point number, emin
  5013. FLT_MIN_EXP
  5014. DBL_MIN_EXP
  5015. LDBL_MIN_EXP
  5016. minimum negative integer such that 10 raised to that power is in the range of
  5017. normalized floating-point numbers, Ã©log10bemin-1ù
  5018. FLT_MIN_10_EXP -37
  5019. DBL_MIN_10_EXP -37
  5020. LDBL_MIN_10_EXP -37
  5021. maximum integer such that FLT_RADIX raised to that power minus 1 is a
  5022. representable finite floating-point number, emax
  5023. FLT_MAX_EXP
  5024. DBL_MAX_EXP
  5025. LDBL_MAX_EXP
  5026. maximum integer such that 10 raised to that power is in the range of
  5027. representable finite floating-point numbers, Ã«log10((1-b-p) bemaxû
  5028. FLT_MAX_10_EXP +37
  5029. DBL_MAX_10_EXP +37
  5030. LDBL_MAX_10_EXP +37
  5031. The values given in the following list shall be replaced by
  5032. implementation-defined expressions with values that shall be equal to or
  5033. greater than those shown:
  5034. maximum representable finite floating-point number, (1 - b-p) bemax
  5035. FLT_MAX 1E+37
  5036. DBL_MAX 1E+37
  5037. LDBL_MAX 1E+37
  5038. The values given in the following list shall be replaced by
  5039. implementation-defined expressions with values that shall be equal to or less
  5040. than those shown:
  5041. the difference between 1.0 and the least value greater than 1.0 that is
  5042. representable in the given floating point type, b1-p
  5043. FLT_EPSILON 1E-5
  5044. DBL_EPSILON 1E-9
  5045.  
  5046. LDBL_EPSILON 1E-9
  5047. minimum normalized positive floating-point number, bemin-1
  5048. FLT_MIN 1E-37
  5049. DBL_MIN 1E-37
  5050. LDBL_MIN 1E-37
  5051. Footnote:
  5052. 10. The floating-point model is intended to clarify the description of each
  5053. floating-point characteristic and does not require the floating-point
  5054. arithmetic of the implementation to be identical. [end of quote]
  5055.  
  5056.  
  5057. Implementing <float.h>
  5058.  
  5059.  
  5060. In principle, this header consists of nothing but a bunch of macro
  5061. definitions. For a given implementation, you merely determine the values of
  5062. all the parameters and plug them in. A common implementation these days is
  5063. based on a fairly recent standard for floating point arithmetic called
  5064. IEEE-754. Older machines, such as System/370 and VAX, still have floating
  5065. point formats that are proprietary. Newer chips, however, tend to follow this
  5066. standard. You will find IEEE-754 floating point arithmetic in the coprocessors
  5067. for the Intel 80X86 family and the Motorola 680X0 family, to name just two
  5068. very popular lines. It is a very complex standard (some say too complex), but
  5069. only its grosser properties affect <float.h>.
  5070. The following header assumes that long double has the same representation as
  5071. double. The former can have an 80-bit representation in the IEEE standard, but
  5072. it doesn't have to. For this common case, you can copy the values out of an
  5073. example in the Standard:
  5074. /* float.h standard header -
  5075. IEEE-754, 4/8/8
  5076. */
  5077. #ifndef _FLOAT
  5078. #define _FLOAT
  5079. /* double properties */
  5080. #define DBL_DIG 15
  5081. #define DBL_EPSILON
  5082. 2.2204460492503131E-16
  5083. #define DBL_MANT_DIG 53
  5084. #define DBL_MAX
  5085. 1.7976931348623157E+308
  5086. #define DBL_MAX_10_EXP 308
  5087. #define DBL_MAX_EXP 1024
  5088. #define DBL_MIN 2.2250738585072014E-308
  5089. #define DBL_MIN_10_EXP -307
  5090. #define DBL_MIN_EXP -1021
  5091. /* float properties */
  5092. #define FLT_DIG 6
  5093. #define FLT_EPSILON 1.19209290E-07F
  5094. #define FLT_MANT_DIG 24
  5095. #define FLT_MAX 3.40282347E+38F
  5096. #define FLT_MAX_10_EXP +38
  5097. #define FLT_MAX_EXP +128
  5098. #define FLT_MIN 1.17549435E-38F
  5099. #define FLT_MIN_10_EXP -37
  5100. #define FLT_MIN_EXP -125
  5101. /* common properties */
  5102. #define FLT_RADIX 2
  5103. #define FLT_ROUNDS -1
  5104. /* long double properties */
  5105. #define LDBL_DIG 15
  5106. #define LDBL_EPSILON 2.2204460492503131E-16L
  5107. #define LDBL_MANT_DIG 53
  5108. #define LDBL_MAX 1.7976931348623157E+308L
  5109. #define LDBL_MAX_10_EXP 308
  5110. #define LDBL_MAX_EXP 1024
  5111. #define LDBL_MIN 2.2250738585072014E-308L
  5112. #define LDBL_MIN_10_EXP -307
  5113. #define LDBL_MIN_EXP -1021
  5114. #endif
  5115. You may find a few problems, however. Not all translators are equally good at
  5116. converting floating point literals. Some may curdle the least significant bit
  5117. or two. That could cause overflow or underflow in the case of some extreme
  5118. values such as DBL_MAX and DBL_MIN. Or it could ruin the critical behavior of
  5119. other values such as DBL EPSILON.
  5120. At the very least, you should check the bit patterns produced by these values.
  5121. You can do that by stuffing the value into a union one way, then extracting it
  5122. another way, as in:
  5123. union {
  5124. double dval;
  5125.  
  5126. unsigned short w[4];
  5127. } dmax = DBL_MAX;
  5128. Here, I assume that unsigned short occupies 16 bits and double is the IEEE-754
  5129. 64-bit representation. Some computers store the most significant word at
  5130. dmax.w[0], others at dmax.w[3]. You have to check what your implementation
  5131. does. Whatever the case, the most significant word should have the value
  5132. 0x7FEF, and all the others should equal 0xFFFF.
  5133. A safer approach is to do it the other way around. Initialize the union as a
  5134. sequence of bit patterns, then define the macros to access the union through
  5135. its floating point member. Since you can only initialize the first member of a
  5136. union, you must reverse the member declarations from the example above. You
  5137. must also give them funny names, to avoid collisions with any user-defined
  5138. macros.
  5139. With this approach, you would place the following definitions and declarations
  5140. in <float.h>:
  5141. typedef union {
  5142. unsigned short _W[4];
  5143. double _D;
  5144. } _Dtype;
  5145. .....
  5146. extern_Dtype _Dmax, _Dmin,
  5147. _Deps;
  5148. #define DBL_MAX _Dmax._D;
  5149. #define DBL_MIN _Dmin._D;
  5150. #define DBL_EPSILON _Deps._D;
  5151. Then in a library source file, you provide a definition for _Dmax. For the
  5152. 80X86 family, which has the least-significant word first, you write:
  5153. #include <float.h>
  5154. .....
  5155. _Dtype _Dmax = {{0xFFFF, 0xFFFF,
  5156. 0xFFFF, 0x7FEF}};
  5157. _Dtype _Dmin = {{0x0000, 0x0000,
  5158. 0x0000, 0x0010}};
  5159. _Dtype _Deps = {{0x0000, 0x0000,
  5160. 0x0000, 0x3CB0}};
  5161. (Warning: I haven't tested all these values, yet. Beware of errors,
  5162. particularly of the off-by-one variety.)
  5163. The macros you define this way have one small drawback. You cannot use them to
  5164. initialize static data. (That includes components of aggregates such as
  5165. structures and arrays, whether static or dynamic.) In other words, you cannot
  5166. write:
  5167. #include <float.h>
  5168. .....
  5169. static BigD = DBL_MAX;/* WRONG! */
  5170. The macro expands to an rvalue expression, not a constant expression as
  5171. required for a static initializer. Such a definition is permitted by the
  5172. Standard. Only FLT_RADIX need be an expression usable in a #if directive. (An
  5173. implementation may not change the number base it uses for floating point
  5174. arithmetic after translation time. This was not viewed as a serious
  5175. restriction.)
  5176. Note, by the way, that FLT_ROUNDS can also be an rvalue expression. The
  5177. rounding mode can change during execution. How you change it is not specified
  5178. in the Standard, but you can test whether it has changed. Just inspect the
  5179. current value of FLT_ROUNDS. Modern coprocessors such as the Intel 80X87
  5180. family and the Motorola 68881 indeed permit limited program control over the
  5181. rounding mode. The C Standard merely acknowledges reality.
  5182.  
  5183.  
  5184. Conclusion
  5185.  
  5186.  
  5187. Having said all this, I feel obliged to tell you to forget most of it. Only
  5188. the most sophisticated of numerical programs care about most of these
  5189. parameters. Only the most sophisticated of programmers know how to write code
  5190. that adapts well to changes among floating representations.
  5191. In all my years of scientific programming, I have found good use for only the
  5192. *_MAX parameters. I use these to build tables for converting between floating
  5193. point and integer. When I want the code to be highly portable, I conditionally
  5194. include the last few entries based on the range of numbers. That is the sort
  5195. of thing that you worry about behind the scenes. It is not likely to appear in
  5196. a typical application.
  5197. You should have some awareness of the peculiarities and pitfalls of floating
  5198. point arithmetic. You should know the safe ranges and precisions for floating
  5199. point values in portable C code and in code you write for your workaday
  5200. machines. You can use some of the parameters in <float.h> to build safety
  5201. checks into your code.
  5202. But don't think that this header contains some key ingredient for writing
  5203. highly portable code. It doesn't.
  5204.  
  5205.  
  5206.  
  5207.  
  5208.  
  5209.  
  5210.  
  5211.  
  5212.  
  5213.  
  5214.  
  5215.  
  5216.  
  5217.  
  5218.  
  5219.  
  5220.  
  5221.  
  5222.  
  5223.  
  5224.  
  5225.  
  5226.  
  5227. Doctor C's Pointers(R)
  5228.  
  5229.  
  5230. Puzzles, Part 3
  5231.  
  5232.  
  5233.  
  5234.  
  5235. Rex Jaeschke
  5236.  
  5237.  
  5238. Rex Jaeschke is an independent computer consultant, author and seminar leader.
  5239. He participates in both ANSI and ISO C Standards meetings and is the editor of
  5240. The Journal of C Language Translation, a quarterly publication aimed at
  5241. implementors of C language translation tools. Readers are encouraged to submit
  5242. column topics and suggestions to Rex at 2051 Swans Neck Way, Reston, VA 22091
  5243. or via UUCP at uunet!aussie!rex or aussie! rex@uunet.uu.net.
  5244.  
  5245.  
  5246. This month I'll present more puzzles, though there will be a slight twist.
  5247. These puzzles either contain syntax errors or produce unexpected output and
  5248. are derived from problems my introductory C seminar attendees often encounter.
  5249. This should be a lesson in "quality of implementation." That is, just how good
  5250. are the error messages produced by a compiler, and what warning messages, if
  5251. any, does it produce? An ANSI-conformant compiler is not required to warn
  5252. about anything. It is permitted to say "Syntax error" once at the end of a
  5253. compilation that detected 100 errors. It's not even required to indicate the
  5254. offending line numbers. In short, the quality of such messages is a
  5255. marketplace and vendor-conscience issue.
  5256. As before, I've included the answers, but you should try to debug them on your
  5257. own first. You might also want to see what messages your compiler produces. In
  5258. some of the puzzles and solutions, I have included the actual messages from
  5259. one or more DOS-based compilers. PC-Lint, a static analysis tool from Gimpel
  5260. Software, produced the other messages. Since the primary job of lint-like
  5261. tools is to go over your code with a fine tooth comb, it is not surprising
  5262. that PC-Lint gave even more help than did almost all of my compilers. (While
  5263. the exact message text has been retained, a common line number prefix has been
  5264. added to each to make them easier to read and compare.)
  5265. The moral of this exercise is that if you can at all afford it, buy more than
  5266. one compiler or buy a lint-like tool in addition to your compiler. If you
  5267. can't or won't do either of these, at least buy a compiler that has many
  5268. lint-like features built in. (That's why I selected Turbo C when it was first
  5269. introduced.) If nothing else, switch on the maximum warning level of the one
  5270. compiler you do have.
  5271. To say that you cannot afford another compiler or lint-like tool but you can
  5272. afford to waste an open-ended number of man-hours is silly. By far, the
  5273. biggest cost of software development is the human resources. In comparison,
  5274. the cost of development tools is negligible, at least in PC environments and
  5275. even in non-trivial projects on larger systems. If you nickel and dime, you'll
  5276. get a proportional result.
  5277. As a consultant, I initially evaluate a potential client's commitment level to
  5278. solving his problem in a reasonable fashion by examining his investment in
  5279. tools and training.
  5280.  
  5281.  
  5282. The Puzzles
  5283.  
  5284.  
  5285. 1. Incompatible return type.
  5286. /* 1*/ struct tag {
  5287. /* 2*/ int i;
  5288. /* 3*/ double d;
  5289. /* 4*/ }
  5290.  
  5291. /* 6*/ f(struct tag *pst)
  5292. /* 7*/ {
  5293. /* 8*/ return (pst->i);
  5294. /* 9*/ }
  5295.  
  5296. line 8 - Expressions are not assignment compatible
  5297. line 8 - 'return' : incompatible types
  5298. 2. I don't know what I want but I don't like what I see.
  5299. /* 1*/ #include <stdio.h>
  5300.  
  5301. /* 3*/ void f()
  5302. /* 4*/ {
  5303. /* 5*/ int c;
  5304.  
  5305. /* 7*/ While ((c = getchar()) != EOF)
  5306. /* 8*/ putchar(c);
  5307. /* 9*/ }
  5308.  
  5309. line 8 - Syntax error, expected:
  5310. ; <operator> [ ( . -> ,
  5311. line 8 - Expecting ; but found fputc
  5312. 3. Some comments on tokens.(See Listing 1.)
  5313. line 15 - Syntax error, expected:
  5314. ; <operator> [ ( . -> ,
  5315. line 15 - missing ; before identifier printf
  5316. line 15 - Statement missing ; in function main
  5317. 4. Hey, where's my output?
  5318.  
  5319. /* 1*/ #include <stdio.h>
  5320.  
  5321. /* 3*/ main()
  5322. /* 4*/ {
  5323. /* 5*/ switch(6) {
  5324.  
  5325. /* 7*/ case 2: printf("case 2\n");
  5326. /* 8*/ break;
  5327. /* 9*/ case 4: printf("case 2\n");
  5328. /*10*/ break;
  5329. /*11*/ default :printf ("default\n");
  5330. /*12*/ break;
  5331. /*13*/ }
  5332. /*14*/ }
  5333. No compilation errors or warnings were produced but there was not any output.
  5334.  
  5335.  
  5336. The Solutions
  5337.  
  5338.  
  5339. 1. The compiler sees that I am returning an int type expression. However, for
  5340. some reason it is expecting something else. But what? The function is intended
  5341. to have a return type of int. I did not explicitly declare this, but that is
  5342. the default. I'll explicitly declare it and see what happens.
  5343. /* 6*/ int f(struct tag *pst)
  5344. /* 7*/ {
  5345. /* 8*/ return (pst->i);
  5346. /* 9*/ }
  5347.  
  5348. line 6 - Too many types
  5349. in declaration
  5350. line 6 - type following
  5351. struct is illegal
  5352. Well, what the compiler is expecting as a return type is still not exactly
  5353. obvious, but the structure type appears to be somehow involved. If the
  5354. implicit return isn't int, what is it? Here's PC-Lint's hint.
  5355. line 8 - Type mismatch (return)
  5356. (struct = int)
  5357. Now I know that a structure type is actually expected as the return type.
  5358. However, it is still not obvious why since that was not my intent.
  5359. Quite a few years ago, the ability to pass and return structures and unions
  5360. to/from functions by value was added to the language. Consider the following:
  5361. struct tag {
  5362. int i;
  5363. double d;
  5364. };
  5365. struct tag f(struct tag *);
  5366. Here, f is a function taking one argument and returning a structure of type
  5367. struct tag by value. In the rare cases you see such return types declared,
  5368. they would be declared as shown, with the structure template separate from the
  5369. prototype. However, both can be combined. For example:
  5370. struct tag {
  5371. int i;
  5372. double d;
  5373. } f(struct tag *);
  5374. This is also permitted when defining a function, and that is exactly what the
  5375. compiler saw with the original source. If you look carefully, you will notice
  5376. that the semicolon was omitted at the end of the structure template
  5377. definition. As such, it was seen not only as the template definition but also
  5378. the return type of the function.
  5379. As you might expect from this discussion, templates can also be defined within
  5380. a function's argument list. For example:
  5381. void g(struct tag {int i;
  5382. double d;} *);
  5383. From a stylistic viewpoint, I urge you to define such templates separately so
  5384. they can easily be placed in headers.
  5385. 2. Well, the first message isn't much help. The second is similar, but refers
  5386. to fputc instead of putchar. With this compiler, putchar apparently is defined
  5387. as a function-like macro that expands to a call to fputc. With maximum warning
  5388. levels activated, the following more useful messages were produced:
  5389. line 7 - Warning! No prototype
  5390. found for While
  5391. line 7 - Warning: Function While
  5392. not declared
  5393. Now the problem is obvious. The keyword while was spelled with a capital W.
  5394. Since all keywords must be spelled in lowercase, the construct While (...)
  5395. looks like a function call (to an undeclared function returning type int). The
  5396. token putchar makes no sense at that location, so it is flagged.
  5397. I have seen this problem arise more often with Pascal programmers, who seem to
  5398. prefer capitalizing leading characters in identifiers. It also happens with
  5399. If. This is a growing style with X-Windows, MS-Windows and OS/2 programmers.
  5400. 3. Similar to the previous problem, the compiler objects to the token printf,
  5401. not because there's something wrong with it specifically, but because such a
  5402. token cannot be used in this context. The problem is, therefore, "Just what
  5403. context are we in?"
  5404. Perhaps increasing the warning level will help.
  5405.  
  5406. line 13 -Warning: Comment within comment
  5407. line 13 -Warning! Nested comment found in comment
  5408. started on line 10
  5409. Sure enough, there's something suspicious, and the second message is even more
  5410. helpful than the first. A nested comment is a comment inside another comment.
  5411. This might seem to be necessary in the following context:
  5412. /* Disable the following piece
  5413. of code
  5414. /* ... */
  5415. printf("Debug data is ..."};
  5416. End disabled section */
  5417. Neither K&R nor ANSI C permits the nesting of comments, although some
  5418. compilers provide this as an extension. 
  5419. So what's all this got to do with our problem? We aren't trying to nest
  5420. comments! Read the second warning message again carefully. It indicates that a
  5421. comment was begun on line 10. Do you see it? If not, go into an infinite loop
  5422. until you do. While the expression i/*pi states exactly what you want, the
  5423. value of i divided by the value of the int pointed to by pi, what is actually
  5424. seen is the token i followed by the start of a comment. This comment ends at
  5425. the next */seen, at the end of line 13. And since another/* is seen along the
  5426. way, the compiler warns that the start of another comment was seen inside the
  5427. first comment.
  5428. This kind of division expression is not particularly unusual, so how should we
  5429. write it to get what we really want? Putting white space between the / and *
  5430. would make it look silly, and some unsuspecting maintenance programmer may
  5431. well remove it. The best approach is probably to use parentheses as in i/(*p),
  5432. and to also add a comment to the effect "Don't you even think about removing
  5433. these seemingly redundant parentheses." (Certainly, indirection has higher
  5434. precedence than division, so the grouping parentheses are redundant from that
  5435. perspective.)
  5436. Regarding the nested comment issue, "If I really want to disable code
  5437. containing comments, how do I do it?" The solution has always been to use the
  5438. preprocessor in a non-intuitive manner. That is, to conditionally compile code
  5439. such that it is never accepted. For example:
  5440. #if 0 /* Disable the following piece of code/
  5441. printf("Debug data is ..."); /* ... */
  5442. #endif/* End disabled section */
  5443. It's maximally portable and strange enough to get your attention. It's also
  5444. easy to locate #if 0 with your editor to reactivate it in the future.
  5445. 4. I admit this is a contrived puzzle -- I have never actually seen anyone
  5446. accidentally trip over it. However, it's still interesting. Since the switch
  5447. controlling expression matches neither of the cases, you would expect control
  5448. to pass to the default label. Clearly this did not happen since no output
  5449. resulted.
  5450. According to PC-Lint though, there is something suspicious.
  5451. line 13 - switch statement has
  5452. no default
  5453. line 14 - default (line 11) not
  5454. referenced
  5455. On the one hand, there is no default case, but on the other there is a default
  5456. label that's not referenced. Isn't that contradictory? Well, we sometimes read
  5457. what we want to read, not what it says. Note that the unreferenced label is
  5458. spelled "default" not "default." The letter "L" was actually typed as the
  5459. digit "1" making the label an unused user-defined label (suitable for use with
  5460. goto only). Of course, being able to goto a label in the middle of a switch
  5461. construct is very bad style, but style is the business of the programmer, not
  5462. the language definition.
  5463.  
  5464. Listing 1
  5465. /* 1*/ #include <stdio.h>
  5466.  
  5467. /* 3*/ main()
  5468. /* 4*/ {
  5469. /* 5*/ int i = 6;
  5470. /* 6*/ int j = 2;
  5471. /* 7*/ int *pi = &j;
  5472. /* 8*/ int k;
  5473.  
  5474. /*10*/ k = i/*pi;
  5475.  
  5476. /*12*/ while (k > O)
  5477. /*13*/ printf("k = %d\n", k--); /* display k */
  5478.  
  5479. /*15*/ printf("Done\n");
  5480. /*16*/ }
  5481.  
  5482.  
  5483.  
  5484.  
  5485.  
  5486.  
  5487.  
  5488.  
  5489.  
  5490.  
  5491.  
  5492.  
  5493.  
  5494.  
  5495.  
  5496.  
  5497.  
  5498.  
  5499.  
  5500.  
  5501.  
  5502. Applying C++
  5503.  
  5504.  
  5505. Paving The Migration Path
  5506.  
  5507.  
  5508.  
  5509.  
  5510. Dan Saks
  5511.  
  5512.  
  5513. Dan Saks is the owner of Saks & Associates, which offers consulting and
  5514. training in C, C++ and Pascal. He is also a contributing editor of TECH
  5515. Specialist. He serves as secretary of the ANSI C++ committee and is a member
  5516. of the ANSI C committee. Readers can write to him at 287 W. McCreight Ave.,
  5517. Springfield, OH 45504 or by email at dsaks@wittenberg. edu.
  5518.  
  5519.  
  5520. There are many good reasons to switch from C to C++. In addition to the full
  5521. functionality of C, C++ offers stricter compile-time checking, type-safe
  5522. linkage, inline expansion of functions, reference types, overloaded function
  5523. names, and overloaded operators. And of course, C++ provides language-level
  5524. support for encapsulation (data abstraction) and object-oriented programming
  5525. (OOP).
  5526. An unfortunate consequence of the current hype surrounding C++ and OOP is that
  5527. some people believe that using C++ for anything other than OOP (employing both
  5528. inheritance and polymorphism) misuses the language. I strongly disagree. OOP
  5529. is very useful in some applications, but not all. C++ adapts well to different
  5530. styles of programming, and OOP is just one of those styles. The language has
  5531. much to offer, even if you're not ready for OOP.
  5532. Still, there are some practical reasons for not using C++. C++ compilers are
  5533. not as widely available as C compilers. Even if you can find C++ compilers for
  5534. your target environment(s), you might not find the necessary support tools,
  5535. such as symbolic debuggers and application libraries. A formal standard for
  5536. C++ is still years away, and there isn't even a full implementation of the
  5537. current de facto standard [1]. I have no doubt that the obstacles to C++ will
  5538. eventually disappear, but that doesn't help you today.
  5539. Even if you're not ready to move to C++, you can pave the migration path by
  5540. writing your C code so that it also compiles as C++. C++ is an improper
  5541. superset of Standard C; only a short list of C constructs won't compile as
  5542. C++. Fortunately, you can easily rewrite every C construct that is not C++ as
  5543. C that is also C++. (Note: Unless I explicitly write "Classic C", "C" means
  5544. Standard C.)
  5545. Tom Plum calls the common subset of C and C++ "C-". (Don't confuse this with
  5546. "C--", Jim Gimpel's pejorative for C++ as a "strongly hyped" language [2].)
  5547. Converting a program from C to C- is the first step in converting to C++. You
  5548. might as well plan ahead and write all your C as C- by observing the following
  5549. guidelines.
  5550.  
  5551.  
  5552. Function Declarations
  5553.  
  5554.  
  5555. Classic C has fairly relaxed rules for data type conversions. Whereas Standard
  5556. C encourages you to get your data types right (and often warns you when you
  5557. don't), C++ is much more insistent. C++ compilers frequently issue errors for
  5558. transgressions that only merit a warning in C. C++ is particularly finicky
  5559. about function declarations.
  5560. Listing 1 shows a simple Classic C program in the lax style of the first
  5561. edition of K&R [3]. The program compiles as Standard C, but not as C++. C++
  5562. has at least two complaints about Listing 1: (1) greet is called before it is
  5563. declared, and (2) printf is not declared at all. On most C++ compilers, both
  5564. error messages say something about a missing prototype. Declaring printf is
  5565. easy; just add
  5566. #include <stdio.h>
  5567. at the top of the source file. Declaring greet is a little more complicated
  5568. and requires some decisions.
  5569. If you move the definition for greet above main, most C++ compilers will
  5570. accept the definition as a declaration, although they will issue a warning
  5571. that the definition's style is obsolete. Rewrite the old-style heading
  5572. greet (s)
  5573. char *s;
  5574. {
  5575. ...
  5576. as the prototyped heading
  5577. greet(char *s)
  5578. {
  5579. ...
  5580. to silence the warning. With a little extra thought, you might recognize
  5581. opportunities to add appropriate const qualifiers to the parameters, such as
  5582. greet(const char *s)
  5583. Once greet is declared before it's used, the compiler notices that greet has a
  5584. default return type of int, but no return expression. You can add an explicit
  5585. void return type to the function heading, or add a return statement with a
  5586. integral expression. In this case, the void return type is more appropriate.
  5587. Notice that main also has a default return type of int, and no return
  5588. expression. Curiously, most C++ compilers treat a missing return expression as
  5589. an error, but don't even issue a warning for a missing return expression in
  5590. main. I hope future compilers will issue at least a warning. In any event, I
  5591. recommend explicitly declaring main as a function returning int with a normal
  5592. return value of EXIT_SUCCESS (from <stdlib. h>).
  5593. As an alternative to moving greet, you can insert the prototype declaration
  5594. void greet(const char *);
  5595. above main. However, you must still rewrite the function definition as a
  5596. prototype to turn off the compiler's warning for using an obsolete function
  5597. heading. Converting a C program to C-by inserting prototypes is probably
  5598. easier than moving entire function definitions, but I prefer to move the
  5599. functions to eliminate unnecessary prototypes. (See Listing 2.)
  5600. In C, the function declaration
  5601. int f( );
  5602. means that f's parameter list is completely unspecified -- f can have any
  5603. number of arguments of any type. To declare f with no arguments, write
  5604. int f(void);
  5605. In C++, both declarations for f mean that f accepts no arguments. To avoid
  5606. confusion in C-, specify empty parameter lists explicitly using void.
  5607. Function prototypes pose a real problem if you want your header and source
  5608. files to compile as both Classic C and C++. Whereas Standard C supports
  5609. prototypes but doesn't require them, Classic C doesn't support prototypes and
  5610. C++ requires them. If you're still struggling with Classic C compilers, then
  5611. writing in C- may be more trouble than it's worth. Nevertheless, you can use
  5612. the preprocessor to write code that works with both Classic C and C++. See the
  5613. sidebar, "Turning Prototypes On and Off" for details.
  5614.  
  5615.  
  5616. Pointer Conversions
  5617.  
  5618.  
  5619. In Standard C, assignment between different (non-void) pointer types is
  5620. non-portable and produces a diagnostic. Most C compilers generate a warning
  5621. against assignments like
  5622. char *pc;
  5623.  
  5624. int *pi;
  5625. ...
  5626. pc = pi;
  5627. but compile the code nonetheless. C++ treats the assignment as an error. It is
  5628. permitted only with an explicit cast:
  5629. pc = (char *)pi;
  5630. When programming in C-, don't ignore the warnings or turn them off. Rewrite
  5631. the code to use compatible pointers or insert a cast.
  5632. Standard C allows assignment of any pointer type both to and from void
  5633. pointers. This allows C programmers to write functions like
  5634. void free(void *p);
  5635. that quietly accept any pointer type. It also permits the return value of
  5636. functions like
  5637. void *malloc(size_t size);
  5638. to be copied to any pointer type without a cast. C++ considers assignment of
  5639. void pointers to non-void pointers as a "hole" in the type safety, and permits
  5640. it only with an explicit cast. Thus, for example, you must write the
  5641. assignment in
  5642. t *p;
  5643. ...
  5644. p = malloc(sizeof(t));
  5645. as
  5646. p = (t *)malloc(sizeof(t));
  5647. to be valid in C-.
  5648. Listing 3 shows an implementation of the Standard C function memcpy written in
  5649. C-. C does not require the casts in the initializations of t1 and t2, but C++
  5650. does.
  5651. C++ allows assignment of a non-void pointer to a void pointer, but only if the
  5652. non-void pointer points to an object that isn't const or volatile. That is,
  5653. given
  5654. const char name[ ] = "Dan";
  5655. void *p;
  5656. then neither
  5657. p = name;
  5658. nor
  5659. memcpy(name, "Ben", sizeof (name));
  5660. are not permitted in C++. (The first parameter of memcpy is a non-const void
  5661. *). C compilers typically compile these statements with only a warning. As
  5662. always, you can force the conversion to void * with an explicit cast.
  5663.  
  5664.  
  5665. Enumerations
  5666.  
  5667.  
  5668. In C, enumeration types are simply integral types. For example, given
  5669. enum color {RED, WHITE, BLUE};
  5670. enum color c;
  5671. int n;
  5672. then C permits assignments like
  5673. c = 1;
  5674. n = BLUE;
  5675. C even permits
  5676. enum day {YESTERDAY, TODAY,
  5677. TOMORROW};
  5678. enum day d = RED;
  5679. although some compilers issue a warning for this assignment.
  5680. C++ treats each enumeration as a distinct type. Assignments like 
  5681. c = 1;
  5682. d = RED;
  5683. are not allowed. C++ still promotes enumeration constants to integers, so
  5684. n = BLUE;
  5685. is legal C++.
  5686. When programming in C-, treat each enumeration as a distinct type. However,
  5687. you can safely assume that enumerations promote to integers.
  5688.  
  5689.  
  5690. Linkage
  5691.  
  5692.  
  5693. In Standard C, a global data object may be declared repeatedly without the
  5694. extern specifier, in the same program or even in the same compilation. In C++,
  5695. a global data declaration without extern is a definition, and each global data
  5696. object must be defined exactly once in a program. (Note that a global data
  5697. declaration with an initializer - with or without the extern specifier - is
  5698. also a definition.)
  5699. For example, suppose an integer total is shared by several source files in a
  5700. C++ program. total must be defined in exactly one of the files using any of
  5701. the following:
  5702. int total;
  5703. int total = 0;
  5704.  
  5705. extern int total = 0;
  5706. Any other source file that references total must declare
  5707. extern int total;
  5708. When programming in C-, observe the linkage rules of C++. Make sure that the
  5709. external declarations in your header files are indeed declarations (with
  5710. explicit extern and no initializer) and not definitions. If you use an
  5711. external definition in a header and include that header in more than one
  5712. source file of a C++ program, the linker will protest against multiple defined
  5713. names.
  5714. In C, the default linkage for global const objects is extern; in C++ it's
  5715. static. For example, suppose file f.c contains
  5716. const MAX = 100;
  5717. and file g.c contains
  5718. extern const MAX;
  5719. If you compile and link f.c and g.c using C, the MAX in f.c has external
  5720. linkage and provides a definition to satisfy the reference in g.c. If you
  5721. compile using C++, the MAX in f.c has internal linkage, and the MAX in g.c is
  5722. an unresolved reference. In C-, use explicit storage class specifiers (extern
  5723. or static) on all global const declarations.
  5724.  
  5725.  
  5726. Name Spaces
  5727.  
  5728.  
  5729. C++ has 16 more keywords than Standard C:
  5730. asm private
  5731. catch protected
  5732. class public
  5733. delete template
  5734. friend try
  5735. inline this
  5736. new virtual
  5737. operato throw
  5738. C- programs should not use these keywords as identifiers.
  5739. C++ predefines the macro __cplusplus. Many C++ compilers (especially those
  5740. based on AT&T's cfront translator) use names containing __ (a double
  5741. underscore) in the translation process. C- programs should avoid identifiers
  5742. with double underscores anywhere in the name. [In standard C, you should avoid
  5743. even a single leading underscore. -pjp]
  5744. C puts all ordinary identifiers (such as function, type and variable
  5745. identifiers) in a single (scoped) name space, and puts all tags (for
  5746. enumerations, structures, and unions) in a separate name space. Thus, for
  5747. example, C lets you declare a structure and a function with the same name in
  5748. the same scope, like
  5749. struct tnode
  5750. {
  5751. char *word;
  5752. struct tnode *left, *right;
  5753. };
  5754. int tnode(const char *s);
  5755. Tag names in C are not type names, so you can't write
  5756. tnode *t;
  5757. You must carry the tag keyword around with the tag name, as in
  5758. struct tnode *t;
  5759. You can simplify tag references in C by defining a type name with the same
  5760. spelling as the tag, like
  5761. typedef struct tnode tnode;
  5762. and write
  5763. struct tnode *t;
  5764. as simply
  5765. tnode *t;
  5766. In C++, tags are also type names. That is, you can declare
  5767. tnode *t;
  5768. without also defining tnode as a typedef. For compatibility with C, C++
  5769. accepts
  5770. typedef struct tnode tnode;
  5771. as well as
  5772. struct tnode *t;
  5773. However, C++ does not allow a tag name to also be a function or variable name
  5774. in the same scope, so that
  5775. struct tnode;
  5776. ...
  5777. int tnode(const char *s);
  5778. is invalid.
  5779. In C-, simply define each tag name as a type name in the same scope. For
  5780. example, declare tnode in C- as
  5781. typedef struct tnode tnode;
  5782. struct tnode
  5783. {
  5784. char *word;
  5785. tnode *left, *right;
  5786.  
  5787. };
  5788. and use tnode (rather than struct tnode) in all subsequent references.
  5789. In C, a tag name or enumeration constant defined within a struct (or union)
  5790. has the same scope as that struct. In Listing 4, for example, tag names t and
  5791. e and enumeration constants X and Y have the same scope as tag name s. Thus
  5792. the declarations of ee and tt are perfectly valid, but
  5793. const int X = 0;
  5794. is an error in C because X is already defined as something else.
  5795. C++ compilers interpret Listing 4 differently depending on their vintage.
  5796. According to the C++ Annotated Reference Manual [1] (based on the AT&T 2.1 C++
  5797. Product Reference Manual [4]), classes introduce a new scope. Since structs
  5798. are just a special case of classes, nested type names and enumeration
  5799. constants are local to the enclosing struct. By this rule,
  5800. const int X = 0;
  5801. is valid. However, the declarations of ee and tt are invalid, because
  5802. identifiers e and t are out of scope. 
  5803. Earlier versions of C++ (including those compatible with AT&T Release 2.0)
  5804. export tag names defined in a struct to the scope of that struct, but keep
  5805. enumeration constants local to the struct. By these rules, the declarations of
  5806. ee, tt and const int X are allowed, but the reference to Y in the initializer
  5807. of ee is undefined.
  5808. Although nested struct and enum definitions can be useful in C++, they're not
  5809. much help in C. Given the variation in rules for nested definitions, your
  5810. safest bet is to avoid nested definitions in C-.
  5811.  
  5812.  
  5813. Odds And Ends
  5814.  
  5815.  
  5816. C discards extra characters in a string initializer. For instance, C ignores
  5817. the \0 at the end of "Dan" in
  5818. char name[3] = "Dan";
  5819. C++ refuses to turns its back on defenseless characters. You must write the
  5820. initializer as
  5821. char name[3] = {'D', 'a', 'n'};
  5822. or face the wrath of the compiler.
  5823. In C, sizeof('a') equals sizeof(int), but in C++, sizeof('a') equals
  5824. sizeof(char). I have yet to encounter a situation in C- where this makes a
  5825. difference. Please let me know if you find one.
  5826.  
  5827.  
  5828. A Step In The Right Direction
  5829.  
  5830.  
  5831. To a large extent, writing your C code so that it also compiles as C++ imposes
  5832. no restrictions on the expressive power of C. Rather, it forces you to abandon
  5833. poor coding practices, most of which are already considered obsolete by the C
  5834. standard. The resulting code is just as efficient, but a little safer than
  5835. Standard C, thanks to C++'s more rigorous type checking. By writing in C-, you
  5836. are paving the road to the future. You can step up to a better C whenever
  5837. you're ready.
  5838. References
  5839. [1] Ellis, Margaret A. and Stroustrup, Bjarne, The Annotated C++ Reference
  5840. Manual. Addison-Wesley, Reading, MA, 1990.
  5841. [2] Gimpel, Jim, "C-," The C Gazette, 4:4, Summer 1990.
  5842. [3] Ritchie, Dennis M. and Kernighan, Brian W., The C Programming Language.
  5843. Prentice-Hall, Englewood Cliffs, NJ, 1978.
  5844. [4] AT&T C++ Language System Release 2.1 Product Reference Manual, AT&T, 1989.
  5845. Turning Prototypes On And Off
  5846. C++ requires that function declarations have prototyped parameter lists, but
  5847. Classic C won't accept prototypes. Code that compiles as both C++ and Classic
  5848. C must sense which language is compiling the code, and turn the prototypes on
  5849. and off accordingly. C++ compilers predefine the macro _cplusplus, so
  5850. #ifdef _cplusplus
  5851. determines the current language.
  5852. The simplest way to write a header that is both Classic C and C++ is to write
  5853. all the function declarations twice - once with an empty parameter list and
  5854. once with a prototyped parameter list. Select the appropriate declarations by
  5855. testing _cplusplus, as in
  5856. #ifdef __cplusplus
  5857. double foo(char *, int);
  5858. int bar(char *);
  5859. #else
  5860. double foo( );
  5861. int bar( );
  5862. #endif
  5863. If you would also like to use prototypes when compiling with Standard C, add a
  5864. test for the predefined macro _STDC_. You can write the test as
  5865. #if defined_cplusplus \
  5866. defined __STDC______LINEEND____
  5867. but Classic C compilers might not understand the defined operator. Write the
  5868. test as
  5869. #ifdef __cplusplus
  5870. #define PROTOTYPES
  5871. #else
  5872. #ifdef __STDC______LINEEND____
  5873. #define PROTOTYPES
  5874. #endif
  5875. #endif
  5876. To avoid writing each function declaration twice, make each parameter list
  5877. conditional. Define a macro PROTO as
  5878. #ifdef PROTOTYPES
  5879. #define PROTO(x) x
  5880. #else
  5881.  
  5882. #define PROTO(x) ( )
  5883. #endif
  5884. Then write the function declarations as
  5885. double foo PROTO((char *, int));
  5886. int bar PROTO((char *));
  5887. The extra set of parentheses around each parameter list transforms the
  5888. comma-separated list of tokens into a single macro argument. Thus, if
  5889. prototypes are available, then
  5890. PROTO((char *, int))
  5891. expands to
  5892. (char *, int)
  5893. Otherwise, it expands to just ( ).
  5894. C++ wants you to replace old-style function definition headings like
  5895. double foo(s, n)
  5896. char *s;
  5897. int n;
  5898. {
  5899. ...
  5900. with prototype definitions like
  5901. double foo(char *s, int n)
  5902. {
  5903. ...
  5904. But Classic C won't accept the prototypes. To make the replacement
  5905. conditional, write the function definition as 
  5906. #if PROTOTYPES
  5907. double foo(char *s, int n)
  5908. #else
  5909. double foo(s, n) char *s; int n;
  5910. #endif
  5911. {
  5912. ...
  5913. This heading is admittedly hard to read. Since C++ only issues a warning (not
  5914. an error) for old-style headings, you may prefer to just live with the
  5915. warnings.
  5916.  
  5917. Listing 1
  5918. /*
  5919. * Greetings in Classic C
  5920. */
  5921. main()
  5922. {
  5923. greet("Dan");
  5924. }
  5925.  
  5926. greet(s)
  5927. char *s;
  5928. {
  5929. printf("Greetings, %s!\n", s);
  5930. }
  5931.  
  5932.  
  5933. Listing 2
  5934. /*
  5935. * Greetings in C-
  5936. */
  5937. #include <stdio.h>
  5938. #include <stdlib.h>
  5939.  
  5940. void greet(const char *s);
  5941. {
  5942. printf("Hello, %s!\n", s);
  5943. }
  5944.  
  5945. int main(void)
  5946.  
  5947. {
  5948. greet("Dan");
  5949. return EXIT_SUCCESS;
  5950. }
  5951.  
  5952.  
  5953. Listing 3
  5954. /*
  5955. * memcpy in C-.
  5956. */
  5957. void *memcpy(void *s1, const void *s2, size_t n)
  5958. {
  5959. char *t1 = (char *)s1;
  5960. const char *t2 = (const char *)s2;
  5961.  
  5962. while (n-- > 0)
  5963. *t1++ = *t2++;
  5964. return s1;
  5965. }
  5966.  
  5967.  
  5968. Listing 4
  5969. struct s
  5970. {
  5971. enum e {X, Y} b;
  5972. struct t {int i;} a;
  5973. };
  5974.  
  5975. enum e ee = Y;
  5976. struct t tt;
  5977. const int X = 10;
  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. Questions & Answers
  6011.  
  6012.  
  6013. More On const
  6014.  
  6015.  
  6016.  
  6017.  
  6018. Ken Pugh
  6019.  
  6020.  
  6021. Kenneth Pugh, a principal in Pugh-Killeen Associates, teaches C language
  6022. courses for corporations. He is the author of C Language for Programmers and
  6023. All On C, and is a member on the ANSI C committee. He also does custom C
  6024. programming for communications, graphics, and image databases. His address is
  6025. 4201 University Dr., Suite 102, Durham, NC 27707. You may fax questions for
  6026. Ken to (919) 493-4390. When you hear the answering message, press the * button
  6027. on your telephone. Ken also receives email at kpugh@dukemvs.ac.duke.edu
  6028. (Internet) or dukeac!kpugh (UUCP).
  6029.  
  6030.  
  6031. Last month, one of the questions revolved around the const modifier.
  6032. The const type modifier is difficult to understand, especially when combined
  6033. with pointers. Although some authors put it first in the declaration (along
  6034. with the storage type), I am inclined to place the const modifier after the
  6035. data type, since that more clearly reflects what the modifier is doing.
  6036. A non-modified type such as
  6037. int i = 5;
  6038. sets aside memory for the variable i and does not give any information as to
  6039. whether the contents will be changed during the program's execution.
  6040. int const i = 5;
  6041. or
  6042. const int i : 5;
  6043. both declare that i is an integer constant. That is, its contents will not be
  6044. modified during execution. Thus the compiler could place it in read-only
  6045. memory (especially if it was a global or static variable) or it could use this
  6046. information to optimize the code.
  6047. Although you probably would not use this for a single integer in place of a
  6048. #define, you might use this for an array or structure that was initialized and
  6049. never altered.
  6050. The keyword static is used for local variables whose memory is allocated at
  6051. load time. You can initialize static variables and not change them. However
  6052. the compiler does not prevent you from accidentally writing over their initial
  6053. contents. The const variable is designed to protect against that occurrence.
  6054. Its protection is compiler dependent, not absolute. A particular
  6055. compiler/processor pair may not support read-only memory.
  6056. The possibilities with static and const are:
  6057. static int i = 5; /* Static variable whose
  6058.  contents may change */
  6059. static int const i = 5; /* Static variable whose
  6060.  contents will not
  6061.  be changed */
  6062. int const i : 5; /* Variable whose contents
  6063.  will not be changed */
  6064. Now that I've covered the simple use of const, I would like to discuss its use
  6065. with pointers. There are two possibilities for placing const.
  6066. char const *pc1; /* const char
  6067. *pc1; */
  6068. char * const pc2;
  6069. pc1 is a pointer that points to characters that are of type const. You cannot
  6070. dereference the pointer (with a *) and assign something to that location. The
  6071. following is valid:
  6072. char const c_const;
  6073. char const another_c_const;
  6074. char const *pc1;
  6075. pc1 = &c_const;
  6076. but you cannot then assign
  6077. *pc1 = 'A';
  6078. since you are attempting to change the char constant. But you can perform:
  6079. pc1 : &another_c_const;
  6080. since the contents of pc1 are not const, only what it points to.
  6081. On the other hand, pc2 is a constant pointer to characters that can change.
  6082. For example, with:
  6083. char variable_c;
  6084. char another_variable_c;
  6085. char * const pc2 = &variable_c;
  6086. then
  6087. *pc2 = 'A';
  6088. is perfectly valid, but
  6089. pc2 = &another_variable_c;
  6090. is not, since you are trying to change the contents of pc2, which is to remain
  6091. constant.
  6092. Because of pointers, I prefer placing the const after the type. If you
  6093. declare:
  6094.  
  6095. const char *pc1;
  6096. the variable pc1 is not what is constant. It is the char to which it points.
  6097. As shown previously, the way to declare that pc1 is constant is:
  6098. char * const pc1;
  6099. This arrangement appears inconsistent and confusing. Using the const after the
  6100. type follows a regular pattern. If the const keyword appears just before the
  6101. variable name, then that variable is const.
  6102. Note that you can assign a less constant type to a more constant type. For
  6103. example, the following is valid:
  6104. char *pc3;
  6105. char const *pc1;
  6106. pc1 = pc3;
  6107. You just cannot use pc1 to alter any values being pointed at.
  6108. *pc3 = 'a'; /* Valid */
  6109. *pc1 : 'a'; /* Invalid */
  6110. We could use const in both places in the declaration. For example:
  6111. char const * const pc3 = c_const;
  6112. Both of the following are invalid:
  6113. *pc3 : 'A';
  6114. pc3 : &another_const;
  6115. Now for one more step. Lets look at double pointers, since that is the crux of
  6116. your question. Given these declarations:
  6117. char const char_const;
  6118. char variable_char;
  6119. char * ptr;
  6120. char const * ptr_to_char_const;
  6121. char * const ptr_const_to_char = &variable_char;
  6122. char const * const ptr_const_char_const;
  6123. You have at least the following possibilities for the declaration and
  6124. assignment of the double pointer:
  6125. char ** ptr_to_ptr_to_char;
  6126. char const ** ptr_to_ptr_to_char_const;
  6127. char * const * ptr_to_ptr_const_to_char;
  6128. char ** const ptr_const_to_ptr_to_char = &ptr;
  6129.  
  6130. ptr_to_ptr_to_char = &ptr;
  6131. ptr_to_ptr_to_char_const = &ptr_to_char_const;
  6132. ptr_to_ptr_const_to_char = &ptr_const_to_char;
  6133. Notice that the last of these declarations has to include the initialization,
  6134. since the variable itself is being declared as const. I won't go into an
  6135. explanation of all of these. You can work out the remaining possible variable
  6136. declarations as:
  6137. char const * const * ptr_to_ptr_const_to_char_const;
  6138. When a pointer to const appears in a function's parameter declaration, it
  6139. implies that the function will not change the variable pointed at by the
  6140. parameter that is passed. The actual variable pointed at by the parameter can
  6141. be a non-const variable.
  6142. This is not needed for the parameters themselves, as a copy is always passed
  6143. to the function. With ints, for example, it is redundant to state in a
  6144. prototype:
  6145. int integer_compare_function ( int const integer1,
  6146. int const integer2);
  6147. For string pointers, which may be changed by the called routine, it is also
  6148. redundant to state:
  6149. int change_strings(char * const string);
  6150. Although it is extraneous in the prototype, you may wish to specify the
  6151. parameters in the function header this way. Inside the function you would not
  6152. be able to accidentally alter their values.
  6153. If the actual strings are not going to be changed, then you would code the
  6154. prototype as:
  6155. int string_compare_function(char const *string1,
  6156. char const *string2);
  6157. Inside the function, you could not make assignments as:
  6158. *string1 = 'a';
  6159. but you could perform:
  6160. string1 = string2;
  6161. If the function were being passed handles (pointers to pointers), then the
  6162. function could be prototyped as:
  6163. int string_handle_compare_function(char const * const
  6164. * string1, char const * const * string2);
  6165. You would not be permitted to use either:
  6166. * string1 = *string2;
  6167. **string1 = 'a';
  6168.  
  6169.  
  6170. Readers' Replies
  6171.  
  6172.  
  6173.  
  6174.  
  6175.  
  6176. Indentation
  6177.  
  6178.  
  6179. Generally, I find your column most interesting. Keep up the good work.
  6180. However, I was a bit disappointed by your comments on Brian S. Merson's letter
  6181. in the September issue.
  6182. You said, "Your style has a lot going for it." While there is nothing
  6183. particularly bad about his style of indentation, it seems to me to be a
  6184. classic case of a solution to a non-problem for two reasons:
  6185. 1) Even given his program as it stood, he could just have drawn a line to the
  6186. left of the braces which would not then have run through the first character
  6187. of the code. Ensuring that all the code was legible would seem to be a basic
  6188. precaution taken before debugging.
  6189. 2) What are aflag and eflag doing in a serious program? Variables should have
  6190. meaningful names with a minimum Hamming distance between them, i.e., they
  6191. should differ in a number of positions.
  6192. Donal Lyons
  6193. Dublin, Ireland
  6194. The style to which you refer in #1 looked like:
  6195. for (...)
  6196. {
  6197. /* Internals indented one space to right of brace */
  6198. }
  6199. I agree with you regarding #2. That's why I started the Obscure Name Contest.
  6200. (KP)
  6201.  
  6202.  
  6203. Pointer Blues
  6204.  
  6205.  
  6206. There may be a simpler explanation than "pointer blues" for the problem
  6207. mentioned by Mark Petrovic in the Q?/A! column in The C Users Journal
  6208. September 1990. He reported that some simple programs did not run correctly
  6209. until a printf() statement was added for debugging. Some C output functions
  6210. are buffered and do not appear to work when stepping through a program in
  6211. debug mode, until the buffer is dumped -- which a printf() statement does. His
  6212. description of the way in which the programs didn't work isn't detailed enough
  6213. to know if this is what he was encountering.
  6214. Janet Price
  6215. Kalamazoo, MI
  6216. You are correct. The printf buffer is usually not dumped to the screen until a
  6217. \n appears. Without the code though, it is difficult to state precisely what
  6218. went on. (KP)
  6219.  
  6220.  
  6221. repeat_format
  6222.  
  6223.  
  6224. In your Q?/A! column (The C Users Journal, September 1990, page 111), I
  6225. noticed another reader (Doug Oliver, Wichita, KS) had written a function,
  6226. repeat_format, which expands format strings with embedded repeat
  6227. specifications to make printf-compatible format strings. He pointed out that
  6228. the function returned the address of data local to repeat_format, which
  6229. technically is not guaranteed valid, but which may be copied and used anyway.
  6230. The reason this data may be so used has to do with stack usage. Each time the
  6231. function repeat_format is called, space is reserved on the stack for local
  6232. data. My compiler (Turbo C 1.5), and perhaps his (he used QuickC), places the
  6233. string space for newfmt[] farthest away in the stack area from BP (the
  6234. processor register relative to whose value local variables and parameters are
  6235. referenced in the compiled functions' code). Also, the 256 bytes reserved for
  6236. newfmt[] in repeat_format is nearly 100 bytes longer than any of the format
  6237. strings generated by the sample driver.
  6238. In the sample driver and in the recursive calls within repeat_format, it is
  6239. used as the source parameter to strcpy, e.g. strcpy(newstr,
  6240. repeat_format(fmtstr[i])), whose stack uses a portion of repeat_format's
  6241. now-invalid data area. Fortunately, in this case other local variables, which
  6242. are not used for anything after the function returns, are the victims.
  6243. To verify this, I changed a copy of the C source so that newstr[152] was used
  6244. as the last local data item declared in repeat_format. (152 is only slightly
  6245. longer than the minimum string length necessary to hold the longest format
  6246. string returned to the sample program.) The result was that, when run, garbage
  6247. was displayed at the end of the longest format string. As long as
  6248. repeat_format is left in its original form, and used as a source parameter for
  6249. strcpy, it should be safe-tempstr[]. The other local variables provide more
  6250. than enough room for strcpy to execute safely.
  6251. It should be noted, though, that the function is somewhat stack-hungry,
  6252. requiring approximately 350 bytes minimum per call, and could run into trouble
  6253. in some memory models if deeply-nested format strings were expanded in a
  6254. data-intensive application.
  6255. I also found it interesting to use this function with dprintf, which also
  6256. appeared in The C Users Journal (September 1990). By modifying dprintf so that
  6257. it uses repeat-format to expand the format string passed to it, and passing
  6258. this result to vdprintf, format strings with repeat format specifiers may be
  6259. treated as ordinary format specifiers in calls to dprintf.
  6260. While testing these functions, I decided to check them for Microsoft
  6261. compatibility with Learn C, a subset of QuickC developed by Microsoft and
  6262. marketed by Microsoft Press. Unfortunately, dprintf would not run under Learn
  6263. C, but, instead, generated the message unresolved external: ftol. I
  6264. subsequently found out that it would not convert from float to int. I am still
  6265. waiting to hear from Microsoft about the problem as of this writing.
  6266. Thank you. I enjoy your column and The C Users Journal.
  6267. John W. Bandy
  6268. Armuchee, GA
  6269. Thank you for your analysis of the code. Using the address of an automatic
  6270. variable, when that variable is no longer allocated, is a cause for
  6271. indeterminate code. The algorithm here will work as long as the
  6272. processor/compiler does not make the previously allocated storage unavailable
  6273. (i.e., to cause an addressing exception to occur).
  6274. One could allocate a large static buffer to fill up with the generated string
  6275. and return the address of that buffer. This is the way that some standard
  6276. library routines work. Whether statics or automatics are used, the routine
  6277. should check to see it does not exceed the length of the array. Some compilers
  6278. allocate automatic variables on the same stack used for return addresses.
  6279. Exceeding array limits causes interesting debugging problems on those
  6280. machines. (KP)
  6281.  
  6282.  
  6283. External Identifiers
  6284.  
  6285.  
  6286. I would like to address Andreas Lang's question in the October 1990 issue
  6287. about keeping all public variables, extern or not, in one header file.
  6288. Learning from other programmer's code and coming up with a few ideas of my
  6289. own, I have developed a scheme for just this purpose.
  6290. Listing 1 shows how I would write the example in question. MAIN is #defined in
  6291. a file that includes this header file, EXT is expanded to nothing, and the
  6292. initialization is taken by the preprocessor. Otherwise, EXT is expanded to
  6293. extern and the initialization is skipped.
  6294. I have found this scheme very useful, I can keep a single header file for
  6295. variables and initialize them too.
  6296. Bill Sharar II
  6297. Denton, Texas
  6298. I was very much interested in your response to Andreas Lang in the October
  6299. 1990 C Users Journal. He is attempting to create a single header file to be
  6300. shared among any number of source files. This header file would allow
  6301. reference to the global variables for the program by declaring them as
  6302. external to all the source files except for one (the "main"source file). His
  6303. attempt works well except in the case where a global variable must be
  6304. initialized. I have another solution to the problem, which, though inelegant,
  6305. would allow him to do what he wishes.
  6306. Using his example, test.h would look like Listing 2.
  6307. The remainder of his example would be unchanged. (I find the use of the
  6308. keyword Global clearer than the EXTERN used in the example. It better defines
  6309. what we are trying to do, which is declare a global variable.)
  6310. Yes, it is kind of ugly, but it does work (at least when using Microsoft C
  6311. v5.1). It permits the same code string to declare, define, and initialize the
  6312. variable as needed, since everything is in one place, with all the associated
  6313. maintenance benefits.
  6314. In that same issue, in your response to Frederick C. Smith, you state that
  6315. stderr cannot be redirected under MS-DOS. This statement requires
  6316. clarification. While it is true that there is no command-line facility
  6317. available to redirect stderr, such as >& in the C-shell under UNIX,
  6318. redirection under program control is possible by calling the freopen function:
  6319. freopen("error.log", "w", stderr);
  6320.  
  6321. system("cmd");
  6322. (These functions do return values that should be checked, but I omitted this
  6323. for the sake of brevity.)
  6324. The previous code sequence would redirect to the new file error.log, then run
  6325. the program cmd. Any error messages from cmd would then be redirected to
  6326. error.log (assuming they were written to stderr).
  6327. I look forward to your column each month in The C Users Journal. I find it
  6328. educational and well-written, and like the rest of the magazine, well worth
  6329. reading. Thank you.
  6330. David Hansen
  6331. I think I have a solution to Andreas Lang's problem (October 1990) about
  6332. initializing external variables in header files without having to write them
  6333. in any other files.
  6334. The macros EXTERN and INIT are defined in the header file as shown in Listing
  6335. 3. The variable is then written in the header file as:
  6336. EXTERN int i INIT(5);
  6337. So, in the main file, the preprocessor produces:
  6338. int i = 5;
  6339. and in all other files it produces:
  6340. extern int i;
  6341. Note that the INIT macro will work independent of val's type (I think). Is
  6342. there anything questionable (or just plain wrong) about this method?
  6343. Larry Leonard
  6344. Norcross, GA
  6345. The only problem with this method is that it looks awkward when initializing
  6346. an array or anything that requires more than one line of initial values. For
  6347. example:
  6348. EXTERN int array[15] INIT({1,2,3,4,5,6,7,8,9,10,\
  6349. 11,12,13,15});
  6350. Notice both the braces next to parentheses and the need for the \ to carry the
  6351. initializing string to the next line. [Some older compilers required the
  6352. backslash, but it is not required in standard C. - pjp]
  6353. Even given the previous three responses, I think I still prefer using a single
  6354. header file that contains the variable declarations with the keyword extern
  6355. and a separate source file with the declarations and initial values.
  6356. Most of the time I try to avoid using global variables entirely and use access
  6357. routines that look like:
  6358. static int global_variable = 5;
  6359. set_global_variable (value)
  6360. {
  6361. global_variable : value;
  6362. }
  6363. get_global_variable(value)
  6364. {
  6365. return value;
  6366. }
  6367. There is no header file to include and only one place where the definition has
  6368. to appear. (KP)
  6369.  
  6370. Listing 1
  6371. #ifdef EXT
  6372. #undef EXT
  6373. #endif
  6374.  
  6375. #ifdef MAIN
  6376. #define EXT
  6377. #else
  6378. #define EXT extern
  6379. #endif
  6380.  
  6381. EXT int i
  6382. #ifdef MAIN
  6383. = 5
  6384. #endif
  6385.  
  6386.  
  6387. Listing 2
  6388. #ifdef MAIN
  6389. #define Global
  6390. #define INIT_GLOBAL
  6391. #else
  6392. #define Global extern
  6393. #endif
  6394.  
  6395. Global int i
  6396. #ifdef INIT_GLOBAL
  6397.  
  6398. = 5
  6399. #endif
  6400. ;
  6401.  
  6402.  
  6403. Listing 3
  6404. /* ---------------------- CUJ.H ----------------------------- */
  6405.  
  6406. #ifdef MAIN
  6407. #define EXTERN
  6408. #define INIT(val) = val
  6409. #else
  6410. #define EXTERN extern
  6411. #define INIT(ignored)
  6412. #endif
  6413.  
  6414. EXTERN int i INIT(5);
  6415.  
  6416. /* ---------------------- CUJ.C ----------------------------- */
  6417.  
  6418. #include <stdio.h>
  6419. #define MAIN
  6420. #include "cuj.h"
  6421.  
  6422. main()
  6423. {
  6424. printf("The value of i in main() is %d.\n", i);
  6425. cuj_fnx();
  6426. }
  6427. /* ---------------------- CUJ_FNX.C ------------------------- */
  6428.  
  6429. #include <stdio.h>
  6430. #include "cuj.h"
  6431.  
  6432. cuj_fnx()
  6433. {
  6434. printf("The value of i in cuj_fnx() is %d.\n", i);
  6435. }
  6436.  
  6437.  
  6438.  
  6439.  
  6440.  
  6441.  
  6442.  
  6443.  
  6444.  
  6445.  
  6446.  
  6447.  
  6448.  
  6449.  
  6450.  
  6451.  
  6452.  
  6453.  
  6454.  
  6455.  
  6456.  
  6457.  
  6458.  
  6459.  
  6460.  
  6461. Implementer's Notebook
  6462.  
  6463.  
  6464. Expect
  6465.  
  6466.  
  6467.  
  6468.  
  6469. Don Libes
  6470.  
  6471.  
  6472. Don Libes is a computer scientist at the National Institute of Standards and
  6473. Technology. He is also the author of Life With UNIX, published by
  6474. Prentice-Hall. His electronic mail address is libes@cme.nist.gov. He can also
  6475. be reached at NIST, Bldg. 220, Rm A-127, Gaithersburg, MD 20899.
  6476.  
  6477.  
  6478. In my September 1990 column, I discussed the embeddable language, Tcl (Tool
  6479. Command Language), written by John Ousterhout at University of California at
  6480. Berkeley. In that column, I described how to build tools using Tcl. I recently
  6481. built a tool using Tcl called expect, which has become quite popular. I would
  6482. like to share some of the internals of it with you.
  6483. Briefly, expect can play the role of a user in an interactive program. In
  6484. effect, it can force an interactive program to be non-interactive! The first
  6485. problem I applied expect to was (of course) a game: Rogue.
  6486.  
  6487.  
  6488. Automating Rogue
  6489.  
  6490.  
  6491. Rogue is an adventure game that presents you with a player who has various
  6492. physical attributes. One attribute is a strength rating. Most of the time the
  6493. strength is 16, but every so often -- maybe one out of 20 games -- you get an
  6494. unusually good strength of 18. Many people know this, but no one restarts the
  6495. game 20 times to find those really good configurations. Well, the expect
  6496. script in Listing 1 can do it for you.
  6497. The script works as follows: Inside a for loop, Rogue is started by using the
  6498. spawn keyword. The strength is checked to see if it is either 18 or 16. If it
  6499. is 16, the next statement in the script is executed, closing the connection
  6500. and effectively sending an EOF to Rogue, which goes away. The loop is then
  6501. restarted, and a new game of Rogue of run. When a strength of 18 is found, you
  6502. break out of the loop and drop down to the bottom of the script, executing the
  6503. next line, which says "interact". Executing this line passes control back to
  6504. the real user, allowing him to play this particular game.
  6505. If you run this script, you'll actually see 20 to 30 initial configurations
  6506. fly across your screen in less than a second, finally stopping with a great
  6507. game for you to play. The only way to play Rogue better is under the debugger!
  6508.  
  6509.  
  6510. The expect Command Described
  6511.  
  6512.  
  6513. I'm not going to explain all of expect. The Rogue script should give you
  6514. enough of a taste. Instead, I'm going to focus on the most interesting part:
  6515. the expect command itself. It illustrates a number of concepts, plus it is
  6516. real code that you might like to plug into your application, with or without
  6517. the rest of the expect program.
  6518. The expect command reads from the output of another process. The output is
  6519. examined for a pattern that matches any of the patterns supplied as arguments
  6520. to the command itself. Expect takes arguments as pattern-action pairs. When
  6521. any of the patterns match, the corresponding action is executed. Actions are
  6522. just Tcl statements (such as a send, or even another expect command). As in C,
  6523. compound statements may be grouped by enclosing them in braces.
  6524. In the script above, there are two patterns. When *Str: 18* is seen, the break
  6525. statement is executed. When *Str: 16* is seen, nothing (the null action) is
  6526. executed. (The * matches anything.)
  6527. One other feature not illustrated by the script is that the keywords eof and
  6528. timeout are special patterns. If an EOF appears in the stream, the action
  6529. paired with the eof pattern (if there is one) is executed. Similarly, if a
  6530. certain time period expires without matching any patterns, the timeout action
  6531. is executed.
  6532.  
  6533.  
  6534. The expect Command Implemented
  6535.  
  6536.  
  6537. Expect follows the usual Tcl calling conventions, which are shown in Listing
  6538. 2. The first argument is a programmer-supplied pointer (unused here, hence the
  6539. ARGSUSED comment to lint.) The second is a pointer to an interpreter instance
  6540. (always the same here). The third and fourth arguments are the command
  6541. arguments that the script supplied, presented in argc/argv style.
  6542. Immediately upon entry, the arguments are checked for correct usage. (See
  6543. Listing 3.) In this case, all we have to do is check for at least one pattern.
  6544. Expect assumes that a final missing action is just the null statement. This
  6545. means you can write statements like expect foo, which just delays until foo or
  6546. EOF appears, or the timeout occurs.
  6547. The tcl_error function saves a message to be passed back to the interpreter
  6548. evaluating each Tcl statement. It will print out the message when it sees
  6549. TCL_ERROR returned.
  6550. Next, the stream is selected. The script can switch between streams by setting
  6551. the Tcl variable spawn_id (also set by the spawn command). Similarly, the
  6552. timeout period is specified by setting the Tcl variable timeout. Listing 4
  6553. shows how we retrieve and use these values from Tcl. (The third argument
  6554. indicates scoping, but will not be further explained here.) These values are
  6555. not passed as parameters because they are expected to change infrequently.
  6556. Thus, they are changed by giving an explicit command (e.g., set timeout 60.)
  6557. The script controls one other variable. match_max defines the number of
  6558. characters that expect guarantees it will use to match patterns. The default
  6559. is 2,000. If patterns must match more than 2,000 bytes, the script must raise
  6560. this value explicitly. expect doesn't automatically use a very large value,
  6561. since that can needlessly slow down the pattern matching process. (More on
  6562. this later.)
  6563. Fetching and resetting the buffer is shown in Listing 5. realloc is called on
  6564. the presumption that if the buffer shrinks, the function is likely to
  6565. efficiently return the same buffer. Similarly, if the buffer grows back, the
  6566. function will return the same buffer, if possible. The logic actually
  6567. allocates space that is twice what the script asked for, so expect can avoid
  6568. dealing with matching output that straddles two buffers.
  6569. The next step is to massage the patterns and actions into a more usable form.
  6570. The code in Listing 7 does just this by picking up the original arguments and
  6571. placing them into a structure declared in Listing 6.
  6572. At this point, expect can begin looking for patterns. The code in Listing 8
  6573. begins by zeroing buf, the buffer that accumulates the incoming bytes. Then
  6574. the code loops. Basically, the loop consists of the following steps:
  6575. 1) read as much new data as is available, blocking if none.
  6576. 2) break out of loop if eof, timeout or any patterns match.
  6577. Naturally, the implementation is a little more complicated than that. In the
  6578. loop, first make sure there is space in the buffer for incoming data. (rc is
  6579. the number of characters in the buffer.) To guarantee that matches can occur
  6580. on data of a minimum length (match_max), copy the second half over the first
  6581. if the buffer is full. This is why the original request was doubled earlier.
  6582. Next the function i_read is called. The arguments to i_read are the stream to
  6583. read, where in the buffer to put new bytes, how many to read, and how long to
  6584. wait for them. Internally, i_read starts an alarm and then begins reading. If
  6585. the read completes, the alarm is cancelled and i_read returns the numbers of
  6586. characters read. Otherwise, the alarm interrupts the read, and i_read returns
  6587. -2 to denote that. (-1 is returned for other errors, and 0 is returned for
  6588. EOF.) Hence i_read is an "interruptible read".
  6589. I don't have the space in this column to show the implementation of i_read. If
  6590. you are interested, I devoted a whole column to it in the Micro/Systems
  6591. Journal (September 1986).
  6592. The result of i_read determines whether an EOF or timeout occurred or whether
  6593. some other abnormal event happened. In all of these cases, the loop is exited.
  6594. If any characters were read, the buffer end is updated. The new characters are
  6595. echoed to stdout, if desired. (The script has control over this via the C
  6596. variable loguser, which is mapped to a Tcl function.)
  6597. At this point, all that is left is to check if the output matches the
  6598. patterns. A slight problem presented by Tcl is that it uses the C convention
  6599. of null-terminating strings. Thus, any nulls must be removed by calling
  6600. rm_nulls (which returns the numbers of nulls removed). rm_nulls is not shown
  6601. here, but it is trivial to write. Notice that this step is done after the
  6602. write to stdout, because it is likely that the nulls are involved in screen
  6603. formatting operations.
  6604. The final operation compares the patterns against the input, by calling the
  6605. patternmatch function, which understands the "usual" wildcards and C escapes.
  6606. Like i_read, the implementation of patternmatch is not relevant here. If a
  6607. pattern matches, the loop is aborted. Notice that goto is used to break out of
  6608. two for loops. This is one of the few acceptable reasons to use goto in a C
  6609. program, since without it, the code would be harder to read.
  6610. Lastly, notice that the keyword cases are skipped when looking for keywords.
  6611. See Listing 8.
  6612. If the loop terminates, expect has either timed out, seen a pattern or EOF, or
  6613. encountered some error. In the last case, the function simply cleans up, and
  6614. returns an error indication to the caller. Otherwise, expect selects the
  6615. appropriate action, passing it to Tcl_Eval for execution, and returning the
  6616. result of Tcl_Eval to your caller. (This allows Tcl to automatically handle
  6617. statements like "break" in Listing 1.) The input buffer is also made available
  6618. in the Tcl variable expect_match. Using this, the script can find out what
  6619. matched a pattern (or what failed to match after a timeout).
  6620.  
  6621. Lastly, the pattern-action structure is freed, and the result is returned. See
  6622. Listing 9.
  6623.  
  6624.  
  6625. Some Less Important Notes
  6626.  
  6627.  
  6628. I have simplified the code, primarily for readability. For instance, the
  6629. quoted strings in the original are defined by C preprocessor definitions.
  6630. Patterns can actually be lists of terns, permitting multiple patterns to
  6631. trigger the same action. This complicates the pair structure and everything
  6632. that touches it, without adding any interesting programming, so I've omitted
  6633. it here. I also removed some logging code. The logging code can record the
  6634. interaction, which is primarily useful in debugging. expect can also be turned
  6635. back on a real user, in which case it shouldn't echo the user's input since he
  6636. is already seeing it (having just typed it). This makes the logging code even
  6637. more complex.
  6638. Note also that some initialization must occur before cmd-Expect is ever
  6639. executed. For instance, we guarantee that the Tcl variables timeout and
  6640. spawn_id have values by setting them initially ourselves.
  6641. Please forgive me for omitting most of the declarations. They should be
  6642. obvious. One that I must comment on is the declaration for stream. It is a
  6643. FILE *. It is interesting because the script can change it by accessing the
  6644. Tcl variable spawn_id. Nothing else can be done with it. To the script, it
  6645. serves only as a magic cookie that can be used to select which process to
  6646. interact with. In the script's world, it appears as a rather odd string, but
  6647. inside expect, it becomes a perfectly usable stream. (Actually, it is a magic
  6648. cookie as far as I'm concerned too, albeit a different kind. We can pass it to
  6649. a limited set of functions, but nothing else.)
  6650.  
  6651.  
  6652. Conclusion
  6653.  
  6654.  
  6655. I hope you've enjoyed exploring this interesting function of a very real
  6656. program. This particular function draws together a large number of techniques
  6657. to accomplish a coherent result. Whether you decide to incorporate a function
  6658. like this one in your code, or just use some of the techniques (such as Tcl),
  6659. you have seen one way of going about it. Being production code, this has also
  6660. shown you all the details that must be taken into account.
  6661. Expect is in the public domain. To date, more than 1,000 people have requested
  6662. and received copies of expect, and there are no known bugs. If you would like
  6663. your own copy of expect, you can ftp it as pub/expect.shar.Z from
  6664. durer.cme.nist.gov. You can request e-mail copies by mailing to
  6665. library@cme.nist.gov. The contents of the message should be (no subject line)
  6666. send pub/expect.shar.Z. Two conference papers on expect exist as
  6667. pub/expect.ps.Z and pub/expect-sysadm.ps.Z.
  6668. Expect requries a multitasking operating system. Because expect is a process
  6669. that controls other processes, it must be able to run multiple processes
  6670. simultaneously. While expect is used primarily on UNIX systems, I would be
  6671. willing to help you complete ports to other systems.
  6672.  
  6673. Listing 1
  6674. for {} 1 {} {
  6675. spawn rogue
  6676. expect "*Str: 18*" break \
  6677. "*Str: 16*" {}
  6678. close
  6679. }
  6680. interact
  6681.  
  6682.  
  6683. Listing 2
  6684. /*ARGSUSED*/
  6685. int
  6686. cmdExpect(clientData, interp,
  6687. argc, argv)
  6688. ClientData clientData;
  6689. Tcl_Interp *interp;
  6690. int argc;
  6691. char **argv;
  6692. {
  6693.  
  6694.  
  6695. Listing 3
  6696. if (argc < 2) {
  6697. tcl_error("usage: expect
  6698. [pattern action] ...
  6699. pattern [action]");
  6700. return(TCL_ERROR);
  6701. }
  6702.  
  6703.  
  6704. Listing 4
  6705. stream = atoi(Tc1_GetVar(interp,
  6706. "spawn_id",0));
  6707. timeout = atoi(Tcl_GetVar(interp,
  6708. "timeout",0));
  6709.  
  6710.  
  6711. Listing 5
  6712. s = Tcl_GetVar(interp,"match_max",0);
  6713. if (buf_size != (new_size = 2*atoi(s))) {
  6714.  
  6715. if (0 == (new_buf = realloc(buf,new_size+1))) {
  6716. tcl_error("failed to grow match buf to %d bytes",
  6717. new_size);
  6718. return(TCL_ERROR);
  6719. }
  6720. buf_size = new_size;
  6721. buf = new_buf;
  6722. }
  6723.  
  6724.  
  6725. Listing 6
  6726. typedef struct {
  6727. char *pattern;
  6728. char *action;
  6729. enum {keyword, pattern} type;
  6730. } pair;
  6731.  
  6732.  
  6733. Listing 7
  6734. pairs_inuse = argc/2; /* number of patterns */
  6735. if (0 == (pairs = (pair *)malloc(pairs_inuse * sizeof(pair)))) {
  6736. tcl_error("malloc(%d pairs)",pairs_inuse);
  6737. return(TCL_ERROR);
  6738. }
  6739.  
  6740. timeout_action = eof_action = 0;
  6741. for (i = 1, p = pairs;i<argc;i+=2,p++) {
  6742. if (!(strcmp(argv[i],"timeout"))) {
  6743. p->type = keyword;
  6744. timeout_action = argv[i+1];
  6745. } else if (!(strcmp(argv[i],"eof"))) {
  6746. p->type = keyword;
  6747. eof_action = argv[i+1];
  6748. } else {
  6749. p->type = pattern;
  6750. p->pattern = argv[i];
  6751. p->action = argv[i+1];
  6752. }
  6753. }
  6754.  
  6755.  
  6756. Listing 8
  6757. buf[0] = '\0';
  6758.  
  6759. for (;;) {
  6760. if (rc == buf_size) {
  6761. memcpy(buf,buf+buf_size/2,buf_size/2);
  6762. rc = buf_size/2;
  6763. }
  6764.  
  6765. cc = i_read(stream,buf+rc,buf_size-rc,timeout);
  6766.  
  6767. if (cc == 0) { /* normal EOF */
  6768. eof = TRUE;
  6769. fclose(stream);
  6770. break;
  6771. } else if (cc == -1) { /* abnormal EOF */
  6772. if (i_read_errno == EBADF) {
  6773. tcl_error("bad spawn_id (process died earlier?)");
  6774.  
  6775. } else {
  6776. tcl_error("i_read(spawn_id=%d): %s",
  6777. stream,sys_errlist[errno]);
  6778. fclose(stream);
  6779. }
  6780. error = TRUE;
  6781. break;
  6782. } else if (cc == -2) break; /* timed out */
  6783.  
  6784. oldrc = rc;
  6785. rc += cc;
  6786.  
  6787. if (loguser) {
  6788. fwrite(buf+oldrc,1,cc,stdout);
  6789. fflush(stdout);
  6790. }
  6791.  
  6792. rc -= rm_nulls(&buf[oldrc],cc);
  6793. buf[rc] = '\0';
  6794.  
  6795. for (p=pairs;p<&pairs[pairs_inuse];p++) {
  6796. if (p->type == keyword) continue;
  6797. if (patternmatch(buf,p->pattern)) {
  6798. match = TRUE;
  6799. goto done;
  6800. }
  6801. }
  6802. }
  6803.  
  6804.  
  6805. Listing 9
  6806. done:
  6807. if (error) result = TCL_ERROR;
  6808. else {
  6809. char *action;
  6810.  
  6811. if (match) action = p->action;
  6812. else if (eof) action = eof_action;
  6813. else action = timeout_action;
  6814.  
  6815. if (action) result = Tcl_Eval(interp,action,0,(char **) NULL);
  6816. else result = TCL_OK;
  6817.  
  6818. Tcl_SetVar(interp,"expect_match",buf,0);
  6819. }
  6820.  
  6821. free((char *)pairs);
  6822.  
  6823. return(result);
  6824. }
  6825.  
  6826.  
  6827.  
  6828.  
  6829.  
  6830.  
  6831.  
  6832.  
  6833.  
  6834.  
  6835.  
  6836.  
  6837.  
  6838.  
  6839.  
  6840.  
  6841.  
  6842.  
  6843.  
  6844.  
  6845.  
  6846.  
  6847.  
  6848.  
  6849.  
  6850.  
  6851.  
  6852.  
  6853.  
  6854.  
  6855.  
  6856.  
  6857.  
  6858.  
  6859.  
  6860.  
  6861.  
  6862.  
  6863.  
  6864.  
  6865.  
  6866.  
  6867.  
  6868.  
  6869.  
  6870.  
  6871.  
  6872.  
  6873.  
  6874.  
  6875.  
  6876.  
  6877.  
  6878.  
  6879.  
  6880.  
  6881.  
  6882.  
  6883.  
  6884.  
  6885.  
  6886.  
  6887.  
  6888.  
  6889.  
  6890.  
  6891.  
  6892.  
  6893.  
  6894.  
  6895.  
  6896.  
  6897.  
  6898. Complex Arithmetic And Matrices In C + +
  6899.  
  6900.  
  6901. Louis Baker
  6902.  
  6903.  
  6904. Louis Baker has a Ph.D. in astronomy and has written books with code in C and
  6905. Ada for McGraw-Hill. He may be reached at Mission Research Corp., 1720
  6906. Randolph SE, Albuquerque NM 87106.
  6907.  
  6908.  
  6909. In the May issue, I presented header files designed to simplify the
  6910. programming of complex arithmetic and matrices in C. In this article, I will
  6911. demonstrate that using C++ could further simplify such programming.
  6912. Developing these programs was my first foray into C++. By mentioning some of
  6913. my misconceptions and stumbling blocks, I hope to ease the way of other C
  6914. programmers taking up C++. After developing these programs with Zortech C++, I
  6915. got my hands on Turbo C++. I will compare the two and comment on porting
  6916. between C++ compilers.
  6917.  
  6918.  
  6919. Complex Arithmetic
  6920.  
  6921.  
  6922. In my previous article (May 1990), I gave an example of a program to compute a
  6923. type of Bessel functions, called Kelvin functions, using complex arithmetic.
  6924. (If you aren't familiar with complex numbers, see the previous article.) Here,
  6925. you will use a header file, COMPLEX.HPP (Listing 1), to facilitate complex
  6926. arithmetic, which employs operator overloading. This header enables you to add
  6927. two complex numbers, a and b, to produce c, with code of the form c=a+b rather
  6928. than CADD(c,a,b) as was required with COMPLEX.H. The code is straightforward
  6929. and needs no discussion. The simplest routines are inline for efficiency.
  6930. I will illustrate using the code by calculating the Hurwitz Zeta function
  6931. z(s,a), which is used in various applications, including summing series and
  6932. number theory. It is defined as
  6933. Click Here for Equation
  6934. The Riemann zeta function is z(s,1). Spanier and Oldham give an efficient
  6935. formula for evaluating this function, which contains two summations and one
  6936. additional term. The first summation is finite, with specified upper limit,
  6937. and the second is infinite, with the terms depending on the upper limit
  6938. specified for the first sum. They then set the first sum equal to zero, and
  6939. use a rational approximation method to obtain the second sum. There is an
  6940. associated cost: for n terms, order n2 operations are required vs. order n for
  6941. summing the series. The added operations include division. The rational
  6942. approximation generally converges more rapidly than summing the series, except
  6943. for large s. I have made two changes to their method. First, I have
  6944. re-introduced the first summation, which improves behavior for large s.
  6945. Second, while they computed the second sum for a fixed number of terms, I
  6946. check the convergence of that sum and terminate when a given tolerance is
  6947. achieved. I limit the sum to a maximum of 80 terms to prevent problems with
  6948. overflow. The program, along with support routines for computing and printing
  6949. the logarithm and exponential of a complex number, as its absolute value
  6950. (magnitude) is given in Listing 2. Note the need to delete the storage
  6951. allocated to the h array.
  6952.  
  6953.  
  6954. Vectors And Matrices
  6955.  
  6956.  
  6957. Vectors and matrices of complex numbers can take advantage of C++ features,
  6958. notably operator overloading, to ease the programmer's burden. While a vector
  6959. can be implemented as a matrix with a single row or column, it is more
  6960. efficient to treat it as a distinct class. Listing 3, CVECTOR.HPP, defines the
  6961. vector class and Listing 4, CMATRIX.HPP, the matrix class, with the functions
  6962. and test program main() in Listing 5, CMATRIX.CPP. You can find a number of
  6963. matrix class implementations in Using C++ by Bruce Eckel, and in J. F.
  6964. Dreitlein and J. R. Sauer's article, "Spinor Software Tools in C++" in
  6965. Computers in Physics magazine (see Reference at the end of this article). I
  6966. was initially tempted to implement the matrix as a vector of row vectors of
  6967. class Cvector, but I decided to follow the usual practice (as in the two cited
  6968. references) of using a single variable of type complex **head. With this
  6969. practice, the natural notation head[i][j] represents the appropriate matrix
  6970. element.
  6971. The classes Cvector and Cmatrix carry along an integer variable named base.
  6972. The default value, 0, gives the usual C convention of vectors with a first
  6973. element of index 0. Set base=1 for the FORTRAN convention of first element
  6974. having the index 1. The latter choice might be useful in converting FORTRAN
  6975. code or a mathematical formula with the usual notation of row and column
  6976. indices beginning at 1.
  6977. Eckel implements matrices by doing his own memory management. Each matrix has
  6978. a reference count, and the destructor deletes the storage required only if
  6979. this count is one. He also copies matrices by assigning the pointer and
  6980. increasing the reference count. This method is economical in time, and
  6981. probably storage, but it is dangerous. Altering one reference to the matrix
  6982. will alter all other copies. The reference count method of storage allocation
  6983. can sometimes fail to free storage when it is possible to do so, for example
  6984. if there is a circular chain of references.
  6985. A safer method is to define the copy operators Cmatrix(Cmatrix&) and
  6986. Cvector(Cvector&) to allocate new memory and copy all the data. This costs
  6987. time and perhaps space, but avoids the two problems mentioned for the other
  6988. scheme. Obviously, there are time-critical applications in which the
  6989. pointer-copying approach could be desirable. Depending on the application, the
  6990. extra memory occupied by multiple copies may or may not outweigh the space
  6991. occupied by the reference count in each object, and any unrecovered memory due
  6992. to circular chains.
  6993. The package CMATRIX.HPP implements the most basic operations, including taking
  6994. the dot product of two vectors, and multiplying two matrices or a vector by a
  6995. matrix. Eckel's package contains a large number of routines, includes
  6996. factoring matrices into their LU decomposition, performing statistical
  6997. operations, and interchanging rows or columns. Interested readers might start
  6998. by converting the Basic Linear Algebra Subroutines (BLAS) package to C++, and
  6999. using its functions to implement more complicated matrix operations.
  7000.  
  7001.  
  7002. Inheritance
  7003.  
  7004.  
  7005. I had originally intended to use inheritance, but discovered it wasn't very
  7006. useful for the problems at hand. For example, I couldn't use complex as a base
  7007. class from which complex vectors were derived. I could have defined vectors
  7008. based at 0 as the base class, and define a class with arbitary bases that
  7009. inherits from that base class and adds the integer variable base. This might
  7010. save some storage and arithmetic if the user wants to use indices based at 0,
  7011. but it seems more sensible to "do it right" from the start.
  7012. I found the data hiding features of C++ unhelpful at best. For example, I had
  7013. originally placed the function error() as a function in Cvector. It was then
  7014. inaccessable to functions in the Cmatrix class, unless I declared that class a
  7015. friend. I would have to modify CVECTOR.HPP whenever I decided there was
  7016. another use for the function as constituted.
  7017. C++ is best suited to bottom-up programming. Bottom-up programming entails
  7018. building tools, such as reusable routines that are often based on less
  7019. sophisticated existing code. Inheritance is ideal for this. On the other hand,
  7020. there seems to me to be little value to inheritance for the first shot at
  7021. code, since you can build in the structure you need. Inheritance is useful
  7022. only when you want to reuse a class that isn't quite up to snuff.
  7023.  
  7024.  
  7025. Comments On Zortech And Turbo C++
  7026.  
  7027.  
  7028. The Zortech compiler comes with two programs to do the second pass. If the
  7029. first version runs out of memory doing the compilation, you try the second. If
  7030. that one runs out of memory, you are out of luck. It's also possible to run
  7031. out of memory on the first pass. These features enforce code modularity,
  7032. because the first version did not tolerate even moderately sized programs.
  7033. Zortech and other C++ compilers use name "mangling,". This means that the
  7034. compiler sees a function like this:
  7035. FILE *OpenFile(const char *FileName,
  7036. const char *IoMode=0);
  7037. the symbol name that it actually places in the object code might be something
  7038. like OpenFile___NpCcT1. The funny characters at the end are an encoding of the
  7039. fact that this function takes two arguments which are both near pointers to
  7040. constant characters.
  7041. The purpose of this is encoding is to allow the linker to detect some kinds of
  7042. type errors. For example, if I referenced the function shown in the previous
  7043. paragraph, but defined it incorrectly as
  7044. FILE *OpenFile(const char
  7045. *FileName);
  7046. then, because functions that accept different types of arguments are given
  7047. different names, the linker would object that the function that was referenced
  7048. had not been defined. Unfortunately, the Zortech linker displays the mangled
  7049. names in its error messages (there is an extra utility that unmangles them as
  7050. an extra step). The Turbo linker automatically unmangles the names in its
  7051. error messages, so you don't have to think about it.
  7052. Turbo C++ has a much snazzier appearence than the previous release of Turbo C.
  7053. This comes at some cost. Before, the F3 function key instantly gave you a
  7054. window for a file to load. Now, you have to wait a few seconds, depending upon
  7055. the speed of your host machine and the clutter of your disk, while it develops
  7056. a directory of your files. You'll have to throw away your old project files
  7057. (*.PRJ) as the format has changed. In fact, so much has changed you'll find
  7058. yourself re-learning how to use the program. (For example, if you are
  7059. mouseless and want to search backwards, you now need to use the tab key to get
  7060. to the option for backward searches, as the return will now begin the search.)
  7061. But don't throw away your old C code or object modules. These may still be
  7062. linked to, either with Zortech's cdecl keyword declaration or with Turbo C's
  7063. extern "C" declaration.
  7064. Both programs generated .EXE files of similar size and speed. Be prepared for
  7065. the additional language complexity to slow compilations compared to straight C
  7066. compilers. That does not imply a runtime overhead, however. In fact, Zortech
  7067. can generate more efficient function call/return code for C++ functions
  7068. because the required function prototype guarantees the number and type of
  7069. function parameters.
  7070. Turbo C++ was pickier than the Zortech, catching a missing void declaration
  7071. for a function which returned nothing. There were also minor differences in
  7072. the languages accepted by the two compilers. Turbo C++ attempts to implement
  7073. the language strictily as defined by AT&T's release 2.0, whereas Zortech has
  7074. some features of AT&T release 2.1.
  7075. For example, the 2.0 definition of C++ did not allow you to define an array of
  7076. class objects unless that class contained a constructor that took no
  7077. arguments. That made sense because the constructor would be getting called
  7078. implicitly in a loop by the compiler to initialize each object in the array --
  7079. there is no way for you to specify arguments to the constructor in that
  7080. situation.
  7081. However, the 2.1 definition of C++ loosened that restriction a little by also
  7082. allowing constructors that have only arguments with default values specified.
  7083. That allows a little more flexibility for the programmer with only a small
  7084. increase in complexity for the compiler. The net result was a constructor
  7085.  
  7086. complex ( double reali=0.,
  7087. double imagi=0.)
  7088. that allowed statements like
  7089. complex *head; head =
  7090. new complex[10];
  7091. with Zortech, but Turbo complained that complex::complex() was not defined.
  7092. Because of the speed at which C++ is evolving, minor incompatibilities like
  7093. this are a fact of life for C+ + programmers.
  7094. References
  7095. J. F. Dreitlein & J. R. Sauer, "Spinor Software Tools in C++", Computers in
  7096. Physics, Jan./Feb. 1990, p.64.
  7097. Bruce Eckel, Using C++, N.Y.: Osborne/McGraw-Hill,1989.
  7098. C. L. Lawson, R. J. Hanson, D. R. Kincaid, F. T. Krough, "Algorithm 539: Basic
  7099. Linear Algebra Subprograms for Fortran Use", Collected Algorithms from ACM
  7100. (CALGO), also "ACM Transactions on Mathematical Software", 5,p.324,1979.
  7101. J. Spanier and K. B. Oldham, Handbook of Functions, N.Y.: Hemisphere, 1987.
  7102. Vendors
  7103. Zortech C++
  7104. Zortech Inc.
  7105. 1165 Massachusettes Ave.
  7106. Arlington, MA 02174
  7107. (617) 646-6703
  7108. FAX (617) 643-7969
  7109. (800) 848-8408
  7110. UK (44) 81-316-7777
  7111. Turbo C++ v2.0
  7112. Borland Int'l
  7113. P.O. Box 660001
  7114. Scotts Valley, CA 95067-0001
  7115. (800) 331-0877
  7116. outside USA 408-438-5300
  7117.  
  7118. Listing 1
  7119. // complex.hpp
  7120. #include <math.h>
  7121. #include <stdio.h>
  7122.  
  7123. class complex {
  7124. protected:
  7125. double x,y;
  7126. public:
  7127. // complex( double xx = 0., double yy = 0.) //create
  7128. // { x=xx;y=yy;}
  7129. complex( double xx , double yy = 0.) //create
  7130. { x=xx;y=yy;}
  7131. complex() //create
  7132. { x=0.;y=0.;}
  7133. inline void operator=(complex rvalue)
  7134. {x=rvalue.x;y=rvalue.y;}
  7135. inline void operator-=(complex rvalue)
  7136. {x-=rvalue.x;y-=rvalue.y;}
  7137. inline void operator+=(complex rvalue)
  7138. {x+=rvalue.x;y+=rvalue.y;}
  7139. inline void operator*=(complex rvalue)
  7140. {
  7141. *this=complex(rvalue.x*x-rvalue.y*y,
  7142. rvalue.x*y+rvalue.y*x);
  7143. //return *this;
  7144. }
  7145. inline void operator*=(double rvalue)
  7146. {
  7147. *this=complex(rvalue*x, rvalue*y);//return *this;
  7148. }
  7149. inline complex operator+(complex rvalue)
  7150.  
  7151. {return complex(x+rvalue.x,y+rvalue.y);}
  7152. inline complex operator-(complex rvalue)
  7153. {return complex(x-rvalue.x,y-rvalue.y);}
  7154. inline complex operator-() //unary minus
  7155. {return complex(-x,-y);}
  7156. inline complex operator*(complex rvalue)
  7157. {return complex(
  7158. rvalue.x*x-rvalue.y*y,
  7159. rvalue.x*y+rvalue.y*x);}
  7160. inline friend complex operator/(double dividend,complex divisor)
  7161. { double temp;
  7162. temp=1./(divisor.x*divisor.x+divisor.y*divisor.y);
  7163. return complex((dividend*divisor.x)*temp,
  7164. (-dividend*divisor.y)*temp);
  7165. }
  7166. inline complex operator/(complex divisor)
  7167. {
  7168. double temp;
  7169. temp=1./(divisor.x*divisor.x+divisor.y*divisor.y);
  7170. return complex((divisor.x*x+divisor.y*y)*temp,
  7171. (divisor.x*y-divisor.y*x)*temp);
  7172. }
  7173. inline int operator==(complex rvalue)
  7174. {return (x==rvalue.x && y==rvalue.y);}
  7175. inline double real() {return x;}
  7176. inline double imaginary() {return y;}
  7177. inline complex conjugate()
  7178. {return complex(x,-y);}
  7179. inline friend complex operator*(complex num,double real)
  7180. {return complex(num.x*real,num.y*real);}
  7181. inline friend complex operator*(double real,complex num)
  7182. {return complex(num.x*real,num.y*real);}
  7183. inline friend complex operator+(complex num, double real)
  7184. {return complex(num.x+real,num.y);}
  7185. inline friend complex operator+(double real,complex num)
  7186. {return complex(num.x+real,num.y);}
  7187. inline complex operator+=(double real)
  7188. {return complex(x+=real,y);}
  7189. inline complex operator==(double real)
  7190. {
  7191. return complex(x-=real,y);}
  7192. inline complex operator++()
  7193. {x+=1.;return *this;}
  7194. inline friend complex operator/(complex num, double real)
  7195. {return complex(num.x/real,num.y/real);}
  7196. inline friend complex operator-(complex num,double real)
  7197. {return complex(num.x-real,num.y);}
  7198. inline friend complex operator-(double real,complex num)
  7199. {return complex(real-num.x,-num.y);}
  7200. double abs();
  7201. complex cexp();
  7202. complex clog();
  7203. complex operator^(double expon);
  7204. complex operator^(complex expon);
  7205. friend complex operator^(double base, complex expon);
  7206. void print( char *ahead="",char *behind="");
  7207. complex hurwitz(complex );
  7208. };
  7209.  
  7210.  
  7211.  
  7212. Listing 2
  7213. //#include <stream.hpp> //for Zortech C++
  7214. #include <iostream.h> //for TURBO C++
  7215. #include <math.h>
  7216. #include "complex.hpp"
  7217. #define errorcode -1.e60
  7218. #define ABS(x) ((x)>0.?(x):-(x))
  7219. #define max(a,b) ((a)>(b)?(a):(b))
  7220. #define pi 3.141592653589793238462643383279
  7221.  
  7222. void complex::print( char *ahead,char *behind)
  7223. {char *between="";
  7224. if(y>=0.)between="+";
  7225. printf("%s %e%s%e i %s",ahead,x,between,y,behind);}
  7226.  
  7227. double complex::abs()
  7228. { return sqrt(x*x+y*y);}
  7229.  
  7230. complex complex::cexp()
  7231. {double scale;
  7232. scale= exp(((double)x));
  7233. return complex(scale*cos(y),scale*sin(y));
  7234. }
  7235. complex complex::clog()
  7236. {double mant,arg,mag;
  7237. mant = log((*this).abs());
  7238. arg= atan2(y,x);
  7239. return complex(mant,arg);
  7240. }
  7241.  
  7242. complex complex::operator^(double expon)
  7243. {
  7244. complex z;
  7245. z= (*this).clog() *expon;
  7246. return z.cexp();
  7247. }
  7248.  
  7249. complex complex::operator^(complex expon)
  7250. {
  7251. complex z;
  7252. z= (*this).clog() *expon;
  7253. return z.cexp();
  7254. }
  7255.  
  7256. double Rzeta2( int k)
  7257. // Riemann Zeta function of 2*(k+1) returned
  7258. {double z[6]={1.64493406684822643647,1.08232323371113819152,
  7259. 1.01734306198444913971, 1.00407735619794433938,
  7260. 1.00099457512781808534,1.00024608655330804830};
  7261. if(k<6)return z[k];
  7262. // return 1+ 2^(-2k)+...+ 6^(-2k) in simplified form;
  7263. if(k>=30)return 1.;
  7264. double fk=-((k+1)<<1); double p2=pow(2.,fk);
  7265. return 1.+((p2+pow(3.,fk))*(1.+p2)+pow(5.,fk));
  7266. }
  7267.  
  7268. void prep( complex& u, complex& v, complex& f)
  7269. {
  7270.  
  7271. f=0.;
  7272. do {
  7273. f+=u^(-v);u++;
  7274. if(v.real()<1.)
  7275. { while(u.real()>2.){u-=1.;f-=u^(-v);}}
  7276. }while(u.real()<=v.real());
  7277. }
  7278.  
  7279. #define tolabs 1.e-10
  7280. #define tolrel 1.e-5
  7281.  
  7282. int size=80,szused,jused;
  7283.  
  7284. complex complex::hurwitz(complex u)
  7285. {
  7286.  
  7287. complex b,c,f,t,p,q,*h,hold,tpu,ev;int i,j,k; double diff,tk,z,scale;
  7288. if( *this==1. ((u-*this+(*this).abs())==0.&&
  7289. (*this).imaginary()==0.))
  7290. return complex(errorcode,0.);
  7291. prep(u,*this,f);
  7292. //u.print("modified a=","");f.print(" modifed sum=","\n");
  7293. ev= -(*this);
  7294. j=4;scale=.25;
  7295. jused=j=max(j, (int)(((*this).abs()+.999)*scale));
  7296. if(x<0.)jused=j=0;
  7297. b=u+ ((double)j); c=u;
  7298. for(i=0;i<j;i++){f+= c^ev;c+=1.;}
  7299. //f.print(" modifed sum=","\n");
  7300. if( size%2)size++;//size must be even integer
  7301. h=new complex[size+1] ;
  7302. t=complex(-2.,0.);
  7303. h[0]=1.;k=0;
  7304. tpu=2.*pi*b;
  7305. tpu=1./(tpu*tpu);
  7306. while(k<size){
  7307. tk=k<<1;
  7308. t*=(*this+tk)*(1.-*this-tk)*tpu;
  7309. z=Rzeta2(k);
  7310. k++;
  7311. h[k]=h[k-1]+t*z;
  7312. q=0.;j=k;
  7313. do {
  7314. p=h[j-1];
  7315. if(h[j]==p)
  7316. {
  7317. h[j-1]=-errorcode;
  7318. if(p==-errorcode)h[j-1]=q;
  7319. }
  7320. else h[j-1]= q+1./(h[j]-p);
  7321. q=p;j--;
  7322. }while(j);
  7323. if(!(k%2))
  7324. {
  7325. diff=(hold-h[0]).abs();
  7326. if(diff<tolabs diff<tolrel * h[0].abs())break;
  7327. hold=h[0];
  7328. }
  7329. };
  7330.  
  7331. szused=k;
  7332. ev= -(*this);
  7333. tpu= 1.+ev;
  7334. q=-h[0]*(b^tpu)/tpu;
  7335. delete h;
  7336. return f+.5*(b^ev)+q;
  7337. }
  7338.  
  7339. main()
  7340. {complex u,v,w;double a,b,c,d;
  7341.  
  7342. while(1)
  7343. {
  7344. cout <<" enter v,u" ;
  7345. //scanf("%le%le%le%le",&a,&b,&c,&d);
  7346. cin >> a >> b >> c >> d ;
  7347. if(a==0.&&b==0.&&c==0.&&d==0.)break;
  7348. u=complex(c,d);v=complex(a,b);
  7349. w=v.hurwitz(u);
  7350. w.print(" Hurwitz=","\n");
  7351. cout << " max k used=" << szused << ", J used: " << jused << "\n" ;
  7352. };
  7353. }
  7354.  
  7355.  
  7356. Listing 3
  7357. // Cvector.hpp
  7358. #include <stdlib.h>
  7359.  
  7360. #include "complex.hpp"
  7361. static complex *present;
  7362.  
  7363. class Cvector {
  7364. protected:
  7365. public:
  7366. int size,base;
  7367. complex *head;
  7368. Cvector(int s=1,int b=0,complex initvalue= complex(0.,0.))
  7369. //constructor
  7370. { head= new complex[s];base=b;
  7371. for(size=0,present=head;size<s;
  7372. size++,present++) *present = initvalue;
  7373. size=s;
  7374. printf(" Cvector built\n");
  7375. }
  7376. Cvector( Cvector&); //copy
  7377. ~Cvector() //destructor
  7378. {
  7379. delete head; printf(" Cvector axed\n");}
  7380. void operator=( Cvector& rhs);
  7381. complex operator*(Cvector& rvalue);//dot product
  7382. inline int length()
  7383. {return size;}
  7384. inline complex& elemnt( int i)
  7385. {return head[i-base];}
  7386. inline void setelemnt( int i, complex& value)
  7387. {head[i-base]=value;}
  7388. void check(int);
  7389. complex& element( int i);
  7390.  
  7391. void setelement(int i, complex& value);
  7392. };
  7393.  
  7394.  
  7395. Listing 4
  7396. //matrix package
  7397.  
  7398. #include "cvector.hpp"
  7399.  
  7400. class Cmatrix {
  7401.  
  7402. private:
  7403. void init(int row=1,int col=1,int b=0);
  7404. public:
  7405. complex** m;
  7406. int rowkt,colkt,base;
  7407.  
  7408. Cmatrix(int rowkt,int colkt,int b)
  7409. { init( rowkt, colkt, b);} //constructor
  7410. Cmatrix() {init();} // "
  7411. Cmatrix( Cmatrix&); // init
  7412. ~Cmatrix(); // destructor
  7413. void operator=(Cmatrix& );
  7414. inline complex& elemnt(int i,int j)
  7415. {return m[i-base] [j-base];}
  7416. void check(int,int);
  7417. complex& element(int,int);
  7418. inline void setelemnt(int i,int j,complex value)
  7419. {m[i][j]=value; }
  7420. void setelement(int,int, complex);
  7421. friend Cmatrix operator*(Cmatrix&,Cmatrix&); //Mat*Mat
  7422. friend Cvector operator*(Cmatrix&,Cvector&); //Mat*Vector
  7423. };
  7424.  
  7425.  
  7426. Listing 5
  7427. #include <stdlib.h>
  7428. #include "cmatrix.hpp"
  7429.  
  7430. error(char* msg, int index,int s)
  7431. {
  7432. printf("%s%d %d\n",msg,index,s);
  7433. exit(1);
  7434. }
  7435.  
  7436. void Cvector::check( int index)
  7437. {int loc;
  7438. loc=index-(*this).base;
  7439. if (loc<0)
  7440. error(" Cvector error: index-base<0",index,(*this).base);
  7441. if (loc>= (*this).size)
  7442. error(" Cvector error: index too large",index,(*this).size);
  7443. }
  7444.  
  7445. void Cmatrix::check( int i, int j)
  7446. {int loc;
  7447. loc=i-(*this).base;
  7448. if (loc<0)
  7449. error(" Cmatrix error: row index-base<0",i,(*this).base);
  7450.  
  7451. if (loc>= (*this).rowkt)
  7452. error(" Cmatrix error: row index too large",i,(*this).rowkt);
  7453.  
  7454. loc=j-(*this).base;
  7455. if (loc<0)
  7456. error(" Cmatrix error: column index-base<0",j,(*this).base);
  7457. if (loc>= (*this).colkt)
  7458. error(" Cmatrix error: column index too large",j,(*this).colkt);
  7459.  
  7460. }
  7461.  
  7462. void Cmatrix::init(int row, int col, int b)
  7463. {
  7464. if(row<1col<1)error(" matrix needs at least one row/col ",row,col);
  7465. rowkt=row;colkt=col;base=b;
  7466. m= new complex *[rowkt];
  7467. for(int j=0;j<rowkt;j++)m[j]= new complex[colkt];
  7468. complex zero;complex one(1.,0.);
  7469. //initialize to identity matrix.
  7470. for(int i=0;i<rowkt;i++){for(j=0;j<colkt;j++)m[i][j]=zero;
  7471. m[i][i]= one;// omit this line for init. to zero matrix
  7472. }
  7473. }
  7474.  
  7475. Cmatrix::Cmatrix(Cmatrix& a)
  7476. { init(a.rowkt,a.colkt,a.base);
  7477. for(int i=0;i<a.rowkt;i++)for(int j=0;j<a.colkt;j++)m[i][j]=a.m[i][j];
  7478. }
  7479.  
  7480. Cmatrix::~Cmatrix()
  7481. {for(int i=0;i<rowkt;i++)delete m[i];
  7482. delete m;
  7483. }
  7484.  
  7485. complex& Cvector::element(int i)
  7486. {
  7487. check(i);return head[i-base];
  7488. }
  7489.  
  7490. void Cvector::setelement( int i, complex& value)
  7491. {
  7492. check(i);
  7493. head[i]=value;
  7494. }
  7495. complex& Cmatrix::element(int i,int j)
  7496. {
  7497. check(i,j);return m[i-base][j-base];
  7498. }
  7499.  
  7500. void Cmatrix::setelement( int i, int j, complex value)
  7501.  
  7502. {
  7503. check(i,j);m[i][j]=value;
  7504. }
  7505.  
  7506. void Cmatrix::operator=(Cmatrix& a)
  7507. {
  7508. if(this==&a)return;
  7509. for(int i=0;i<rowkt;i++)delete m[i];
  7510.  
  7511. delete m;
  7512. init(a.rowkt,a.colkt,a.base);
  7513. for(i=0;i<a.rowkt;i++)for(int j=0;j<a.colkt;j++)m[i][j]=a.m[i][j];
  7514. }
  7515.  
  7516. Cvector::Cvector( Cvector& orig) //copy
  7517. {
  7518. size=orig.size;base=orig.base;head=new complex[orig.size];
  7519. for(int i=0;i<size;i++)head[i]=orig.head[i];
  7520. }
  7521.  
  7522. Cmatrix operator*(Cmatrix&a, Cmatrix& b)
  7523. {
  7524. int i,j,k;complex zero;
  7525. if(a.colkt!=b.rowkt)
  7526. error(" cannot multiply matrices colkt,rowkt ",a.colkt,b.rowkt);
  7527. Cmatrix prod(a.rowkt,b.colkt,a.base);
  7528. for(i=0;i<a.rowkt;i++)for(j=0;j<b.colkt;j++)
  7529. {prod.m[i][j] =zero;
  7530. for(k=0;k<a.colkt;k++)prod.m[i][j] += a.m[i][k]*b.m[k][j];
  7531. }
  7532. return prod;
  7533. }
  7534.  
  7535. Cvector operator*(Cmatrix& a, Cvector& b)
  7536. {
  7537. int k;complex zero;
  7538. if(a.colkt!=b.size)error(" cannot mult. matrix,vector ",a.colkt,b.size);
  7539. Cvector prod(b.size,b.base,zero);
  7540. for(int i=0;i<a.rowkt;i++)
  7541. {prod.head[i]=zero;
  7542. for(k=0;k<a.colkt;k++)prod.head[i] += (a.m[i][k])*(b.head[k]);
  7543. }
  7544. return prod;
  7545. }
  7546.  
  7547. complex Cvector::operator*(Cvector& rvalue) // dot product
  7548. {int i; complex *element,*relement; complex sum(0.,0.);// sum
  7549. if(rvalue.size!= size)
  7550. error(" dot product unequal length vectors ",size,rvalue.size);
  7551. for(i=0,element=head,relement=rvalue.head;i< size;
  7552. i++,element++,relement++)
  7553. sum += *element * *relement;
  7554. return sum;
  7555. }
  7556.  
  7557. void Cvector::operator=( Cvector& rhs)
  7558. {
  7559. if( this== &rhs)return;
  7560. if(size!=rhs.size)
  7561. {
  7562. delete head;
  7563. head= new complex[rhs.size];
  7564. }
  7565. for(int j=0;j<size;j++){head[j]=rhs.head[j];}
  7566. }
  7567.  
  7568. main()
  7569. {
  7570.  
  7571. complex a,b(1.,0.),c(0.,1.); double dot; int i,j;
  7572. printf(" size of complex=%d\n",sizeof(complex));
  7573. a=b+c;
  7574. a.print("summand=","\n");
  7575. printf(" magnitude=%e\n", a.abs());
  7576. b=a/c;
  7577.  
  7578. b.print("quotient=","\n");
  7579. a=b.cexp();
  7580. a.print(" exponential=","\n");
  7581. a= complex(1.,0.);b=complex(3.,2.);
  7582. printf(" size of Cvector=%d\n",sizeof(Cvector));
  7583. //
  7584. {Cvector A(4,0,a);Cvector B(4,0,b);Cvector D(4,0,b);
  7585. for(j=0;j<B.size;j++) B.element(j).print(" B=","\n");
  7586. a= A*B;
  7587. B=A;
  7588. for(j=0;j<B.size;j++) B.element(j).print(" B=","\n");
  7589. a.print(" dot product=","\n");
  7590. {Cmatrix m(4,4,0),n(4,4,0),o(4,4,0);
  7591. c=complex(0.,1.);
  7592. for (i=0;i<4;i++)
  7593. for(j=0;j<4;j++){
  7594. m.m[i] [j].print(" matrix element=","\n");}
  7595. for (i=0;i<4;i++)
  7596. for(j=0;j<4;j++){
  7597. n.setelement( i,j, complex((double)(i+j),0.));
  7598. n.m[i] [j].print(" matrix element=","\n");}
  7599. D = (m * A);
  7600. for(j=0;j<B.size;j++) B.element(j).print(" B=","\n");
  7601. B = (m * A);
  7602. for(j=0;j<B.size;j++) B.element(j).print(" B=","\n");
  7603. o= m * n;
  7604.  
  7605. for (i=0;i<4;i++)
  7606. for(j=0;j<4;j++){
  7607. o.m[i] [j].print(" matrix element=","\n");}
  7608. printf(" end of inner block\n");
  7609. }
  7610. printf(" end of block\n");
  7611. }
  7612. printf(" end of program\n");
  7613. }
  7614.  
  7615.  
  7616.  
  7617.  
  7618.  
  7619.  
  7620.  
  7621.  
  7622.  
  7623.  
  7624.  
  7625.  
  7626.  
  7627.  
  7628.  
  7629.  
  7630.  
  7631.  
  7632.  
  7633.  
  7634. Software Engineering in C
  7635.  
  7636.  
  7637. Robert E. Cady
  7638.  
  7639.  
  7640. Bob Cady holds bachelor's degrees in Computer Science, Electronics Technology,
  7641. and Business Management and has been programming for 13 years. He is president
  7642. of a software company developing communications programs for DOS and Windows.
  7643. He may be contacted at Tethys Software Co, 6651 E. Indian River Rd., Suite
  7644. 138, Va Beach, VA 23464-3441.
  7645.  
  7646.  
  7647. Despite the title, Software Engineering in C is not about software
  7648. engineering. The authors describe it in the preface as a textbook for
  7649. beginning and intermediate C programmers. It serves that purpose well and
  7650. emphasizes good programming style.
  7651. This fast-paced book is organized into 12 chapters and six appendices. Every
  7652. chapter concludes with exercises that test understanding of the material
  7653. covered. It does not assume any particular operating system, hardware, or
  7654. compiler.
  7655. Chapter one briefly introduces high-level programming languages and the
  7656. history of C. Chapter two explains the fundamentals of C programming: the
  7657. preprocessor, the #include and #define directives, and the
  7658. edit/compile/link/execute cycle. It also covers C functions in general and
  7659. main() and printf() in particular, reserved keywords, variable naming,
  7660. expressions, and assignments. Chapter three is an extensive discussion of
  7661. scalar data types, including pointers and typedef. Notable are the sections on
  7662. implicit and explicit conversions, enumeration types, and the mixing of data
  7663. types in expressions.
  7664. Chapters four through 11 cover the real meat of C programming. The authors use
  7665. flow charts and formal syntax charts. Unfortunately, they don't describe
  7666. either of these types of charts, so if you aren't already familiar with them,
  7667. they may be more confusing than helpful. Chapter four introduces control flow,
  7668. and discusses conditional branching, looping, the switch statement, and gotos.
  7669. It also includes a section on infinite loops. Chapter five, "Operators and
  7670. Expressions," covers precedence of operators, relational operators, and
  7671. logical operators, but only briefly explains bitwise operators.
  7672. Chapter six contains some of the best discussions of arrays and pointers I
  7673. have seen. It gives good examples of initializing arrays, passing them to
  7674. functions, and pointer operations on them. It also effectively covers pointer
  7675. arithmetic and using arrays of pointers. Chapter seven, one of the most
  7676. valuable chapters in this book, discusses storage classes. This chapter
  7677. clearly describes scope and duration of variables.
  7678. Chapter eight, "Structures and Unions," is the next most valuable chapter.
  7679. This chapter clearly explains how to set up and use unions and nested
  7680. structures. Chapter nine is a rigorous look at functions, pointers to
  7681. functions, recursion, and prototyping. However, the discussion on calling
  7682. functions using pointers is shallow. Because this book is not intended for
  7683. advanced users, I don't consider this a major deficiency.
  7684. Chapter 10 adequately discusses the preprocessor, with a good comparison of
  7685. macros and functions. Chapter 11 extensively discusses file input and output.
  7686. It explains buffered and unbuffered I/O, differentiating between sequential
  7687. and random access methods.
  7688. Chapter 12 is the only chapter that deals with software engineering, and it
  7689. barely scratches the surface of this important topic. It covers product
  7690. specification, software design, project management, software production tools,
  7691. debugging, testing, performance analysis, and documentation in only 30 pages.
  7692. By way of example, it includes the functional specification and project plan
  7693. for a C interpreter. The book includes source code for the interpreter in an
  7694. appendix.
  7695. Appendix A is a comprehensive overview of the ANSI C standard, including a
  7696. listing of all functions in the runtime library. Appendix B contains the
  7697. formal syntax charts for the C language. Appendix C is a list of numerical and
  7698. translation limits. Appendix D lists the differences between K&R and ANSI C.
  7699. Appendix E is a list of reserved names. Appendix F is a listing of the source
  7700. code for the C interpreter referred to in Chapter 12. The source code is also
  7701. available on diskette. Appendix G is the obligatory listing of ASCII character
  7702. codes (up to Ox7f). This book was copyrighted in 1988. There have been
  7703. numerous changes to the ANSI standard since then, so you can't rely on it as
  7704. your reference to the standard.
  7705. This book is written in a clear, friendly, and easy-to-read style. The
  7706. sections in each chapter contain enough examples to adequately reinforce the
  7707. subject matter. Each chapter contains sidebars containing either bug alerts or
  7708. notes on ANSI implementation. The bug alerts include the usual errors, such as
  7709. using an assignment operator (=) when a test for equality (==) is intended, or
  7710. vice versa. It also highlights more subtle errors. For example, the expression
  7711. if ((a < b) && (c == d++))
  7712. is discussed. It is certainly possible that a programmer would intend that d
  7713. be incremented only if a is less than b, but it seems more likely that d
  7714. should always be incremented. There are quite a few of these helpful alerts
  7715. throughout the book.
  7716.  
  7717.  
  7718. Summary
  7719.  
  7720.  
  7721. Although the authors intended this book for beginning and intermediate C
  7722. programmers, I don't think it is suitable for beginning programmers. It is a
  7723. very fast-paced book that implicitly expects the reader to have a good
  7724. understanding of some other high-level structured programming language, such
  7725. as Pascal or COBOL. As an advanced C programmer, I have found this book to be
  7726. most valuable as a reference. In fact, it has replaced five C books I used to
  7727. keep on the shelf above my programming desk. The replaced books include K&R
  7728. (first edition), Plum's Reliable Data Structures In C, and two bookds titled
  7729. Advanced C.
  7730. I like this book and use it a lot. I'm not programming 12 hours a day as I
  7731. used to, so I rely heavily on a good reference, especially when working under
  7732. a deadline at a client site. Software Engineering in C is that reference and I
  7733. highly recommend it. If you are going to have just a couple of books on C,
  7734. include this one on your list.
  7735. Software Engineering in C
  7736. Peter A. Darnell and Philip E. Margolis
  7737. Springer-Verlag
  7738. $32.00, 612 pages
  7739. ISBN 0-387-96574-2
  7740.  
  7741.  
  7742.  
  7743.  
  7744.  
  7745.  
  7746.  
  7747.  
  7748.  
  7749.  
  7750.  
  7751.  
  7752.  
  7753.  
  7754.  
  7755.  
  7756.  
  7757.  
  7758.  
  7759.  
  7760.  
  7761.  
  7762.  
  7763.  
  7764.  
  7765.  
  7766.  
  7767.  
  7768. Editor's Forum
  7769. Yes, Virginia, there really is a Standard C.
  7770. Tom Plum encouraged me to start using that term several years ago. He was
  7771. looking ahead to a day when both ANSI and ISO would complete their work on a C
  7772. standard. Like many of us, he was and remains fiercely dedicated to the notion
  7773. that the two standards should be identical. The last thing we wanted was an
  7774. endless debate on the relative merits, and the relative importance, of ANSI C
  7775. versus ISO C.
  7776. Just to get people in the habit, Tom argued, start using the term Standard C.
  7777. Call it ANSI C where you must, but go easy on any nationalistic sentiments.
  7778. Plan ahead.
  7779. That, of course, is why my monthly column in The C Users Journal has been
  7780. called "Standard C" from the outset, over two years ago. That is why Jim
  7781. Brodie and I called our comprehensive reference, Standard C, even though the
  7782. state of both ANSI and ISO standards were uncertain the day we froze copy.
  7783. That is why I cheer whenever I see others adopt the term as a matter of
  7784. course.
  7785. I am happy to pass on some good news from Dave Prosser. Dave is the Editor of
  7786. both standards. He has been wrestling for months, in his copious spare time,
  7787. with the formatting requirements imposed by ISO. (The two standards may be
  7788. identical semantically, but the separate organizations have in the past
  7789. required different layouts, and sometimes even different spellings of words.)
  7790. Dave called me the day before Thanksgiving to report that the final version of
  7791. the ISO C standard was on its way to Geneva.
  7792. Unless we on WG14 miss a beat, we should have an official ISO standard by the
  7793. end of 1990. Standard C has thus become a fitting term, one that rolls off the
  7794. tnogue more smoothly than ANSI/ISO C. Tom Plum, and quite a few others, are
  7795. getting their wish.
  7796. The battle is far from over. NIST, the government agency in charge of
  7797. specifying language standards for government procurement, has its oar in the
  7798. water. They have already proposed additional requirements on conforming
  7799. translators, typically in the area of error reporting. As far as I know, they
  7800. haven't changed the underlying language. At least not yet. And X3J16 is busy
  7801. standardizing C+ +. Many of us have a concern that this newer language not
  7802. deviate arbitrarily from its C heritage.
  7803. Extensions to C itself are now under active consideration within WG14. There
  7804. is a growing concensus that this is the proper venue for exploring proposed
  7805. changes to C, since most of the changes affect the international community.
  7806. C is not Latin. So long as it is alive and growing, it will change. At this
  7807. moment in time, however, it is refreshing to know that there really is a
  7808. Standard C.
  7809. P. J. Plauger
  7810. Editor
  7811.  
  7812.  
  7813.  
  7814.  
  7815.  
  7816.  
  7817.  
  7818.  
  7819.  
  7820.  
  7821.  
  7822.  
  7823.  
  7824.  
  7825.  
  7826.  
  7827.  
  7828.  
  7829.  
  7830.  
  7831.  
  7832.  
  7833.  
  7834.  
  7835.  
  7836.  
  7837.  
  7838.  
  7839.  
  7840.  
  7841.  
  7842.  
  7843.  
  7844.  
  7845.  
  7846.  
  7847.  
  7848.  
  7849.  
  7850.  
  7851.  
  7852.  
  7853.  
  7854.  
  7855.  
  7856.  
  7857.  
  7858.  
  7859.  
  7860. New Products
  7861.  
  7862.  
  7863. Industry-Related News & Announcements
  7864.  
  7865.  
  7866.  
  7867.  
  7868. ydb Symbolic Debugger For yacc Grammers And Parsers
  7869.  
  7870.  
  7871. Bloomsbury Software Group is now shipping the ydb symbolic debugger for yacc
  7872. grammers and parsers. ydb is a grammer development tool offering interactive
  7873. environments for grammar debugging and parser generation on UNIX systems. It
  7874. is 100 percent backward compatible with yacc, the standard UNIX parser
  7875. generator, and so can be used for debugging yacc grammars, or for creating
  7876. parsers more flexible than those generated by yacc.
  7877. ydb provides a set of tools for producing correct, conflict-free grammars at
  7878. translate time. ydb also offers users complete debugging control of an
  7879. operating parser at runtime, including the ability to trace parser actions and
  7880. set breakpoints at particular rules or other points in the parse.
  7881. ydb is available immediately for Sun 3, Sun 4, and DECstation computers, and
  7882. is being ported to others, including Hewlett-Packard workstations. ydb offers
  7883. X windows, sunView and ASCII terminal interfaces.
  7884. Prices begin at $1,250 for a single CPU license. The company offers academic
  7885. discounts, quantity discounts and site licenses. For more information, contact
  7886. Bloomsbury Software Group, P.O. Box 390018, Mountain View, CA 94039, (415)
  7887. 964-3486.
  7888.  
  7889.  
  7890. Intermetrics Provides Debugging Environment For Motorola 68332
  7891.  
  7892.  
  7893. Intermetrics Microsystems Software has released RMXDB 5.0, a ROM
  7894. monitor-based, C source-level cross debugger for Motorola's 68332 processor.
  7895. RMXDB 5.0 integrates the source-level debugging features of XDB 5.0 (as
  7896. source-level cross debugger) with a low-level target monitor program.
  7897. RMXDB 5.0 is compatible with Motorola's 68332 development system. The
  7898. ROM-based monitor portion of RMXDB is pre-configured to reside directly on the
  7899. EEPROMs located on the 68332 EVS, and communicate target information to the
  7900. RMXDB interface on the host computer.
  7901. Features of RMXDB 5.0 include the ability to display source code, registers,
  7902. and stack information, set software breakpoints, single step at the C or
  7903. assembly level, monitor and modify data, define macros and aliases, record and
  7904. playback debugging sessions simulate target I/O, and access pop-up windows for
  7905. status information and online help.
  7906. RMXDB is integrated with the Inter-Tools set of C cross compilers, cross
  7907. assemblers, and programming utilities for writing ROMable, reentrant code for
  7908. the 68332. RMXDB can be configured for almost any hardware environment with a
  7909. serial port. Intermetrics provides an installation kit to help configure the
  7910. ROM monitor for the user's board. This kit includes pre-written driver
  7911. programs, and configuration examples for a variety of off-the-shelf single
  7912. board computers and common USART boards.
  7913. Prices for the RMXDB 5.0 begin at $2,500. For more information, contact
  7914. Intermetrics Microsystems Software, 733 Concord Ave., Cambridge, MA
  7915. 02138-1002, (617) 661-0072; FAX (6I 7) 868-2843.
  7916.  
  7917.  
  7918. Microsoft Releases New Programming Tools
  7919.  
  7920.  
  7921. Microsoft Corporation has made available three programming tools: the
  7922. Microsoft Professional Advisor Library, the Microsoft C Developers Toolkit,
  7923. and a maintenance release to Microsoft C Professional Development System.
  7924. The Professional Advisor Library provides software developers with the core
  7925. functionality required to add context-sensitive, hypertext-based online help
  7926. to their DOS and OS/2 applications. The library offers extensive
  7927. cross-referencing and cross-linking capabilities with highly compressed data
  7928. for minimal hard disk usage. The library provides routines for base file
  7929. management, topic look-up, context maneuvering, support for text attributes,
  7930. and utility routines for handling multiple help files.
  7931. The Professional Development System integrates the process of editing,
  7932. building, and debugging an application. The C Developers Toolkit software and
  7933. documentation provide the details necessary to write complementary products
  7934. for these systems tools, including the Source Browser, Code-View, and
  7935. Programmer's WorkBench. By integrating their tools, add-on tool vendors can
  7936. simplify their users' development projects.
  7937. The toolkit provides documentation on the formats for the CoveView debugging
  7938. object module, extended executable debug information, symbolic information,
  7939. and incremental linker.
  7940. The C Professional Development System v6.0 provides developers with an
  7941. open-architecture integrated system. It features globally optimizing C
  7942. compiler, Source Browser, Programmer's WorkBench development environment, and
  7943. CodeView debugger, as well as other tools.
  7944. The Professional Advisor Library sells for $49.95, the C Developer's Toolkit
  7945. sells for $29.95, and the C Professional Development System v6.0 sells for
  7946. $12.95. For more information, contact Microsoft Corporation, One Microsoft
  7947. Way, Redmond, WA 98052-6399, (206) 882-8080; FAX (206) 883-8101.
  7948.  
  7949.  
  7950. WinSoft Releases Data Validating Screen Library For Windows 3.0
  7951.  
  7952.  
  7953. WinSoft has released the commercial version of Instant Windows, a software
  7954. tool for developing portable data-oriented applications for Windows 3.0 and
  7955. DOS. Instant Windows is a true data validation oriented C library for Windows
  7956. 3.0. It interfaces easliy to SQL server, Novell BTrieve and XQL, Oracle,
  7957. Informix, Sybase, Xdb, dbVista, CTree, WinTrieve, CTrieve, CIndex, 3270/HLL
  7958. API, and other database and communciations systems.
  7959. Instant Windows offers data validation, edits, and customization. Standard
  7960. validations include number/string controls, a variety of picture strings, and
  7961. range checks. Instant Windows generates automatic bug-free C code for complete
  7962. user interface. It offers a C function library that lets the programmer
  7963. develop the bulk of his application with only three major functions.
  7964. Instant Windows prices start at $249 for MS-DOS and $995 for Windows 3.0. For
  7965. more information, contact WinSoft, 1016 E. El Camino Real, Suite 216,
  7966. Sunnyvale, CA 94087, (415) 324-9552.
  7967.  
  7968.  
  7969. HP Unveils Programming Advancements
  7970.  
  7971.  
  7972. Hewlett-Packard has introduced three object-oriented programming advancements
  7973. for HP 9000 and HP Apollo computer platforms: C++ v2.1 compiler on the HP-UX
  7974. operating system; C++ Developer on Domain operating system, and Domain/C++
  7975. v2.1.
  7976. These advancements add improved compile-time performance, simplified class
  7977. construction, improved code modification and support for C++ v2.1 from AT&T.
  7978. The HP-UX and Domain operating systems comply with AT&T's UNIX system.
  7979. Also available form HP is a true C++ compiler based on AT&T C++ v2.1 on the
  7980. HP-UX operating system that generates native code on HP 9000 systems. This
  7981. compiler generates object code directly from C++ source code.
  7982. HP's C++ Developer is a class construction and browsing tool. It is a
  7983. standalone X Window System that provides a graphical representation of the C++
  7984. class-in-heritance hierarchy. The C++ Developer allows users to browse class
  7985. definition and member-function source code graphically, add and modify classes
  7986. and inheritance hierarchies, generate source-code templates and diagnose and
  7987. correct errors automatically.
  7988. For more information, contact Hewlett-Packard Company Inquiries, 19310
  7989. Pruneridge Ave., Cupertino, CA 95014, (800) 752-0900.
  7990.  
  7991.  
  7992. Borland Offers Upgrade To Microsoft C Users
  7993.  
  7994.  
  7995.  
  7996. Borland International is now offering users of Microsoft C an upgrade to
  7997. Borland's Turbo C++ Professional for $149.95. Customers may obtain the Turbo
  7998. C++ Professional competitive upgrade through participating computer dealers or
  7999. direct from Borland. The offer is valid for owners of Microsoft C or any
  8000. PC-based C or C++ compiler through January 31, 1991 in the United States or
  8001. Canada.
  8002. Turbo C++ Professional is a development environment that lets programmers add
  8003. object-oriented programming to their skill set and more effectively tackle
  8004. programming projects. Turbo C++ Professional contains Turbo C++, Turbo
  8005. Debugger, Turbo Profiler, and Turbo Assembler, and it comes with nine manuals.
  8006. International versions of Turbo C++ Professional and Turbo C++ are available
  8007. in French, German, and Italian. A Japanese version will be available in
  8008. January 1991.
  8009. For more information, contact Borland International, 1800 Green Hills Road,
  8010. Scotts Valley, CA 95066-0001. To order an upgrade, call (800) 331-0877.
  8011.  
  8012.  
  8013. Design Tool Now Generates C++
  8014.  
  8015.  
  8016. Caset Corporation has enhanced the Hierarchical Object-Oriented Design (HOOD)
  8017. Toolset to generate C++ code, in addition to ADA, to implement object-oriented
  8018. designs. The HOOD toolset is an interactive, workstation-based tool supporting
  8019. multi-user design teams.
  8020. The toolkit organizes design information in a design database facilitating
  8021. iterative design refinement. The toolkit generates documentation and
  8022. implementation code in ADA and now in C++. Design information can be
  8023. transferred between the ADA and C++ toolset variants.
  8024. The HOOD object management system captures design details for later
  8025. representation as C++ code fragments or ADA modules, so developers can
  8026. implement each HOOD object as an instance of its own C++ class. Standard
  8027. system specification packages and their own interdependencies are recorded as
  8028. environmental objects. Third party packages and re-usable code modules are
  8029. represented as class objects.
  8030. The HOOD C++ and ADA mappings are separately available options. Either
  8031. implementation language may be initially specified, the other language may be
  8032. purchased as an extension. SUN, DEC, HP, and additional UNIX/VMS workstations
  8033. are currently supported.
  8034. For more information, contact Caset Corporation, 33751 Connemara Drive, P.O.
  8035. Box 939, San Juan Capistrano, CA 92693, (714) 496-8670; FAX (714) 661-5463.
  8036.  
  8037.  
  8038. SDE Introduces OOSD/C++
  8039.  
  8040.  
  8041. Interactive Development Environments has added Object-Oriented Structured
  8042. Design (OOSD)/C++ to its Software through Pictures family of CASE products.
  8043. OOSD/C++ includes a graphical design editor that automates the standard OOSD
  8044. notation extended for C++.
  8045. The product's features include C++-specific drawing rules, which minimize
  8046. errors, and the C++ Reuse Library and Browser, which reinforce the reuse
  8047. capabilities of object-oriented languages while still in the design phase.
  8048. OOSD/C++ also includes data modeling editors, a central repository, document
  8049. preparation, and version control. An automated training tool that provides
  8050. drawing support for the OOSD notation extended for C++ is available as part of
  8051. the recently introduced "Development Methods for Migrating from C to C++ using
  8052. OOSD" course. OOSD/C++ will be available as part of an open solution, called
  8053. the C++ Development Environment.
  8054. OOSD/C++ includes a graphical editor, which automates OOSD for C++, two data
  8055. modeling editors, a multi-user repository, the document preparation system,
  8056. and version control. The basic OOSD notation has been extended to include
  8057. classes, member functions, templates, global and local scoping, single and
  8058. multiple inheritance, as well as public, private and protected relationships.
  8059. The product allows users to browse class hierarchies in the C++ Reuse Library,
  8060. which increases quality and productivity by reinforcing the reuse capablities
  8061. of object-oriented languages while still in the design phase. In the future,
  8062. the library will be pre-populated with extensible class libraries. Another key
  8063. feature of OOSD/C++ is the C++ Guidance System, which minimizes errors by
  8064. enforcing C++-specific syntax rules regarding the interaction between C++
  8065. components, such as assembly of classes, connections between objects, and
  8066. exportation of objects.
  8067. For more information, contact Interactive Development Environments, 595 Market
  8068. St., 10th Floor, San Francisco, CA 94105, (415) 543-0900; FAX (415) 543-3716.
  8069.  
  8070.  
  8071. FairCom Enhances File Handler
  8072.  
  8073.  
  8074. FairCom has released c-tree Plus, a new file management and data server
  8075. product that enhances and extends the c-tree file handler, FairCom's file
  8076. management toolkit.
  8077. Using c-tree Plus, programmers can build applications for more than 100
  8078. environments, receive full portability offered by the C programming
  8079. environment. With these tools, developers can use either high-level index
  8080. sequential-like access methods, or low-level data management functions. Both
  8081. methods offer advanced features, such as transaction processing, ANSI-standard
  8082. SOL support, resource records, superfiles and batch operations.
  8083. The c-tree Plus file handler sells for $595. Current users of c-tree can
  8084. upgrade for $200. For more information, contact FairCom, 4006 West Broadway,
  8085. Columbia, MO 65203, (800) 234-8180; FAX (314) 445-9698.
  8086.  
  8087.  
  8088. Softaid Introduces Download Package For In-Circuit Emulators
  8089.  
  8090.  
  8091. Softaid has introduced a high-speed download package for its line of
  8092. in-circuit emulators. The Fiber Optics Link transfers the user's code to the
  8093. emulator at more than 250,000 bytes per second.
  8094. The Fiber Optics Link replaces RS-232 with an ultra high-speed serial
  8095. protocol. All communications goes over the fiber. This link transfers data in
  8096. excess of 250,000 bytes per second; a one million byte program will download
  8097. in only four seconds.
  8098. The Fiber Optics Link includes a five-meter long duplex fiber cable that
  8099. connects the emulator to the PC. A half slot controller board for any PC or
  8100. compatible is included. The link costs $1,200. For more information, contact
  8101. Softaid, 8930 Route 108, Columbia, MD 21045, (301) 964-8455; FAX (301)
  8102. 596-1852.
  8103.  
  8104.  
  8105. SparcStation 1 Now Hosts C Executive O/S
  8106.  
  8107.  
  8108. JMI's C Executive operating system for the Sun Microsystems SPARC has been
  8109. repackaged to be hosted on SPARCstation 1. The SPARC is a high-performance
  8110. RISC processor. The SPARC-based workstation can be used to develop
  8111. applications before downloading to a SPARC target board.
  8112. C Executive is a real-time, multitasking operating system kernel used in
  8113. embedded control applications. The new SPARC version of the kernel allows
  8114. current C Executive customers using CISC microprocessors to move their
  8115. applications to the SPARC. The original SPARC port of C Executive was
  8116. accomplished using a SPARC cross compiler on the Sun 2 workstation. The new
  8117. version was produced using native C compiler, assembler, and linker running on
  8118. the SPARCstation 1 under SunOS.
  8119. The new SPARC version also offers an optional file system, CE-DOSFILE, and a
  8120. system debugger, CE-/VIEW. CE-DOSFILE replicates the DOS 8086 file structure
  8121. on external media, allowing SPARC-based embedded systems to read and write DOS
  8122. diskettes online.
  8123. For more information, contact JMI Software Consultants, 904 Sheble Lane,
  8124. Spring House, PA 19477, (215) 628-0846.
  8125.  
  8126.  
  8127. Book Shows How To Build Reusable Software
  8128.  
  8129.  
  8130. Prentice Hall has published A C++ Tool Kit by Jonathon Shapiro. In his book,
  8131. Shapiro discusses how object-oriented languages can help with reusing
  8132. software, and he provides a reusable tool in each chapter taken from his own
  8133. programming projects. Divided into four parts, the book covers software reuse
  8134. and object-oriented languages; an overview of C++ implementation details such
  8135. as tuning performance and memory allocation for faster and more efficient
  8136. applications.
  8137. To order this book, contact Prentice Hall, Order Department, 200 Old Tappan
  8138. Rd, Old Tappan, NJ 07675, (201) 767-5937.
  8139.  
  8140.  
  8141.  
  8142. Companies Join C++ Reseller Alliance
  8143.  
  8144.  
  8145. Sixteen companies have joined the C++ Reseller Alliance, formed by AT&T's UNIX
  8146. System Laboratories, to promote products based on AT&T's C++ programming
  8147. language and libraries, and to share information on using C++ in large
  8148. software development projects.
  8149. The C++ Reseller Alliance will share with its members information about coming
  8150. enhancements to the C++ language and libraries from USL. Membership in The C++
  8151. Reseller Alliance is open to any hardware or software vendor who licenses and
  8152. distributes the AT&T USL C++ Language System. No membership fees are required.
  8153. The group will meet two or three times a year, coincident with significant C++
  8154. industry events.
  8155. For more information, contact Paul Fillinich, C++ Product Manager, UNIX System
  8156. Laboratories, at (201) 580-4363.
  8157.  
  8158.  
  8159. Expanded Memory Support Enhances Text Editor
  8160.  
  8161.  
  8162. American Cybernetics has announced version 5.0 of Multi-Edit. The text editor
  8163. offers expanded memory support. Users can select either an SAA (windows-style)
  8164. interface or a Classic (PC-style) interface from a long list of setup options.
  8165. Features include seamless mouse support throughout the editor environment, a
  8166. user's menu for frequently executed macros, programs, and text files; a
  8167. keystroke macro manager to simplify and organize unlimited on-the-fly macros,
  8168. expanded online hypertext help; and a 380-page User's Guide and Macro Lanugage
  8169. Reference Guide.
  8170. For more information, contact American Cybernetics, 455 S. 48th St., Suite
  8171. I07, Tempe, AZ 85281, (602) 968-1945; FAX (602) 966-1654.
  8172.  
  8173.  
  8174. File Shuttle Utility
  8175.  
  8176.  
  8177. GetC Software has introduced File Shuttle Xpress 5.0. This File Shuttle
  8178. utility integrates inter-computer file transfer with file management
  8179. capabilities for users running any combination of Windows 3.0, DOS, OS/2 in
  8180. its DOS compatibility box and networks.
  8181. Version 5.0 requires 256K RAM for the DOS version, one parallel or serial port
  8182. on each computer and DOS 2.0 or later. The Windows 3.0 version of the program
  8183. requires that Windows 3.0 be installed with the necessary system requirements
  8184. for that environment.
  8185. File Shuttle Xpress 5.0 sells for $139.95. For more information, contact GetC
  8186. Software, I280 Seymour St., 2nd Floor, Vancouver, B.C. V6B 3N9, (604)
  8187. 684-3230; FAX (604) 689-1401.
  8188.  
  8189.  
  8190. Expert Systems Module Accelerates Software Development
  8191.  
  8192.  
  8193. Integrated Systems has introduced a knowledge-based, real-time expert systems
  8194. module. RT/Expert automates and accelerates the development of real-time
  8195. software that incorporates rule-based logic.
  8196. RT/Expert gives users an elegant way to describe a collection of IF-THEN-ELSE
  8197. rules. These rules are effective for diagnostics, monitoring, alarm filtering,
  8198. and mode selection applications.
  8199. For more information, contact Integrated Systems, 2500 Mission College Blvd.,
  8200. Santa Clara, CA 95054-1215, (408) 980-1500; FAX (408) 980-0400.
  8201.  
  8202.  
  8203. SSC Offers Command Summary For UNIX System V
  8204.  
  8205.  
  8206. Specialized System Consultants is offering an 80-page command summary for UNIX
  8207. System V.4 commands. System V.4 is the merging of AT&T System 5 releases with
  8208. BSD features.
  8209. SSC's UNIX System V.4 pocket-size reference details command syntax and
  8210. describes the options available for each command. A comprehensive summary of
  8211. the nawk command and a five-page summary of the System V shell are included.
  8212. The summary also includes expanded sections on ed, sdb, sed, and telnet.
  8213. The booklet is priced at $8. For more information, contact Specialized System
  8214. Consultants, P.O. Box 55549, Seattle, WA 98155, (206) 527-3385; FAX (206)
  8215. 527-2806.
  8216.  
  8217.  
  8218.  
  8219.  
  8220.  
  8221.  
  8222.  
  8223.  
  8224.  
  8225.  
  8226.  
  8227.  
  8228.  
  8229.  
  8230.  
  8231.  
  8232.  
  8233.  
  8234.  
  8235.  
  8236.  
  8237.  
  8238.  
  8239.  
  8240. New Releases
  8241.  
  8242.  
  8243. Updates
  8244.  
  8245.  
  8246.  
  8247.  
  8248. CUG329 UNIX Tools for PC
  8249.  
  8250.  
  8251. Henri de Feraudy has updated his string substitution utility, csubst. This
  8252. update includes minor bug fixes.
  8253.  
  8254.  
  8255. CUG330 CTask
  8256.  
  8257.  
  8258. Thomas Wagner (Germany) has updated his multitasking routines, CTask. This
  8259. version 2.2 release includes bug fixes, some internal changes, and addition of
  8260. new functions.
  8261.  
  8262.  
  8263. New Releases
  8264.  
  8265.  
  8266.  
  8267.  
  8268. CUG334 GNUPLOT
  8269.  
  8270.  
  8271. Written by Thomas Williams, Colin Kelley, modified by Russell Lang, Dave Kotz,
  8272. John Campbell, and submitted by Henri de Feraudy (France), GNUPLOT v2.0 is a
  8273. command-driven interactive function plotting program. By typing commands
  8274. interactively or loading a text file that contains commands, user can draw
  8275. graphs or plot data points on screen in a given graphics mode or printer using
  8276. a given printer driver. GNUPLOT provides a set of commands: loading/saving
  8277. command file, plotting a function(built-in or user-defined) or data files,
  8278. printing a title, label or arrow on a graph, clipping data points, specifying
  8279. graphics mode (CGA, EGA, VGA if PC), line style, grid, ranges, offset, scaling
  8280. size, sampling rate, polar/rectangular coordinate, turning on/off auto-axis
  8281. scaling or auto-tic marks, output redirection, online help, and escaping to
  8282. shell.
  8283. Built-in mathmatical functions are the same as the corresponding function in
  8284. UNIX math library, except that all functions accept integer, real, and complex
  8285. arguments. The sgn function is also supported as in BASIC.
  8286. GNUPLOT supports the following graphics drivers: AED 512, AED 767, BBN
  8287. BitGraph, Roland DXY800A, EEPIC, Epson LX-800, Fig, HP2623, HP2648, HP75xx,
  8288. HPGL, IBM Proprinter, Imagen, Iris4D, Kermit-MS, LaTeX, NEX CP6 pinwriter,
  8289. PostScript, QMS QUIC, ReGis (VT125 and VT2xx), Selanar, Tek 401x, Vectrix 384,
  8290. and UNIXplot. For the PC version, it supports IBM CGA, EGA, MCGA, VGA,
  8291. Hercules, ATT 6300, and Corona 325 graphics.
  8292. The disk includes a complete set of C source files for the program and
  8293. graphics drivers, makefile for UNIX, Microsoft C and Turbo C, documentation,
  8294. demo command files (Fig 1 - 4), and an MS-DOS executable file (compiled under
  8295. Turbo C). The program is compiled under UNIX, VMS, MS-DOS (using Microsoft C
  8296. or Turbo C).
  8297.  
  8298.  
  8299.  
  8300.  
  8301.  
  8302.  
  8303.  
  8304.  
  8305.  
  8306.  
  8307.  
  8308.  
  8309.  
  8310.  
  8311.  
  8312.  
  8313.  
  8314.  
  8315.  
  8316.  
  8317.  
  8318.  
  8319.  
  8320.  
  8321.  
  8322.  
  8323.  
  8324.  
  8325.  
  8326. We Have Mail
  8327. Mr. Ward:
  8328. I feel obligated to respond to Mr. Curren's letter (CUJ, December 1990)
  8329. criticizing you for including the SoftC Database Library in Volume 326 of the
  8330. CUG library.
  8331. Mr. Curren was upset because he believed that our licenese agreement demanded
  8332. a royalty payment for each copy of his application he sold. This is simply not
  8333. the case! We try to make this clear in the "Product Code and Derivative Works"
  8334. section of the license agreement with the following statement:
  8335. "The Customer may reproduce and distribute application programs created using
  8336. the Product without additional licenses or fees."
  8337. I think this plainly states our position that we do not expect any royalties
  8338. from our users. We do restrict the ways in which the library alone (not as
  8339. part of a derivative product) can be used. Essentially we say that the library
  8340. is to be used like a book. Perhaps Mr. Curren made the mistake of also
  8341. extending this restriction to his applications.
  8342. We think our support policies clearly reflect our user-orientation. Fixes for
  8343. all reported bugs are available directly from us at no charge. Prior to each
  8344. release our library is subjected to several thousand tests (1 megabyte of
  8345. source consisting of 195 test files). We do our best to ship bug-free
  8346. software.
  8347. It's unfortunate that Mr. Curren let the licensing issue keep him from "test
  8348. driving" our product. The combination of our product with the CXL library
  8349. (Vol. 278) has proven to be extremely powerful and in many ways more flexible
  8350. than our competitors'. Many of our users have returned competing products
  8351. after trying our library.
  8352. It is indeed unfortunate that Mr. Curren never sought to contact us. We would
  8353. have been (and still would be) happy to provide explanations for any questions
  8354. he may have had and even supply a free shareware version of the library (as we
  8355. have done for many other software professonals who have called with
  8356. questions).
  8357. Finally, I am curious why you printed Mr. Curren's letter without a single
  8358. comment. It seems out of character for you to allow such complaints to go
  8359. without response. Are you aware that your silence gave the impression that you
  8360. concurred with his statements?
  8361. Sincerely,
  8362. Kim Schumann
  8363. Chief Software Engineer
  8364. SoftC, Ltd.
  8365. 16820 Third Street N.E.
  8366. Ham Lake, MN 55304-4703
  8367. Thank you for clarifying the royalty issue. As for not answering the letter, I
  8368. sincerely hope everyone knows that letters from readers represent only their
  8369. authors' opinions, not mine nor the magazine's. We publish all kinds of
  8370. letters we don't agree with; that's part of being a forum. Many factors
  8371. influence whether a specific letter gets answered: whether we have room for
  8372. the answer, whether I know anything about the subject, whether I think it
  8373. deserves legitimization, whether the muse is sitting with me.
  8374. We certainly didn't mean to endorse Curren's opinions by not responding. I
  8375. apologize if the letter gave that impression. Thanks for the support. -- rlw
  8376. Attn: Rex Jaeschke
  8377. Dear Sir:
  8378. I enjoyed your article in the Sept '90 issue concerning use of the qsort
  8379. function. Under the subheading Multikey Sorts, however, you make the
  8380. recommendation (as I understand it) to separately sort the primary key and
  8381. then make smaller subsorts of each secondary key. Under any circumstances that
  8382. I have been able to imagine, this should not be necessary as long as the sort
  8383. function passed makes its determinations giving the primary key full priority
  8384. over the secondary, that is the secondary field is only used if a complete
  8385. match occurs in the primary. This has always worked in any place I have used
  8386. it.
  8387. Sincerely,
  8388. Jay Holovacs
  8389. 95 King George Rd.
  8390. Warren, NJ 07059-6921
  8391. I agree. Thanks. -- Rex
  8392. Dear Mr. Plauger,
  8393. Here are a couple more solutions to Dale Wharton's problem. They should work
  8394. regardless of the host character set. The first (printc) puts out a format
  8395. suitable for re-encoding with sprintf (when using the same character set). The
  8396. second (printasciic) decodes ASCII. Both are shown in Listing 1.
  8397. Your magazine has been one of the most useful I have ever subscribed to. I
  8398. keep digging into my stack of past issues looking for things I remember are
  8399. there somewhere. Do you have any plans for indexing past issues? I've also
  8400. enjoyed your work since Software Tools. My copy of Standard C is already
  8401. looking pretty beat up. It's good to see you step in as editor of the jounal.
  8402. Sincerely,
  8403. Eric Blossom
  8404. 2737 Russell Street
  8405. Berkeley, CA
  8406. Those are both useful functions. I can't count how many times I have cobbled
  8407. versions with only part of the needed functionality. Thanks for passing them
  8408. on. And thanks for the kind words. -- pjp
  8409. And yes, we are planning a comprehensive index... soon? -- rlw
  8410. Editor:
  8411. I often use the following code when it makes more sense than a do-while loop:
  8412. #define until (expression) \
  8413. while (! (expression))
  8414.  
  8415. do {.....
  8416. }
  8417. until (something happens);
  8418. A typical use would be to read the keyboard and stay in the loop "until the
  8419. key pushed == ESC." It's just easier to think of than to stay in the loop
  8420. "while the key ! = ESC". Or for infinite loops:
  8421. #define HELL_FREEZES_OVER 0
  8422.  
  8423. do {.....
  8424. }
  8425. until (HELL_FREEZES_OVER);
  8426. Sincerely,
  8427. Donald Gessling
  8428. Dynatech Nevada
  8429. 2000 Arrowhead Dr.
  8430. Carson City, NV 89706
  8431. If you feel that helps readability, fine. Just be careful when you ship code
  8432. to other folks - they may not agree with your notions of a good language. I
  8433. have seen C altered, via macros, to read like Pascal PL/I, and Algol 68. That
  8434. can certainly please recent converts, but it can interfere with maintenance if
  8435. you go overboard. -- pjp
  8436. Dear Mr. Ward,
  8437. I have just received your final notice of my subscription expiring, and am
  8438. taking the time to inform you as to why I'm letting it go.
  8439.  
  8440. First, let me point out the admirable aspects of your magazine; it deals
  8441. wholly in the C family of languages, your Q&A section is unique in its ability
  8442. to answer questions without belittling the asker, there's a good mix of narrow
  8443. band and wide band articles with respect to their intended audience, and the
  8444. regular authors generally cover important topics for the professional
  8445. programmer. Taken as a whole, these qualities represent a formidable arsenal
  8446. for the subscriber/regular reader.
  8447. Unfortunately, there is one severe drawback in your implementation of The C
  8448. Users Journal: source code. It simply is that important. Let me illustrate
  8449. through example. Suppose you buy a paper which describes a fantastic squash
  8450. casserole. You say to yourself, "Gee, I really like squash, how do I make
  8451. this?" Upon close examination of the paper, you find that it will cost you two
  8452. to four times as much for the recipe as it did to hear the description! You
  8453. may be miffed the first time, but after twelve issues of the paper, each time
  8454. seeing something that would be nice to make (without violating any copyrights,
  8455. of course), and each time being reminded that you'll have to pay again for
  8456. something that should have been in there to begin with, you get to the point
  8457. of disgust. This is why I am no longer interested in maintaining my
  8458. subscription with CUJ.
  8459. Since I have defined a problem, it is only proper that I try to define a
  8460. solution. There are two options that I can think of, though I'm sure I haven't
  8461. exhausted them all. First, what of an online listings service (BBS)? Doctor
  8462. Dobb's Journal uses one with great success. They archive the listings and name
  8463. them with the three letter month mnemonic and the year of the issue (ie,
  8464. aug90.arc). the users could then retrieve any listing they desired at their
  8465. convenience. Second, printing the listings in the magazine itself would solve
  8466. this dilemma, but would increase the number of pages in the magazine. Paper is
  8467. everywhere, subscribers are not. Right now, the cover price is $4.50 and the
  8468. subscription price is $2.25. This comes to approximately 3.1 and 1.6 cents a
  8469. page, respectively. Is your overhead that high?
  8470. Each possible solution described above assumes that you do not wish to make a
  8471. profit from your followers twice for one thing: solutions. Since I tire of
  8472. being teased, I shall wait patiently until you deem it fitting to freely allow
  8473. access to the source listings (those that are allowed access by the
  8474. copyrighters' consent). At this time, I shall enthusiastically resubscribe. I
  8475. have a lifetime to wait, can CUJ afford that luxury?
  8476. Regretfully,
  8477. Steve Vickers
  8478. Apopka, FL 32712
  8479. I too am regretful. You write a very rational letter, arguing a position with
  8480. which I totally agree. But I don't understand -- why do you perceive us as
  8481. withholding source code?
  8482. You say one solution is to print the listings in the magazine. That's exactly
  8483. what we do! Apparently we've inadequately communicated something, please help
  8484. me figure out what.
  8485. In all but a few rare exceptions (where the listings would run hundreds of
  8486. pages), we print full source code with each article. We also offer this same
  8487. source code on disk for a $5.00 service fee. Perhaps we haven't explained the
  8488. code disk well enough? Occassionally we put extra goodies on that disk (like
  8489. maybe a shareware library that was mentioned in a story), but aside from such
  8490. bonuses, the code disk is just a machine readable copy of the source that
  8491. appeared in the magazine. We're certainly not trying to force you to buy the
  8492. code disk by holding back essential source!
  8493. Are you referring to the code on CUG volumes? We report on what's available in
  8494. the CUG library, just as we report on developments on Usenet and other PDS
  8495. sources -- surely you don't expect us to print all that source just because we
  8496. mentioned it? We're talking about multi megabytes of source here!
  8497. As for putting the source on a bulletin board, right now I don't think we have
  8498. the staff to properly administer such a service (keeping the overhead low and
  8499. all that). We hope to remedy that in the next few months, either by adding
  8500. staff or making an arrangement with a reputable volunteer site. But
  8501. nevertheless, since printing the source is an acceptable alternative for you,
  8502. I don't understand why you think we've failed. -- rlw
  8503. Dear Dr. Plauger:
  8504. I have another interpretation of the question from Mr. Wharton (November 1990,
  8505. We Have Mail). If he is using an IBM-PC style computer, control characters
  8506. have been assigned various symbols such as card suits, arrows, and smiley
  8507. faces. The catch in displaying these characters "the way printed charts
  8508. picture them" is not with C, but with DOS and BIOS. When characters such as
  8509. bell and line feed are sent from the C environment to DOS (via printf(),
  8510. putchar(), etc.) they are executed with their traditional meanings, which is
  8511. what most of us want most of the time.
  8512. In order to display the special PC symbols with the same codes, it is
  8513. necessary to bypass DOS and call the BIOS directly. (See Listing 2.) Books on
  8514. PC graphics or PC hardware that explain other interesting BIOS functions
  8515. should be available at any computer bookstore.
  8516. Sincerely,
  8517. Bob Raemer
  8518. 8960 Neill Lake Rd.
  8519. Eden Prarie, MN 55347
  8520. Thanks to you, too -- pjp
  8521. Dear Mr. Ward:
  8522. In the October issue, Andrew Hollands mentioned Modula-2, and you expressed
  8523. interest in hearing from readers who use it extensively. I have been
  8524. programming professionally for many years and have used many languages.
  8525. Excluding the research and experimental languages which are not generally used
  8526. or available, Modula-2 is my clear favorite.
  8527. When Wirth first published the description of Modula-2 around 1982, I was a
  8528. bit disappointed in it. Since then, a few of the original problems have been
  8529. fixed, and my point of view has changed in light of the alternatives. There
  8530. are still a couple of things about Modula-2 that I wish were different, but
  8531. overall, I find it a great productivity enhancer.
  8532. The best summary I can give is that Modula-2 has the necessary trapdoors so
  8533. that you can do low-level (and probably machine-dependent) stuff when you
  8534. really need to, but it provides a lot of safety most of the time, when you
  8535. don't. You won't fall into a trapdoor inadvertently.
  8536. By contrast, C forces you to use too low-level constructs in several extremely
  8537. common situations where they aren't needed, because there is nothing else. My
  8538. favorite examples are explicit use of pointers when arrays and by-reference
  8539. parameters are really needed, and the fact that every pointer is really a
  8540. pointer to one element of an infinite array, whereas a pointer to a single
  8541. object or into a bounded array is usually what is really needed.
  8542. Modula-2 also has a type-safe separate compilation facility, and a method of
  8543. packaging data structure with the procedures that operate on it. While these
  8544. features have been regarded as standard techniques for many years by
  8545. programming language researchers, Modula-2 and Ada are the only languages with
  8546. any degree of popularity which fully support them. (Some of the
  8547. object-oriented languages finally emerging do provide an improved variation of
  8548. the latter feature.)
  8549. I have also used Ada professionally for about five years now, and I continue
  8550. to be more and more disillusioned with it. It has a lot of useful features,
  8551. including a few which Modula-2 lacks. However, it also has a mind-numbing
  8552. array of complex and bizarre rules that depend on every imaginable factor
  8553. short of the phase of the moon. A colleague recently remarked that even after
  8554. five years programming in Ada, he still frequently encounters a simple
  8555. declaration or statement whose real meaning he can't be confident he
  8556. understands, without spending hours poring over the reference manual.
  8557. A lot of the desirable features of Modula-2 which distinguish it from original
  8558. (and ISO) Pascal have also made their way into the various implementations of
  8559. Pascal too. Pascal compiler implementors have apparently recognized these as
  8560. valuable, and put them in as compiler-specific extensions to the language.
  8561. However, since Modula-2 got these in the (original or revised) language
  8562. description, they are more portable than in the dialects of Pascal. Also, no
  8563. Pascal that I know of has separate interface and implementation modules. The
  8564. popular UCSD Pascal units support type safe separate compilation, but they
  8565. force massive recompilation, if you have to touch one.
  8566. A standardization effort for Modula-2 is well underway. To some extent, it too
  8567. suffers from some of the usual problems stemming from trying to solve tough
  8568. technical dilemmas while immersed in political battles. However, the process
  8569. has begun much earlier in the history of the language than usual. There is not
  8570. nearly so great a proliferation of dialects as there were of Pascal and C,
  8571. when standardization of those languages began. Hopefully, this will lead to a
  8572. standard which is not so far out of sync with established practice, and which
  8573. will be better accepted.
  8574. "Standard" Modula-2 does, of course, lack object-oriented features. Despite
  8575. all the excessive hype, object-orientation has tremendous value for
  8576. programming. There is an object-oriented dialect of Modula-2 from JPI. This is
  8577. even better than plain Modula-2, unless you care about portability, as I do.
  8578. In that case, you can hardly afford to use the object-oriented features, since
  8579. they are unique to one machine, one operating system, and one compiler.
  8580. (Perhaps some day, I will write a letter about Modula-3, which, though little
  8581. known, is considerably more powerful than Modula-2, is almost as simple, and
  8582. has object-oriented features in its original report.)
  8583. Mr. Hollands refers to Modula-2 as a "crutch," and you state that debugging in
  8584. C (but presumably not in Modula-2) is too difficult for students. I have heard
  8585. from countless people who believe that, for an experienced programmer who has
  8586. the ability to cope with a more difficult language like C or Ada, an
  8587. easier-to-use language like Modula-2 has no real advantage. I disagree. I can
  8588. cope with the difficult languages as well as anybody, but I also care about my
  8589. own productivity. I prefer to devote as much of my programming skill as
  8590. possible to solving real problems which could not be solved for me
  8591. mechanically by a compiler for a programming language which uses well-known
  8592. language technology.
  8593. Modula-2 has been slowly growing in popularity for a long time. We have all
  8594. heard the argument over and over, that the technical quality of a programming
  8595. language has little to do with its actual success. What will actually happen
  8596. to Modula-2 remains to be seen, but I think it looks more likely than ever
  8597. that it will be successful. It already enjoys far greater popularity in Europe
  8598. than in the United States. Even here, it is already sufficiently successful
  8599. that there is little doubt that a compiler will be available for most any
  8600. machine/operating system you decide later to port your code to. In the
  8601. meantime, I am having more fun than I've had with any programming language in
  8602. 25 years and getting more code working in less of my time.
  8603. Rodney M. Bates
  8604. 1513 Blue Spruce
  8605. Derby, KS 67037
  8606. Thanks for a very thoughtfully-written letter. -- rlw
  8607. I agree that Modula-2 is a definite improvement over Pascal. I also agree that
  8608. a programming language should do as much of the dirty work as possible. It
  8609. should minimize the number of error-prone operations you have to perform.
  8610. My observation, however, is that Modula-2 is not spreading like wildfire. It
  8611. is true that the language is more popular in Europe than in the U.S. So, too,
  8612. were Algol 60, Algol 68, and Pascal -- none of which have survived as serious
  8613. commercial programming languages. If you gamble on widely available compilers
  8614. and support tools soon, you're taking a serious risk.
  8615. What makes a language a rip-roaring success is not always easy to quantify or
  8616. articulate. Many find C, and FORTRAN before it, inelegant or outright
  8617. dangerous. Others simply notice that you can get a lot of work done by
  8618. programming in C. I'm just grateful that the Elegance Police don't have much
  8619. power. I prefer the freedom of an open marketplace.
  8620. If the marketplace makes Modula-2 a winner in the next few years, I'll have no
  8621. objections. Someday, we may even see a Modula-2 Users Journal. For now, I just
  8622. view languages like this as interesting experiments.
  8623. But then, I'm notoriously pragmatic. -- pjp
  8624. Dear Robert Ward,
  8625. I am a developer who is working with C for the Windows platform. I am looking
  8626. to purchase a library of statistical routines, the most important of which is
  8627. Multiple Regression (linear fitting).
  8628. My first choice for the library is one that is written for the Windows
  8629. environment. However, if such a thing does not exist I will settle for a
  8630. library for which source code is available. The source code will have to be
  8631. written in C.
  8632. Any help that you can give me in this area is immensely appreciated. Thank
  8633. your for your kind attention.
  8634. Avi Farah
  8635. Price Waterhouse
  8636. 65 Madison Avenue
  8637. Morristown, NJ 07960-1940
  8638. Both CUG 266 microPLOX and CUG334 GNUPLOT are not statistics packages, but
  8639. would provide an excellent tool to help your statistical analysis. Both
  8640. packages take commands and data input and display or draw graphs/charts on
  8641. monitors or printers. microPLOX would be appropriate for drawing a bar chart,
  8642. while GNUPLOT is capable of drawing graphs or plotting data points based on a
  8643. given mathematical function (built-in or user-defined), such as sin(x),
  8644. cos(x), etc. GNUPLOT supports an extensive set of graphics drivers including
  8645. PostScript.
  8646. With these tools, all you need may be some mathematical formula to implement
  8647. statistical techniques. However, CUG library still lacks an integrated
  8648. statistical package, for which we count on submissions from readers. -- Kenji
  8649. Hino
  8650. Dear Mr. Plauger,
  8651. I received your special introductory offer in the mail today to subscribe to
  8652. The C Users Journal. I don't usually write to people who send me offers to
  8653. subscribe to a magazine; this is the first time, but I have a suggestion for
  8654. you which may help increase the number of subscribers that you have.
  8655. I subscribed to your magazine a while back, as I was attempting to learn C. I
  8656. had purchased Borland's Turbo C, along with a couple of books on C. I was
  8657. struggling to learn the language; it was unlike Basic or dBase, which I had
  8658. taught myself by reading the manuals and trying different things out. The C
  8659. language is very powerful and unique!
  8660. I found your magazine to be more in depth than what I was ready for at that
  8661. time. The reason I'm writing to you now is I think there is a market for a
  8662. publication that addresses itself to the beginning and intermediate market.
  8663. There are a lot of people out there like me who bought and continue to buy
  8664. Turbo C or Quick C because they want to learn the language, but the
  8665. complexities and new concepts like pointers are just overwhelming at first.
  8666. Why don't you tap into that market?
  8667. You could create a separate magazine that holds the hand of the beginner and
  8668. teaches him basic things, and which also has sections of interest to the
  8669. intermediate C programmer. As the person became more advanced, he could move
  8670. up to The C Users Journal. Alternately, you could just expand The C Users
  8671. Journal to cover topics of interest to all, from beginner to expert, but that
  8672. would be more intimidating to the new user, as well as a partial waste for the
  8673. advanced user. By having two separate publications, you're not sending out a
  8674. lot of pages that a subscriber has no intention of reading.
  8675. You have a wonderful library, and you have the resources to do it. You
  8676. definitely have the technical expertise to do it, and there's no magazine on
  8677. the market at this time that I'm aware of that caters to beginning C
  8678. programmers. The market's all yours!
  8679. If you should decide to expand and go for the beginning market, let me know
  8680. and I'll be more than happy to subscribe, and I'm sure a lot of other people
  8681. will too. You could even include a flyer in packages of Turbo C and Quick C to
  8682. let new buyers know of your publication(s).
  8683. Thank you for taking the time to consider my proposal, and I look forward to
  8684. hearing from you soon. I wish you the best of luck in your publishing
  8685. endeavors!
  8686.  
  8687. Sincerely,
  8688. Vincent Harris
  8689. P.O. Box 1324
  8690. Sebastopol, CA 95473
  8691. A magazine can't be all things to all people. We do try to publish a range of
  8692. editorial material, some for beginners and some for advanced C programmers.
  8693. Keeping both a broad range and a tight editorial focus is a continual
  8694. balancing act. We can only keep trying to get it right.
  8695. Starting a new magazine is a publisher's decision. That's Robert Ward's
  8696. domain. I just edit here. -- pjp
  8697. Dear CUJ:
  8698. In your November, 1990 article "A Flexible Dynamic Array Allocator" by Dick
  8699. Hogaboom, I located a small typo that keeps the routine from correctly
  8700. initializing arrays. On page 118,
  8701. p_data = base + data_off * ptr_size
  8702. should be
  8703. p_data = base + off(num_dim-1) *
  8704. ptr_size
  8705. Regards,
  8706. Jon Michaels
  8707. 61 Cutler Road
  8708. Greenwich, CT 06831
  8709. Thanks -- pjp
  8710.  
  8711. Listing 1
  8712. /* show.c - Demonstrates some character
  8713. printing functions */
  8714. #include <stdlib.h>
  8715. #include <stdio.h>
  8716. #include <string.h>
  8717. #include <ctype.h>
  8718.  
  8719. #define showx(a) printf(isprint(a) ? \
  8720. "%c": "\\x%\.2x", a)
  8721.  
  8722. void
  8723. showc( int c )
  8724. {
  8725. static char esc[ ] = "\a\b\t\n\r\f\v";
  8726. static char *ptr[ ] = {
  8727. "\\a", "\\b", "\\t", "\\n",
  8728. " \\r ", " \\ f ", "\\v"
  8729. };
  8730. char *s = strchr(esc, c);
  8731.  
  8732. if (s && c)
  8733. printf ( ptr[s-esc] );
  8734. else
  8735. showx( c );
  8736. }
  8737.  
  8738. void
  8739. showasciic(int c)
  8740. {
  8741. static char *a[ ] = {
  8742. "<NUL>", "<SOH>", "<STX>", "<ETX>",
  8743. "<EOT>", "<ENQ>", "<ACK>", "<BEL>",
  8744. "<BS>", "<HT>", "<LF>", "<VT>",
  8745. "<FF>", "<CR>" "<SO>", "<SI>",
  8746. "<DLE>", "<DC1>", "<DC2>", "<DC3>",
  8747. "<DC4>", "<NAK>", "<STN>", "<ETB>",
  8748. "<CAN>", "<EM>", "<SUB>", "<ESC>",
  8749. "<FS>", "<GS>", "<RS>", "<US>"
  8750. };
  8751.  
  8752.  
  8753. static char b[ ] =
  8754. " !\"#$%&'( )*+,-./0123456789:;<=>?" \
  8755. "@ABCDEFGHIJKLMNOPQRSTUVWXYZ [\\]^" \
  8756. "'abcdefghijklmnopqrstuvwxyz{}~";
  8757.  
  8758. if (c & ~0x7f) ( /* not ASCII */
  8759. printf ("<");
  8760. showx(c);
  8761. printf ("?>");
  8762. }
  8763. else { /* ASCII */
  8764. if {c == 0x7f)/* DEL */
  8765. printf ("<DEL>");
  8766. else if (c & ~0xlf) /* graphic */
  8767. putchar(b[c-32]);
  8768. else /* control */
  8769. printf (a[a]);
  8770. }
  8771. }
  8772.  
  8773. void
  8774. main(void)
  8775. {
  8776. int c;
  8777.  
  8778. for (c = -1; c < 129; c++)
  8779. showc (c);
  8780. putchar ('\n');
  8781.  
  8782. for (c=-1; c<129; c++)
  8783. showascii(c);
  8784. putchar { ' \n ' );
  8785. }
  8786.  
  8787.  
  8788. Listing 2
  8789. /* works with Microsoft C V6.0, PC with EGA */
  8790.  
  8791. #include <stdio.h>
  8792. #include <dos.h>
  8793.  
  8794. void cchar(int chr, int color, int background, int blink, int page);
  8795.  
  8796. #define BLACK 0
  8797. #define BLUE 1
  8798. #define GREEN 2
  8799. /* etc. */
  8800.  
  8801. void main()
  8802. {
  8803. printf("%c", 65); /* prints 'A' */
  8804. printf("%c", 1); /* prints smiley face */
  8805.  
  8806. printf("%c", 7); /* rings bell */
  8807. putchar(7); /* rings bell */
  8808.  
  8809. /* above functions call DOS (probably via INT 21H), which
  8810. ** executes some control codes with their traditional meaning.
  8811. */
  8812.  
  8813.  
  8814. /* now bypass DOS and use ROM-BIOS * /
  8815. cchar(7, GREEN, BLACK, 1, 0); /* blinking green diamond */
  8816. }
  8817.  
  8818. /* color character at present cursor location */
  8819. void cchar( int chr, int color, int background, int blink, int page)
  8820. {
  8821. union REGS regs;
  8822.  
  8823. regs.h.ah = 9; /* write character function */
  8824. regs.h.al = chr; /* character code */
  8825. /* attribute */
  8826. regs.h.bl = (blink ? 0x80 : 0) ((background & 7) << 4) (color & 0x0f);
  8827. regs.h.bh = page; /* display page */
  8828. regs.x.cx = l; /* repetition count */
  8829. int86(0xl0, ®s, ®s); /* call BIOS video function */
  8830. }
  8831.  
  8832.  
  8833.  
  8834.  
  8835.  
  8836.  
  8837.  
  8838.  
  8839.  
  8840.  
  8841.  
  8842.  
  8843.  
  8844.  
  8845.  
  8846.  
  8847.  
  8848.  
  8849.  
  8850.  
  8851.  
  8852.  
  8853.  
  8854.  
  8855.  
  8856.  
  8857.  
  8858.  
  8859.  
  8860.  
  8861.  
  8862.  
  8863.  
  8864.  
  8865.  
  8866.  
  8867.  
  8868.  
  8869.  
  8870.  
  8871.  
  8872.  
  8873.  
  8874.  
  8875.  
  8876. Removing Recursion From Algorithms
  8877.  
  8878.  
  8879. Gary Syck
  8880.  
  8881.  
  8882. Gary Syck is a programmer for Crystal Point in Botholl, Washington. He has 10
  8883. years programming experience, including seven years in C. You can contact him
  8884. at 12032 100th Ave. NE, #D203, Kirkland, WA 98034.
  8885.  
  8886.  
  8887. Recursion is a powerful tool for the algorithm designer. The idea is to break
  8888. a large task down into a number of identical sub-tasks and have the recursive
  8889. routine call itself for each sub-task. This tool simplifies tasks such as
  8890. reading all of the files on an MS-DOS system. Instead of trying to come up
  8891. with a routine that visits all of the sub-directories, you just develop a
  8892. routine to read a single directory that calls itself whenever a directory item
  8893. is a sub-directory. What once was a large task is now a manageable one. In
  8894. this article, I will demonstrate how to create recursive algorithms and then
  8895. convert them to non-recursive routines to avoid some of the problems with
  8896. implementing recursive routines on microcomputers.
  8897. When designing a recursive algorithm, you must first ask yourself if the task
  8898. can be broken down into a number of identical sub-tasks. Next, determine under
  8899. what conditions the recursive routine exits. Knowing these conditions is
  8900. important, since the routine must not only exit from any given instance but
  8901. must also exit from all previous instances. Later in this article you will see
  8902. two examples of how to develop recursive algorithms.
  8903. Recursion loses some of its appeal in actually implementing the routine. Some
  8904. languages do not allow recursion at all. Others such as C give you just enough
  8905. rope to get yourself into trouble. The source of this trouble is often related
  8906. to stack usage. Each time the recursive routine calls itself, it must save any
  8907. registers it uses and the return address on the stack. Then the new instance
  8908. of the routine makes room for a fresh copy of all of its local variables on
  8909. the stack.
  8910. Consider the directory reading example previously mentioned. If the program is
  8911. compiled using the large model, a string pointer argument (the path to read)
  8912. takes eight bytes. This routine uses stack space to hold directory entries (32
  8913. bytes) and miscellaneous variables to keep track of the search (say eight
  8914. bytes each). The total stack usage is 48 bytes for each call. If you call this
  8915. routine from deep within a program that has little stack space to begin with
  8916. (for example a TSR), you will get some interesting errors as the stacks grows
  8917. up into whatever precedes it.
  8918. To show how to design recursive algorithms, this article presents two
  8919. recursive routines. Once the routines are designed and coded in a recursive
  8920. fashion, you can look at how to make them non-recursive to keep stack usage
  8921. under control. After that you can compare the recursive and non-recursive
  8922. versions to determine the possible benefits of this approach.
  8923. The first routine is for searching binary trees. In this case the tree is
  8924. arranged such that the left child of any node is less than the root and the
  8925. right child is greater than the root. The goal of the routine is to locate a
  8926. specific value in the tree. This problem can easily be broken down into
  8927. identical sub-tasks, each of which compares the value to find with a node. If
  8928. the value to find is less than the node, get the left child. Otherwise, get
  8929. the right child. The routine calls itself with each new node, until it finds a
  8930. node without the required child, or it finds one that matches the value.
  8931. Listing 1 shows this algorithm coded in C. There are two cases to check to see
  8932. if the routine should return. The first is if the value being tested matches
  8933. the value for the node. In this case the search is successful and the routine
  8934. returns the node number. The other case is if the required child node does not
  8935. exist. This indicates that the value is not in the tree so the routine returns
  8936. a --1. Each instance of the routine returns the result of the child instances
  8937. until the value is finally returned to the original calling routine.
  8938. The next routine shows how to use recursion to sort a list of items. This is
  8939. the well-known quicksort algorithm. It works by dividing the list into two
  8940. parts. The parts are arranged such that all of the items in the first part are
  8941. less than all of the items in the second part. Then, each of the parts are
  8942. subjected to the same procedure. Eventually, the part is a single item so it
  8943. must be sorted. The use of recursion in this algorithm is obvious: the routine
  8944. need only make the two parts, then call itself twice, once for each part.
  8945. The tricky part of this algorithm is to make the two parts in the first place.
  8946. Many techniques have been proposed, but the actual technique used is not
  8947. important for this article. Listing 2 shows an implementation of the quicksort
  8948. algorithm that uses a technique from Sedgewick.
  8949. Both of these routines handle a difficult problem efficiently. The tree search
  8950. routine can find an item in a large tree with very few compares. The quicksort
  8951. algorithm out-performs many other sort techniques for sorting randomly ordered
  8952. data sets. One bottleneck common to both of these routines is the overhead
  8953. required to do the recursion.
  8954. The tree search routine must do a function call for each node that it visits.
  8955. In addition, once the desired node is found, it must do a number of returns to
  8956. get that information back to the calling routine. Stack usage for this routine
  8957. depends on how "balanced" the tree is. If the tree is perfectly balanced (all
  8958. nodes without children are in the last row), the number of visits to find any
  8959. given node is less than log2 of the size of the tree. For a 65,536 node tree,
  8960. fewer than 16 visits are required. The impact on the stack in this case is
  8961. minimal. On the other hand, in a worst-case unbalanced tree the routine may
  8962. need to visit every node in the tree to find a node. In this case you may need
  8963. more than the available stack space to run this routine.
  8964. Since the quicksort algorithm divides the list into two parts, it has the same
  8965. stack usage properties as the binary tree search. If the routine to divide the
  8966. list into two parts divides the parts evenly, there will not be many recursive
  8967. calls. To make the partitioning routine efficient, it rarely divides the list
  8968. evenly. As with the binary search, this routine may make as many recursive
  8969. calls as there are items in the list.
  8970. Both of these routines can be converted to non-recursive versions that remove
  8971. the overhead of the recursive calls. In both examples, removing recursion
  8972. improves the execution speed of the routine. In the tree search routine it
  8973. also removes the requirement for extra memory for each instance of the
  8974. routine.
  8975. A recursive call accomplishes two tasks. First, it changes the arguments for
  8976. the next instance. Second, it returns to the beginning of the routine. You can
  8977. change the arguments by modifying the formal parameters. In the tree search
  8978. routine this means setting NodeNumb to the number of the next node to look at.
  8979. You can use a goto command to get back to the beginning of the routine.
  8980. Next, the exit condition needs to be changed. The return statements in the
  8981. recursive version of a routine must be changed to a goto to the instruction
  8982. past the recursive call. When there is more than one recursive call, you need
  8983. a flag to show which call to jump past. When this is done to the tree search
  8984. routine, you can easily see the advantages of the non-recursive routine. Since
  8985. there is only one instance, there is no need to pass the final result up
  8986. through previous instances. The routine can immediately return to the caller
  8987. with the result.
  8988. Once the recursive routine has been converted, you may want to examine it to
  8989. see if it can be cleaned up a bit. Listing 3 shows the tree search routine
  8990. after it has been cleaned up. The first step was to notice that the two calls
  8991. could be combined into a single goto with different values in NodeNumb. After
  8992. that change, the routine looked like a while loop that exits with a null node
  8993. or a matching node. Using a while statement gets rid of the gotos and results
  8994. in an elegant non-recursive routine.
  8995. Complications come about when converting the quicksort routine. After the
  8996. recursive call to divide the first half, you have to make another call to
  8997. divide the second half. Of course, the first half, in turn, has a first and
  8998. second half. The question is, "Where do you store the variables that define
  8999. the halves?" The answer is to make a stack, and push the beginning and end of
  9000. each half left to sort onto the stack. The routine is finished when the stack
  9001. is exhausted.
  9002. If you have to make a stack to eliminate recursion, what is the advantage? The
  9003. first advantage is that this stack can hold more halves than the recursive
  9004. stack because there are no stack frames on it. The other advantage is that you
  9005. can monitor the amount of data in the stack and use a different strategy when
  9006. it gets full.
  9007. Listing 4 shows the modified quicksort routine. It pushes the beginning and
  9008. end of each half to sort on the stack, then pops one half off the stack before
  9009. looping back to the beginning. In this case, the loop with gotos resembles a
  9010. do..while loop that exits when there are no items left in the stack. There is
  9011. also logic to determine when the stack is full. If the stack is full, the
  9012. quicksort routine calls a shell sort routine to sort the section.
  9013. In both the tree search routine and the quicksort, removing recursion makes a
  9014. more efficient routine by eliminating time-consuming function calls. In
  9015. addition, it eliminates the danger of stack overflow. If these routines are
  9016. going to be part of a library, you should use the non-recursive versions.
  9017. Some algorithms would be difficult or impossible to discover if recursion were
  9018. not available. The algorithm designer must consider recursion as a possible
  9019. solution for any problem that could be divided into smaller parts. Once a
  9020. recursive algorithm has been found, you can use the techniques demonstrated in
  9021. this article to convert it to a more efficient and less dangerous,
  9022. non-recursive routine.
  9023.  
  9024. Listing 1
  9025. int
  9026. TreeSearch( int NodeNumb, int Value )
  9027. {
  9028. if( NodeNumb = -1 Value == Tree[NodeNumb].Data )
  9029. return NodeNumb;
  9030. if( Value < Tree[NodeNumb].Data )
  9031. return TreeSearch( Tree[NodeNumb].Left );
  9032. else
  9033. return TreeSearch( Tree[NodeNumb].Right );
  9034. }
  9035.  
  9036.  
  9037. Listing 2 Recursive Quicksort Routine
  9038. void
  9039. QuickSort( int *List, int Begin, int End )
  9040. {
  9041. int Value, Tmp, i, j;
  9042.  
  9043. if( End > Begin )
  9044. {
  9045. /* Divide the list in two */
  9046. Value = List[End];
  9047. i = Begin - 1;
  9048. j = End;
  9049. do {
  9050.  
  9051. while( List[++i] < Value );
  9052. while( List[--j] > Value );
  9053. Tmp = List[i];
  9054. List[i] = List[j];
  9055. List[j] = Tmp;
  9056. } while( j > i );
  9057. List[j] = List [i];
  9058. List[i] = List[End];
  9059. List[End] = Tmp;
  9060. /* Sort the first part */
  9061. QuickSort( List, Begin, i - 1 );
  9062. /* Sort the last part */
  9063. QuickSort( List, i + 1, End );
  9064. }
  9065. }
  9066.  
  9067.  
  9068. Listing 3 Non-Recursive Tree Search Routine
  9069. int
  9070. TreeSearch( int NodeNumb, int Value )
  9071. {
  9072. while( NodeNumb != -1 && Value
  9073. ! = Tree[NodeNumb].Data )
  9074. {
  9075. if( Value < Tree[NodeNumb].Data )
  9076. NodeNumb = Tree[NodeNumb].Left;
  9077. else
  9078. NodeNumb = Tree[NodeNumb].Right;
  9079. }
  9080. }
  9081.  
  9082.  
  9083. Listing 4 Non-Recursive Quicksort
  9084. /* Quick sort routine (not recursive) */
  9085.  
  9086. /* Prototypes */
  9087. void QuickSort( int *List, int Begin, int End );
  9088. void ShellSort( int *List, int Begin, int End );
  9089.  
  9090. #define MAXSTACK 200
  9091. int Stack[MAXSTACK];
  9092. int StackPoint;
  9093.  
  9094. void
  9095. QuickSort( int *List, int Begin, int End )
  9096. {
  9097. int Value, Tmp, i, j;
  9098.  
  9099. StackPoint = 2;
  9100. do
  9101. {
  9102. /* Divide the list in two */
  9103. Value = List[End];
  9104. i = Begin - 1;
  9105. j = End;
  9106. do {
  9107. while( List[++i] < Value );
  9108. while( List[--j] > Value );
  9109. Tmp = List[i];
  9110.  
  9111. List[i] = List[j];
  9112. List[j] = Tmp;
  9113. } while( j > i );
  9114. List[j] = List[i];
  9115. List[i] = List[End];
  9116. List[End] = Tmp;
  9117. /* If more than 2 items push the first part */
  9118. if( i - Begin > 2 )
  9119. {
  9120. if( StackPoint >= MAXSTACK )
  9121. ShellSort( List, Begin, i-1 );
  9122. else
  9123. {
  9124. Stack[StackPoint++] = i-1;
  9125. Stack[StackPoint++] = Begin;
  9126. }
  9127. }
  9128. if( End - i > 2)
  9129. {
  9130. if( StackPoint >= MAXSTACK )
  9131. ShellSort( List, i+1, End );
  9132. else
  9133. {
  9134. Stack[StackPoint++] = End;
  9135. Stack[StackPoint++] = i+1;
  9136. }
  9137. }
  9138. Begin = Stack[--StackPoint];
  9139. End = Stack[--StackPoint];
  9140. } while( StackPoint );
  9141. }
  9142.  
  9143. /* Shell sort for when the stack is full */
  9144. void
  9145. ShellSort( int *List, int Begin, int End )
  9146. {
  9147. int i, j, Mid, Value;
  9148.  
  9149. for( Mid = 1; Mid <= End - Begin + 1; )
  9150. Mid = 3 * Mid + 1;
  9151. do {
  9152. Mid /= 3;
  9153. for( i=Mid+1; i <= End - Begin + 1; i++ )
  9154. {
  9155. Value = List[Begin+i];
  9156. j = i;
  9157. while(j > Mid
  9158. && List[Begin+j-Mid] > Value )
  9159. {
  9160. List[Begin+j] = List[Begin+j-Mid];
  9161. j = j - Mid;
  9162. }
  9163. List[Begin+j] = Value;
  9164. }
  9165. } while( Mid != 1 );
  9166. }
  9167.  
  9168.  
  9169.  
  9170.  
  9171.  
  9172.  
  9173.  
  9174.  
  9175.  
  9176.  
  9177.  
  9178.  
  9179.  
  9180.  
  9181.  
  9182.  
  9183.  
  9184.  
  9185.  
  9186.  
  9187.  
  9188.  
  9189.  
  9190.  
  9191.  
  9192.  
  9193.  
  9194.  
  9195.  
  9196.  
  9197.  
  9198.  
  9199.  
  9200.  
  9201.  
  9202.  
  9203.  
  9204.  
  9205.  
  9206.  
  9207.  
  9208.  
  9209.  
  9210.  
  9211.  
  9212.  
  9213.  
  9214.  
  9215.  
  9216.  
  9217.  
  9218.  
  9219.  
  9220.  
  9221.  
  9222.  
  9223.  
  9224.  
  9225.  
  9226.  
  9227.  
  9228.  
  9229.  
  9230.  
  9231.  
  9232.  
  9233.  
  9234. Skip Lists
  9235.  
  9236.  
  9237. Frederick Hegeman
  9238.  
  9239.  
  9240. Frederick Hegeman is an amateur programmer and computer language hobbyist. He
  9241. can be reached at P.O. Box 2368, Rapid City, SD 57709, telephone (605)
  9242. 343-7014.
  9243.  
  9244.  
  9245. Maintaining some sort of ordered list must surely be one of the most common
  9246. problems in programming. An ordered list can be a symbol table, a data
  9247. dictionary or the distribution of periodic readings from some instrument.
  9248. Maintaining an ordered list is also one of the most commonly studied,
  9249. analyzed, refined, and seemingly well understood problems.
  9250. That's why running across a completely novel approach is a bit unnerving, like
  9251. finding a clam with legs. William Pugh's article, "Skip Lists: A Probabilistic
  9252. Alternative To Balanced Trees," in CACM (June 1990), presented a novel data
  9253. structure with characteristics intermediate between those of simple linear
  9254. linked lists and perfectly balanced binary trees. I know a clam with legs when
  9255. I see one.
  9256.  
  9257.  
  9258. Skip Lists
  9259.  
  9260.  
  9261. Skip lists are linked lists with extra pointers that leapfrog, or skip over,
  9262. intermediate nodes during search procedures. Because the extra pointers are
  9263. assigned by consulting a random number generator, search times are independent
  9264. of the ordering of the data input during list construction. Figure 1 diagrams
  9265. a skip list that might be built by the test program with a command line of
  9266. sltest 7 4 2 n x
  9267. If you start searching along the pointer chain marked level one, you are
  9268. obviously searching along a simple one-way linked list, with a NIL pointer
  9269. marking the end of the list. Each key along the chain is examined in turn. If
  9270. the key compares less than the searchkey, you look at the next node. If it
  9271. compares equal, you have found the desired node. If it compares greater than,
  9272. the key you are searching for is not in the list. If you reach the NIL
  9273. pointer, the key is not in the list.
  9274. The strategy for searching the skip list is similar. Find the highest level
  9275. pointer in the list header and follow the pointer chain. If you reach a NIL
  9276. pointer, decrease the level one step. If the level drops to zero, the key is
  9277. not in the list; otherwise, continue along the new chain. If you reach a node
  9278. with a greater key, you've gone too far and have to drop down one more level.
  9279. If the level drops to zero, the key is not in the list; otherwise, continue
  9280. along that chain.
  9281. Pugh provides a great deal of analysis. I've taken a more informal approach.
  9282. First, I build a skip list, and then I take a kind of snapshot of the way it
  9283. looks and behaves.
  9284.  
  9285.  
  9286. Contents Of skiplist.h
  9287.  
  9288.  
  9289. Listing 1, skiplist.h, describes the node structure used to build the lists.
  9290. In order to build the largest possible list, sizeof(NODE) has been kept
  9291. artificially small, by omitting any data members. If you want to use skip
  9292. lists in any real application, you will have to declare a member to hold or
  9293. point to data. The structure is indifferent as to where you put the data
  9294. member(s), as long as the dynamically sized array pointers is the last member
  9295. of the structure.
  9296. Listing 1 also defines several important constants, such as the name of the
  9297. comparison function COMPARE. As written, the functions depend on a COMPARE
  9298. function that conforms to the strcmp()/memcmp() model (returning negative,
  9299. zero, or positive). Integers are used as the keys in the test program, both to
  9300. avoid using memory for storing character strings and to keep comparison times
  9301. to the minimum.
  9302.  
  9303.  
  9304. Skip List Routines
  9305.  
  9306.  
  9307. Listing 2, skiplist.c, contains the actual list maintenance routines. search()
  9308. works exactly as above, returning a pointer to the desired node if successful,
  9309. NIL if not. insert() and delete() are only slightly more complicated, needing
  9310. to preserve the integrity of the pointer chains. In either case, we begin by
  9311. recording all pointers that pass the indicated key. insert() returns
  9312. immediately whenever the key is already in the list. delete() records that
  9313. pointer also. The number of comparisons performed in this step is limited by
  9314. the maximum node level currently in the list. In the case of delete(), one
  9315. additional comparison is necessary to determine if the node actually exists.
  9316. After fetching a new list node or deleting an existing one, the code
  9317. reconstructs pointer chains in a loop governed by the level of the affected
  9318. node.
  9319. The list always begins with a pseudo-member, or header, with space for
  9320. MAXLEVEL pointers and provision for recording the current level of the list.
  9321. newlist() ensures that you have a header node large enough to hold the maximum
  9322. number of pointers that may be allotted. The call to calloc() will initialize
  9323. each potential pointer to NIL. The initial level is set to one, which is the
  9324. minimum. newnode() gets memory for a dynamically sized node and initializes
  9325. the key member of the structure.
  9326. randomlevel() returns an integer from 1 through MAXLEVEL, based on the numbers
  9327. chosen for DMAX (MAXLEVEL) and NMAX (PARTITION). The ratio NMAX/DMAX
  9328. determines the distribution of node levels through the list. For example, if
  9329. DMAX is 4 and NMAX is 2, the function slrandom() is called repeatedly with 4
  9330. as its argument, incrementing the local variable newlevel so long as the
  9331. returned value is 0 or 1. Note that the entire series of consecutive values
  9332. less than NMAX is consumed. The length of the series determines the level.
  9333. Given a random number generator with reasonable performance, the distribution
  9334. will approach the programmer specified ideal where approximately one-half of
  9335. all nodes will have only one pointer, one-fourth will have two pointers,
  9336. one-eighth will have three, and only one-sixteenth of all nodes will have the
  9337. maximum number.
  9338.  
  9339.  
  9340. Random Number Routines
  9341.  
  9342.  
  9343. The pseudo-random number routines are in Listing 3, slrandom.c. The functions
  9344. slrand() and slsrand() and the constant SL_RAND_MAX mimic their ANSI standard
  9345. library counterparts in <stdlib.h>. They are based on code published by the
  9346. ANSI committee, using longs rather than unsigned longs to accommodate
  9347. compilers that do not recognize unsigned long ints. I included them so you
  9348. could reproduce results across compilers and machines. When you're tired of
  9349. the test program, use the standard functions. The function slrandom() scales a
  9350. pseudo-random number returned from the standard function to fit within a
  9351. specified range. Your compiler libraries may already have something similar.
  9352. slrandom() provides a good distribution of values within a range -- other
  9353. schemes may not.
  9354.  
  9355.  
  9356. Test Routines
  9357.  
  9358.  
  9359. Listing 4, sltest.c, contains the code for a simple test program to build and
  9360. examine some skip lists. Five parameters are entered on the command line as
  9361. unsigned integers -- the size of the list to build, the value of MAXLEVEL, the
  9362. value of PARTITION, a seed for the pseudo-random number generator, and a flag
  9363. to choose between random insertion of keys or an ordered insertion. None of
  9364. these routines is very interesting on its own. A skip list of the indicated
  9365. size, or of the largest size allowed by memory, is built as requested. Certain
  9366. calculations are handled most easily by lookup tables, so the maximum size you
  9367. can request is limited to 32,767, which will probably exceed the size of any
  9368. list you can actually build in memory. Once the list is built, each element
  9369. known to be in the list is searched for in its turn. I use the number of
  9370. comparisons performed during a search as the measure of the length of the
  9371. search path. This information is collected, organized, and output to stdout as
  9372. shown in Figure 4.
  9373.  
  9374.  
  9375. Skip Lists
  9376.  
  9377.  
  9378. When you look at Figure 4, start with the line "pointers per level." No node
  9379. has more than seven pointers, far fewer than the maximum allowed. About
  9380. three-fourths of all nodes have only one pointer. The number of nodes with two
  9381. pointers is approximately one-fourth the number at level one. Likewise with
  9382. levels three and four. A little calculation shows that 4,050 pointers (not
  9383. counting 16 pointers in the header) are sufficient to maintain a skip list of
  9384. 3,000 entries -- 1.35 pointers per node. If trees always require two pointers
  9385. per node, the savings is 1,950 pointers -- most probably 3,900 or 7,800 bytes.
  9386. In general, the average number of pointers per node will be 1/((MAXLEVEL --
  9387. PARTITION)/MAX-LEVEL), a constant figure independent of the length of the
  9388. list. The expected figure here would be 1.33 pointers per node. Unless the
  9389. list is very short, you can make a reasonably close calculation of memory
  9390. requirements even though you can't predict the structure of the list.
  9391. The line "comparisons per search" is less interesting in isolation than when
  9392. changing the command line parameters. Note, however, that you would have built
  9393. a degenerate tree with the same input sequence.
  9394. The simple graph is scaled so that the display will fit on a 24-line screen.
  9395. Here, it shows a nice distribution of results about the mean. Very short lists
  9396. can have distributors that are bi- or tri-modal, or even discontinuous. The
  9397. random number routines simply don't cycle long enough to display any "random"
  9398. behavior. The graph of comparisons over a reasonably long list sometimes
  9399. starts looking less than bell-shaped. That tells you that one of the command
  9400. line parameters needs to be adjusted to get better search properties.
  9401. I consider a tree to be perfect when it requires the minimum number of
  9402. comparisons to find each node in the tree. This situation occurs when each
  9403. level of the tree, except, perhaps, the last level, is full. A degenerate tree
  9404. is the same as a linked list. The figures for either are easily calculated and
  9405. presented for comparison.
  9406.  
  9407.  
  9408.  
  9409. Using sltest
  9410.  
  9411.  
  9412. Twiddling the knobs can illustrate a number of things. First, varying only the
  9413. random number seed and the flag parameter should demonstrate that a skip list
  9414. of any reasonable length is indifferent to the order in which it was built.
  9415. Once you're satisfied that this it true, you will find that processes go a
  9416. little faster by always requesting in-order insertion.
  9417. Second, the effect of varying the random number seed decreases as the size of
  9418. the list increases. Building a series of short lists can seem chaotic, but
  9419. only because the sequence of numbers generated never becomes long enough to
  9420. become truly random.
  9421. Third, varying the size of the list while keeping the other parameters
  9422. constant might show some seemingly paradoxical behavior -- the mean number of
  9423. comparisons goes down as the size of the list increases. It's only temporary.
  9424. In general, the mean increases as the size of the list increases, but not
  9425. always continuously. A strategic node is inserted, randomly of course, and
  9426. many search paths are favorably affected. The list grows longer, and the mean
  9427. begins to increase again. Pugh demonstrates that searching a skip list is
  9428. 0(log N) -- as in searching a balanced tree. Intuitively, the cost of
  9429. searching the list increases very slowly relative to the size of the list.
  9430. This is the case if MAXLEVEL and PARTITION are in the proper proportions --
  9431. both to each other and to the size of the list. A little doodling around
  9432. should demonstrate that a ratio greater than one-half makes no sense at all.
  9433. One-half works well but uses an average of two pointers per node. Pugh
  9434. suggests the ratio one-fourth. Empirically, one-fourth seems an ideal balance
  9435. between performance and memory usage, but why this should be a magic number is
  9436. beyond my understanding.
  9437. Tweaking the values for MAXLEVEL while maintaining the same ratio to PARTITION
  9438. is less informative than it might appear. It is interesting to watch
  9439. performance degrade as list size grows and/or MAXLEVEL decreases. Think of the
  9440. distribution of pointers per node. If the distribution was exactly in our
  9441. chosen ratio, we could determine the level where the expected number falls to
  9442. one or below. Take a list of 32,768 elements. If the ratio were one-half, the
  9443. level would be 15. If the ratio were one-fourth, the level would be nine. As
  9444. MAXLEVEL starts to drop below expected levels, pointers begin to accumulate at
  9445. the highest level, and search times increase. You might not have noticed how
  9446. few nodes have been allotted at levels higher than strictly necessary,
  9447. although I tried to hint at it previously. I would suggest that maintaining
  9448. the ratio is more important than saving a few bytes allotted in the header, a
  9449. few more that might or might not be needlessly allocated in one or two rogue
  9450. nodes, and a negligible bit of stack space on insertion or deletion. A level
  9451. of 16 will safely accommodate at least 65,536 nodes -- I would think very hard
  9452. about what kind of memory space that would require -- and leaves more room for
  9453. adjusting PARTITION, where you can preserve serious amounts of memory.
  9454. Varying the value of PARTITION over a constant sized MAXLEVEL has another
  9455. important effect that might not be readily apparent. That ratio, together with
  9456. list size and random chance, has a bearing on the maximum comparisons per
  9457. search. It seems obvious that the maximum should appear to increase as the
  9458. size of the list increases. Changing the random number seed should cause the
  9459. maximum to vary as different lists are built. Changing the ratio should affect
  9460. the maximum as more or less leapfrogging gets done at higher levels. It might
  9461. not be so obvious that as the ratio grows away from one-half, the maximum path
  9462. length grows increasingly sensitive to changes in the random number seed, or
  9463. chance. At the limit of one-sixteenth, the variations can be striking.
  9464. Chance is the wildcard that I have been deliberately ignoring. There is always
  9465. the chance that you might build a degenerate structure like one of those in
  9466. the sidebar. You might as well get the worrying out of your system by using my
  9467. random number routines and running
  9468. sltest 16 3 1 35 1
  9469. You may never see such a situation again. Why this should be so might be
  9470. illustrated by going through the previous paragraph in reverse.
  9471. As the ratio approaches one-half, the volatility of the maximum search path
  9472. lessens. If MAXLEVEL was chosen intelligently, or allowed to default, the
  9473. maximum search path in any list may vary but should remain within a
  9474. comfortable multiple of the mean. This conforms to Pugh's probabilistic
  9475. analysis. Instinctively, also, as the mean increases very slowly in comparison
  9476. to increases in list size, any constant multiple of the mean is also
  9477. increasing very slowly. At the same time, the observed maximum is becoming
  9478. relatively less sensitive to the random number seed. It is behaving rather
  9479. nicely as a multiple of the mean, and as a multiple of what we might guess to
  9480. be the mean of the means, so to speak, of all the lists of that size that are
  9481. being built. The result is a crude demonstration that the behavior is becoming
  9482. more regular as the size of the list increases.
  9483. This seems to me one of the most profound properties of skip lists. The
  9484. probability of worst-case behavior decreases exactly as the consequences of
  9485. such behavior become more severe. And, inversely, the probability of poor
  9486. behavior is greatest exactly at that stage when dealing with it means the
  9487. least. Build a list of 16 elements -- the size of the list is limited; the
  9488. random numbers don't look so random; the algorithms construct a degenerate
  9489. list; it might take 16 comparisons to find a member of the list. So what?
  9490. Build a list of 3,000 elements and the chances can be less than one in one
  9491. hundred million that it might take, not 3,000 comparisons, but as many as 80
  9492. or 90 comparisons. It will not be likely to ever take 3,000.
  9493.  
  9494.  
  9495. Conclusions
  9496.  
  9497.  
  9498. The common choice for managing ordered lists of any real size has usually been
  9499. binary trees. Binary trees are sensitive to the randomness of the keys from
  9500. which they are built, so various schemes for building balanced trees have been
  9501. developed to keep search times from getting out of hand. Search can be much
  9502. faster, but insertion and deletion are much slower. How much slower? Baer and
  9503. Schwab suggest that the number of searches per node should be at least three
  9504. before you consider AVL trees over random trees. They also found that AVL
  9505. trees perform better than other balancing algorithms if only insertions and
  9506. searches are performed.
  9507. Now take as input some arbitrary text, break it down into individual words,
  9508. record each distinct word, keep a count of how often each word appears in the
  9509. input, then output the list of words in alphabetical order, together with each
  9510. word's count of appearances. Sound familiar? Think of what could go wrong. Do
  9511. you really need, or want, to use AVL trees?
  9512. Skip lists are simple and straightforward to implement. In-order traversal is
  9513. an iterative procedure that operates in constant space, unlike recursive
  9514. procedures applied to tree structures, which can corrupt the stack when the
  9515. list is large or highly unbalanced. Insertion and deletion procedures are
  9516. faster than those for AVL trees. Skip lists are space efficient and can yield
  9517. good search performance using an average of 1.33 pointers per node. Although
  9518. skip lists have terrible worst-case search performance, no predictable input
  9519. sequence will create a degenerate structure, and no random sequence of
  9520. insertions and deletions will create such a degenerate structure with any
  9521. significant degree of probability. Performance is sensitive to the size of the
  9522. list, the number of pointers allowed per node, and the fraction used to
  9523. determine when a node is allotted an extra pointer. These, however, are
  9524. subject to tuning by the programmer.
  9525. I cannot recommend Pugh's approach too highly because skip lists have so many
  9526. desirable properties.
  9527.  
  9528.  
  9529. Bibliography
  9530.  
  9531.  
  9532. Pugh, William. "Skip Lists: A Probabilistic Alternative to Balanced Trees."
  9533. Communications Of The ACM. XXXIII, 6, June 1990, Pp. 668-676.
  9534. Baer, J.-L. and Schwab, B. "A Comparison of Tree-Balancing Algorithms."
  9535. Communications Of The ACM. XX, 5, May 1977, Pp. 322- 330.
  9536. Knuth, Donald E. The Art Of Computer Programming: Vol. I, Fundamental
  9537. Algorithms. Reading, MA, Addison-Wesley, 1968. Knuth goes deeply into lists
  9538. and trees, though not, of course, skip lists.
  9539. Skip Lists, Linked Lists And Unpleasant Thoughts
  9540. Simple one-way linked lists might, in general, be built with a data structure
  9541. something like this:
  9542. struct node {
  9543. char *key;
  9544. int data;
  9545. struct node *next;
  9546. };
  9547. It might not be readily apparent that the following is exactly equivalent:
  9548. struct node {
  9549. char *key;
  9550. int data;
  9551. struct node *next[1];
  9552. };
  9553. Turning things on their heads, you could describe the one-way linked list as a
  9554. specialized skip list with MAXLEVEL of 1 and PARTITION of 0, where all nodes
  9555. have pointers at the same level. Therefore searching in the list never
  9556. leapfrogs over any node. In fact, any skip list where every node has the same
  9557. level pointer is an expensive approximation to a linear list. Keep this in
  9558. mind when setting MAXLEVEL. Too small a figure will deform the list and
  9559. degrade performance significantly.
  9560. You might be unfortunate enough to build a replica of Figure 2. It also
  9561. emulates a linear list, even though the distribution of levels throughout the
  9562. list is the same as in Figure 1. You might intuitively guess that degradation
  9563. to a linear list is the limit on how bad things could get. Pugh, himself,
  9564. described worst-case performance as the list where all nodes are level 1. But,
  9565. consider the case of Figure 3. Clams with legs, indeed.
  9566. Nobody would throw out quicksort because it might degrade to the performance
  9567. of bubblesort. With a little thought, that possibility becomes manageable. The
  9568. same applies here.
  9569. Pugh's analysis is elegant and sophisticated. Using his methods, it is
  9570. possible to show that even for relatively short skip lists of around 250 or so
  9571. elements, the chances that a search path will exceed the expected length
  9572. (about 15-16 comparisons in this case) by a factor of three can be about one
  9573. in one million. The probability that the path length will actually resemble
  9574. the list length is calculable but negligible, and grows even more remote as
  9575. the length of the list grows longer.
  9576. Figure 1
  9577. Figure 2
  9578. Figure 3
  9579. Figure 4
  9580. A>sltest 3000 16 4 673 0
  9581.  
  9582. skiplist size = 3000
  9583. pointers per level = 2231 567 145 40 14 1 2 0 0 0 0 0 0 0 0 0
  9584. comparisons per search: mean = 18.131, minimum = 1, maximum = 39
  9585.  
  9586.  *
  9587.  ****
  9588.  *******
  9589.  *********
  9590.  ***********
  9591.  *************
  9592.  ***************
  9593.  *****************
  9594.  ********************
  9595.  ***********************
  9596. ***************************************
  9597. distribution from minimum through maximum: scale = 21:1
  9598. for perfect trees: mean = 10.639, minimum = 1, maximum = 12
  9599. for degenerate trees: mean = 1500.500, minimum = 1, maximum = 3000
  9600.  
  9601. A>
  9602.  
  9603. Listing 1 (skiplist.h)
  9604. /* skiplist.h */
  9605.  
  9606. #ifndef MAXLEVEL
  9607. #define MAXLEVEL 16 /* must be a constant */
  9608. #endif
  9609. #ifndef PARTITION
  9610. #define PARTITION 4 /* probably always a constant */
  9611. #endif
  9612.  
  9613. #ifdef SLTEST
  9614. int intcmp();
  9615. #define KEYTYPE int
  9616. #define COMPARE intcmp
  9617. #define DMAX maxlevel /* the denominator */
  9618. #define NMAX partition /* the numerator */
  9619. #else
  9620. #define DMAX MAXLEVEL
  9621. #define NMAX PARTITION
  9622. #endif
  9623.  
  9624. struct node {
  9625. union {
  9626. KEYTYPE key;
  9627. int level;
  9628. } korl;
  9629. struct node *pointers[1];
  9630. };
  9631.  
  9632. #define NODE struct node
  9633. #define NIL (NODE *)NULL
  9634.  
  9635. #ifndef TRUE
  9636. #define TRUE 1
  9637. #endif
  9638. #ifndef FALSE
  9639. #define FALSE 0
  9640. #endif
  9641.  
  9642. /* declare skiplist.c routines */
  9643. NODE *newlist();
  9644. NODE *search();
  9645.  
  9646. NODE *insert();
  9647. int delete();
  9648. NODE *newnode();
  9649. int randomlevel ();
  9650.  
  9651.  
  9652. Listing 2 (skiplist.c)
  9653. /* skiplist.c */
  9654.  
  9655. #define SLTEST 1
  9656. #include <stdlib.h>
  9657. #include <setjmp.h>
  9658. #include "skiplist.h"
  9659. #ifdef SLTEST
  9660. #define NOMEM 1
  9661. #endif
  9662.  
  9663. /*
  9664. * Skip list routines based on algorithms described
  9665. * by William Pugh, SKIP LISTS: A PROBABILISTIC
  9666. * ALTERNATIVE TO BALANCED TREES. CACM, XXXIII, 6,
  9667. * June, 1990. Pp. 668 -- 676.
  9668. */
  9669.  
  9670. NODE *newlist()
  9671. {
  9672. NODE *temp;
  9673.  
  9674. if(temp = (NODE *)calloc(sizeof(NODE) +
  9675. (sizeof(NODE *) * (DMAX -- 1)), 1))
  9676. {
  9677. temp-->korl.level = 1;
  9678. }
  9679. return(temp);
  9680. }
  9681.  
  9682. NODE *search(searchlist, searchkey)
  9683. NODE *searchlist;
  9684. KEYTYPE searchkey;
  9685. {
  9686. NODE *list, *temp;
  9687. int i, probe;
  9688.  
  9689. list = searchlist;
  9690. for(i = searchlist-->korl.level; ----i >= 0; )
  9691. {
  9692. while(temp = list-->pointers[i])
  9693. {
  9694. if((probe = COMPARE(temp-->korl.key, searchkey))
  9695. < 0)
  9696. {
  9697. list = temp;
  9698. }
  9699. else if(probe == NIL) /* key found */
  9700. return(temp);
  9701. else
  9702. break;
  9703. }
  9704. }
  9705.  
  9706. return(NIL);
  9707. }
  9708.  
  9709. NODE *insert(searchlist, searchkey)
  9710. NODE *searchlist;
  9711. KEYTYPE searchkey;
  9712. {
  9713. NODE *lnode, *tnode;
  9714. int i, newlevel, probe;
  9715. NODE *tempptrs[MAXLEVEL];
  9716.  
  9717. lnode = searchlist;
  9718. for(i = searchlist-->korl.level; ----i >= 0; )
  9719. {
  9720. while(tnode = lnode-->pointers[i])
  9721. {
  9722. if((probe = COMPARE(tnode-->korl.key, searchkey))
  9723. < 0)
  9724. {
  9725. lnode = tnode;
  9726. }
  9727. else if(probe == NIL) /* already present */
  9728. return(tnode);
  9729. else
  9730. break; /* break from while loop */
  9731. }
  9732. tempptrs[i] = lnode;
  9733. }
  9734. /* key not yet present in list ---- insert it */
  9735. newlevel = randomlevel();
  9736. if(newlevel > searchlist-->korl.level)
  9737. {
  9738. for(i = searchlist-->korl.level; i < newlevel; ++i)
  9739. {
  9740. tempptrs[i] = searchlist;
  9741. }
  9742. searchlist-->korl.level = newlevel;
  9743. }
  9744. lnode = newnode(newlevel, searchkey);
  9745. for(i = 0; i < newlevel; ++i)
  9746. {
  9747. lnode-->pointers[i] = tempptrs[i]-->pointers[i];
  9748. tempptrs[i]-->pointers[i] = lnode;
  9749. }
  9750. return(lnode);
  9751. }
  9752.  
  9753. int delete(searchlist, searchkey)
  9754. NODE *searchlist;
  9755. KEYTYPE searchkey;
  9756. {
  9757. NODE *lnode, *tnode;
  9758. int i;
  9759. NODE *tempptrs[MAXLEVEL];
  9760.  
  9761. lnode = searchlist;
  9762. for(i = lnode-->korl.level; ----i >= 0; )
  9763. {
  9764. while((tnode = lnode-->pointers[i])
  9765.  
  9766. && (COMPARE(tnode-->korl.key, searchkey) < 0))
  9767. {
  9768. lnode = tnode;
  9769. }
  9770. tempptrs[i] = lnode;
  9771. }
  9772. tnode = lnode-->pointers[0];
  9773. if(tnode && (COMPARE(tnode-->korl.key, searchkey) == 0))
  9774. {
  9775. lnode = searchlist;
  9776. for(i = 0; i < lnode-->korl.level; ++i)
  9777. {
  9778. if(tempptrs[i]-->pointers[i] != tnode)
  9779. break;
  9780. tempptrs[i]-->pointers[i] = tnode-->pointers[i];
  9781. }
  9782. free(tnode);
  9783. while((i = lnode-->korl.level) > 1
  9784. && lnode-->pointers[i] == NIL)
  9785. {
  9786. lnode-->korl.level = ----i;
  9787. }
  9788. return(TRUE);
  9789. }
  9790. else
  9791. return(FALSE);
  9792. }
  9793.  
  9794. NODE *newnode(nlevel, nkey)
  9795. int nlevel;
  9796. KEYTYPE nkey;
  9797.  
  9798. {
  9799. #ifdef SLTEST
  9800. extern jmp_buf errbuf;
  9801. extern int *emptr;
  9802. extern int nnodes;
  9803. extern int levels[];
  9804. #endif
  9805. NODE *temp;
  9806.  
  9807. temp = (NODE *)malloc(sizeof(NODE)+
  9808. sizeof(NODE *) * (nlevel - 1));
  9809. #ifdef SLTEST
  9810. if(temp == NIL)
  9811. longjmp(errbuf, NOMEM); /* best we can do */
  9812. emptr[nnodes] = nkey; /* record keys as entered */
  9813. nnodes += 1; /* track number allocated */
  9814. levels[nlevel - 1] += 1; /* track distribution */
  9815. #endif
  9816. temp->korl.key = nkey; /* NOTE!!! no error checking */
  9817. return(temp);
  9818. }
  9819.  
  9820. int randomlevel();
  9821. {
  9822. int slrandom();
  9823. int newlevel;
  9824.  
  9825.  
  9826. /*
  9827. * NMAX/DMAX constitute a ratio n/d, such that (d-n)/d
  9828. * of all nodes will have only 1 pointer and n/d of all
  9829. * nodes with pointers at any level N will also have
  9830. * pointers at level N+1
  9831. */
  9832.  
  9833. newlevel = 1;
  9834. while(slrandom(DMAX) < NMAX)
  9835. newlevel += 1;
  9836. if(newlevel > DMAX)
  9837. return(DMAX);
  9838. else
  9839. return(newlevel);
  9840. }
  9841.  
  9842.  
  9843. Listing 3 (random.c)
  9844. /* random.c */
  9845.  
  9846. #define SL_RAND_MAX 32767
  9847.  
  9848. static long next = 1;
  9849.  
  9850. unsigned slrand()
  9851. {
  9852. next = next * 1103515245 + 12345;
  9853. return((unsigned int) ((next / 65536) % 32768));
  9854. }
  9855.  
  9856. slsrand(seed)
  9857. unsigned seed;
  9858. {
  9859. next = seed;
  9860. }
  9861.  
  9862. /* return a random number in the range 0 ..(limit - 1)*/
  9863. int slrandom(limit)
  9864. unsigned limit;
  9865. {
  9866. long num;
  9867.  
  9868. num = (long) slrand();
  9869. return((int) ((num * limit) / (1L + SL_RAND_MAX)));
  9870. }
  9871.  
  9872.  
  9873. Listing 4 (sltest.c)
  9874. /* sltest.c */
  9875.  
  9876. #define SLTEST 1
  9877. #include <stdio.h>
  9878. #include <stdlib.h>
  9879. #include <setjmp.h>
  9880. #include "skiplist.h"
  9881.  
  9882. /* globals */
  9883.  
  9884. int listsize; /* size of our test list */
  9885.  
  9886. int maxlevel = MAXLEVEL;
  9887. int partition;
  9888. int *emptr; /* dynamic array of list elements */
  9889. int nnodes; /* count of nodes allocated */
  9890. long ctotals; /* total number of comparisons */
  9891. int comps; /* temporary counter */
  9892. int mincomps;
  9893. int maxcomps;
  9894. #define COMPMAX 80 /* width of screen */
  9895. int compary[COMPMAX] = { 0 }; /* for graph routine */
  9896. int overflow = 0;
  9897. int levels[MAXLEVEL] = { 0 };
  9898. jmp_buf errbuf; /* short circuit when out of memory */
  9899. NODE *testlist;
  9900. int orderflag; /* non-zero for random insertions */
  9901. #define SL_RAND_MAX 32767
  9902.  
  9903. int intcmp(a, b) /* test comparison routine */
  9904. KEYTYPE a, b;
  9905. {
  9906. comps += 1; /* track length of search path */
  9907. if(a < b)
  9908. return(-1);
  9909. else if(a == b)
  9910. return(0);
  9911. else
  9912. return(1);
  9913. }
  9914.  
  9915. main(argc, argv)
  9916. int argc;
  9917. char *argv[];
  9918. {
  9919. if(argc < 6)
  9920. usage();
  9921. if((listsize = atoi(argv[i])) < 1)
  9922. usage();
  9923. if(listsize > SL_RAND_MAX)
  9924. {
  9925. listsize = SL_RAND_MAX;
  9926. printf("listsize reduced to %d\n", SL_RAND_MAX);
  9927. }
  9928. if((maxlevel = atoi(argv[2])) < 1)
  9929. usage();
  9930. if(maxlevel > MAXLEVEL)
  9931. {
  9932. maxlevel = MAXLEVEL;
  9933. printf("maxlevel reduced to %d\n", MAXLEVEL);
  9934. }
  9935. if((partition = atoi(argv[3])) < 1)
  9936.  (partition >= maxlevel))
  9937. {
  9938. usage();
  9939. }
  9940. slsrand((unsigned) atoi(argv[4]));
  9941. orderflag = atoi(argv[5]);
  9942.  
  9943. list_init();
  9944. list_build();
  9945.  
  9946. list_test();
  9947. list_stats();
  9948. exit(EXIT_SUCCESS);
  9949. }
  9950.  
  9951. usage() /* describe command line errors */
  9952. {
  9953. printf("\nUsage:\t");
  9954. printf("sltest <size> <maxlevel> <partition> <seed> <flag>\n");
  9955. printf("\t<size> of test list (>= 1)\n");
  9956. printf("\t<maxlevel> max number of pointers per node (>= 1)\n");
  9957. printf("\t<partition> (>= 1 && < maxlevel [= %d])\n", maxlevel);
  9958. printf("\t<seed> for the random number generator\n");
  9959. printf("\t<flag> 0 for in-order insertions, otherwise random");
  9960. exit(EXIT_FAILURE);
  9961. }
  9962.  
  9963. list_init()
  9964. {
  9965. if((emptr = (int *) calloc(listsize, sizeof(int)))
  9966. == NULL)
  9967. {
  9968. nomem();
  9969. }
  9970. if((testlist = newlist()) == NIL)
  9971. nomem();
  9972. }
  9973.  
  9974. nomem()
  9975. {
  9976. printf("Not enough memory for a list of size %u\n",
  9977. (unsigned)listsize);
  9978. exit(EXIT_FAILURE);
  9979. }
  9980.  
  9981. list_build()
  9982. {
  9983. unsigned slrand();
  9984. unsigned i;
  9985.  
  9986. if(setjmp(errbuf))
  9987. listsize = nnodes; /* largest possible */
  9988. else if(orderflag == 0)
  9989. {
  9990. for(nnodes = 0, i = 1; i <= listsize; )
  9991. insert(testlist, i++);
  9992. }
  9993. else
  9994. {
  9995. for(nnodes = 0; nnodes < listsize; )
  9996. insert(testlist, slrand());
  9997. }
  9998. }
  9999.  
  10000. list_test()
  10001. {
  10002. int i;
  10003.  
  10004. ctotals = 0L;
  10005.  
  10006. mincomps = listsize;
  10007. maxcomps = 0;
  10008. for(i = 0, comps = 0; i < listsize; ++i, comps = 0)
  10009. {
  10010. search(testlist, emptr[i]);
  10011. ctotals += (long) comps;
  10012. if(comps < mincomps)
  10013. mincomps = comps;
  10014. else if(comps > maxcomps)
  10015. maxcomps = comps;
  10016. if(comps < COMPMAX)
  10017. compary[comps] += 1;
  10018. else
  10019. overflow += 1; /* won't fit on screen */
  10020. }
  10021. }
  10022.  
  10023. /* for computing behavior of balanced trees */
  10024. unsigned treelevels[] = {
  10025. 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
  10026. 11, 12, 13, 14, 15, 16 };
  10027. unsigned powersof2[] = {
  10028. 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048,
  10029. 4096, 8192, 16384, (SL_RAND_MAX + 1) };
  10030. int depth;
  10031. long accumulator;
  10032.  
  10033. list_stats() /* print out statistics */
  10034. {
  10035. int i;
  10036. int scale;
  10037. int temp;
  10038. int cmax;
  10039.  
  10040. printf("\nskiplist size = %d", listsize);
  10041. printf("\npointers per level = ");
  10042. for(i = 0; i < maxlevel; i += 1)
  10043. printf("%d ", levels[i]);
  10044. printf("\ncomparisons per search: ");
  10045. printf("mean = %.3f, minimum = %d, maximum = %d\n",
  10046. ((float)ctotals) / ((float)listsize),
  10047. mincomps, maxcomps);
  10048. if(overflow != 0) /* graph won't fit on screen */
  10049. printf("distribution of comparisons overflows buffer\n");
  10050. else
  10051. {
  10052. cmax = 0;
  10053. for(i = mincomps; i <= maxcomps; i += 1)
  10054. {
  10055. if(compary[i] > cmax)
  10056. cmax = compary[i];
  10057. }
  10058. scale = cmax / 10;
  10059. if(scale == 0) /* watch out for cmax < 10!! */
  10060. scale = 1;
  10061. temp = cmax % 10;
  10062. if((temp / scale) > 5)
  10063. scale += 1;
  10064. for(temp = cmax; temp >= 0; temp -= scale)
  10065.  
  10066. {
  10067. if(temp <= scale) /* last pass through */
  10068. if(scale > 1) /* make sure we */
  10069. temp = 1; /* print all */
  10070. for(i = mincomps;i <= maxcomps; i += 1)
  10071. {
  10072. if(compary[i] /temp)
  10073. printf("*");
  10074. else
  10075. printf(" ");
  10076. }
  10077. printf("\n");
  10078. }
  10079. printf("distribution from minimum through maximum:");
  10080. printf(" scale = %d:1\n", scale);
  10081. }
  10082. for(i = 0; (powersof2[i] - 1) < listsize; i += 1)
  10083. ;
  10084. depth = i--; /* maximum for perfectly balanced tree */
  10085. accumulator = ((long) treelevels[i])
  10086. * ((listsize - powersof2[i--]) + 1);
  10087. white(i >= 0)
  10088. {
  10089. accumulator += (long) (treelevels[i]
  10090. * powersof2[i--]);
  10091. }
  10092. printf("for perfect trees: ");
  10093. printf("mean = %.3f, minimum = 1, maximum = %d/n",
  10094. (((float)accumulator) / ((float)listsize)), depth);
  10095. printf("for degenerate trees: ");
  10096. printf("mean = %.3f, minimum = 1, maximum = %d\n",
  10097. ((float) (listsize + 1) / 2), listsize);
  10098. }
  10099.  
  10100. /* EOF */
  10101.  
  10102.  
  10103.  
  10104.  
  10105.  
  10106.  
  10107.  
  10108.  
  10109.  
  10110.  
  10111.  
  10112.  
  10113.  
  10114.  
  10115.  
  10116.  
  10117.  
  10118.  
  10119.  
  10120.  
  10121.  
  10122.  
  10123.  
  10124.  
  10125.  
  10126.  
  10127.  
  10128.  
  10129. Positioning Nodes For General Trees
  10130.  
  10131.  
  10132. John Q. Walker
  10133.  
  10134.  
  10135. John Q. Walker II manages one of the software development teams implementing
  10136. the SNA communications protocols on OS/2. He joined IBM in 1978, where he was
  10137. a developer and tester for the operating system software of the IBM System/38.
  10138. In Research Triangle Park, NC, John has been an architect for the IBM
  10139. Token-Ring Network, serving as co-editor of the IEEE 802.5 local area network
  10140. standard in 1983 and 1984. John received a B.S., B.A., and M.S. from Southern
  10141. Illinois University and is completing a Ph.D. in computer science at the
  10142. University of North Carolina at Chapel Hill. You may contact John at IBM
  10143. Corp., Dept. E42, Bldg. 673, P.O. Box 12195, Research Triangle Park, NC 27709.
  10144.  
  10145.  
  10146. The bulk of this article first appeared in Software - Practice and Experience,
  10147. July 1990, Copyright 1990 by John Wiley and Sons, Ltd. Reprinted by
  10148. permission.
  10149.  
  10150.  
  10151. Drawing a tree consists of two stages: determining the position of each node,
  10152. and actually rendering the individuals nodes and interconnecting branches. The
  10153. algorithm described here concerns the first stage. That is, given a list of
  10154. nodes, an indication of the hierarchical relationship among them, and their
  10155. shape and size, where should each node be positioned for optimal aesthetic
  10156. effect?
  10157. This algorithm determines the positions of the nodes for an arbitrary general
  10158. tree. The positioning, specified in x, y coordinates, minimizes the width of
  10159. the tree. A general tree may have an unlimited number of offspring per node.
  10160. Binary and ternary trees, on the other hand, can have only two and three
  10161. offspring per node, respectively. This algorithm operates in time 0(N), where
  10162. N is the number of nodes in the tree.
  10163. In textbooks, most tree drawings have been positioned by the sure hand of a
  10164. human graphic designer. Many computer-generated positionings have been either
  10165. trivial or contained irregularities. Earlier work by Wetherell and Shannon [8]
  10166. and Tilford [6], upon which the algorithm builds, failed to correctly position
  10167. the interior nodes of some trees. The algorithm in this article correctly
  10168. positions a tree's nodes in two passes. It also handles several practical
  10169. considerations: alternate orientations of the tree, variable node sizes, and
  10170. out-of-bounds conditions. Radack [2], also building on Tilford's work, solved
  10171. this same problem with an algorithm that makes four passes.
  10172. A history of algorithms leading up to this one is presented in reference [7].
  10173. This algorithm lets a computer reliably generate tree drawings equivalent to
  10174. those done by a skilled human. Below are some of the applications that often
  10175. use tree-drawings.
  10176. Drawings of B-trees and 2-3 trees
  10177. Structure editors that draw trees
  10178. Flow charts without loops
  10179. Visual LISP editors
  10180. Parse trees
  10181. Decision trees
  10182. Hierarchical database models
  10183. Hierarchically-organized file systems (for example, directories,
  10184. subdirectories, and files)
  10185. Organizational charts
  10186. Tables of contents in printed matter
  10187. Biological classifications
  10188.  
  10189.  
  10190. General Trees
  10191.  
  10192.  
  10193. This paper deals with rooted, directed trees, that is, trees with one root and
  10194. hierarchical connections from the root to its offspring. No node may have more
  10195. than one parent.
  10196. Each node in a general tree can have an unlimited number of offspring. A
  10197. general tree is also known as an m-ary tree, since each node can have m
  10198. offspring (where m is 0 or more). As a class, binary trees differ from general
  10199. trees in that an offspring of a binary tree node must be either the left
  10200. offspring or the right offspring. Binary tree drawings preserve this
  10201. left-right distinction. Thus, a single offspring is placed under its parent
  10202. node either to the left or right of its parent's position. This left-right
  10203. distinction does not apply in a general tree. If a node has a single
  10204. offspring, the offspring is placed directly below its parent.
  10205. This algorithm positions a binary tree by ignoring the left-right distinction.
  10206. If a node has exactly one offspring, it is positioned directly below its
  10207. parent. Supowit and Reingold [4] noted that it is NP-hard to optimally
  10208. position minimum-width binary trees (while adhering to the left-right
  10209. distinction) to within a factor of less than about four percent.
  10210.  
  10211.  
  10212. Aesthetic Rules
  10213.  
  10214.  
  10215. Wetherell and Shannon [8] first described a set of aesthetic rules against
  10216. which a good positioning algorithm must be judged. Tilford [6] and Supowit and
  10217. Reingold [4] expanded that list in an effort to produce better algorithms.
  10218. A tidy tree drawing occupies as little space as possible while satisfying
  10219. certain aesthetics:
  10220. 1. Nodes at the same level of the tree should lie along a straight line, and
  10221. the straight lines defining the levels should be parallel [8].
  10222. 2. A parent should be centered over its offspring [8].
  10223. 3. A tree and its mirror image should produce drawings that are reflections of
  10224. one another. Moreover, a subtree should be drawn the same way regardless of
  10225. where it occurs in the tree. In some applications, one wishes to examine large
  10226. trees to find repeated patterns. Searching for patterns is facilitated by
  10227. having isomorphic subtrees drawn isomorphically [3].
  10228. This implies that small subtrees should not appear arbitrarily positioned
  10229. among larger subtrees. Small, interior subtrees should be spaced out evenly
  10230. among larger subtrees (where the larger subtrees are adjacent at one or more
  10231. levels). Small subtrees at the far left or far right should be adjacent to
  10232. larger subtrees.
  10233. The algorithm described in this article satisfies these aesthetic rules.
  10234.  
  10235.  
  10236. How The Algorithm Works
  10237.  
  10238.  
  10239. This algorithm initially assumes that the root of the tree is at the top of
  10240. the drawing [1]. Node-positioning algorithms are concerned only with
  10241. determining the x-coordinates of the nodes. The y-coordinate can be determined
  10242. from its level in the tree, due to Aesthetic 1 and the convention of providing
  10243. a uniform vertical separation between consecutive levels. A later section
  10244. presents a variation for placing the root in other positions.
  10245. This algorithm uses two positioning concepts, the first of which is building
  10246. subtrees as rigid units. Moving a node moves all of its descendants (if it has
  10247. any) -- the entire subtree is thus treated as a rigid unit. A general tree is
  10248. positioned by building it up recursively from its leaves toward its root.
  10249. Second is the concept of using two fields for the positioning of each node.
  10250. They are a preliminary x-coordinate, and a modifier field.
  10251. Two tree traversals are used to produce a node's final x-coordinate. The first
  10252. traversal assigns the preliminary x-coordinate and modifier fields for each
  10253. node. The second traversal computes the final x-coordinate of each node by
  10254. summing the node's preliminary x-coordinate with the modifier fields of all of
  10255. its ancestors. This technique makes moving a large subtree simple and allows
  10256. the algorithm to operate in time 0(N).
  10257.  
  10258. For example, to move a subtree four units to the right, increment both the
  10259. preliminary x-coordinate and the modifier field of the subtree's root by four.
  10260. As another example, the modifier field associated with the tree's apex node is
  10261. used in determining the final position of all of its descendants. (I use the
  10262. term apex node to distinguish the root of the entire tree from the roots of
  10263. individual internal subtrees.)
  10264. The first tree traversal is a postorder traversal, positioning the smallest
  10265. subtrees (the leaves) first and recursively proceeding from left to right to
  10266. build up the position of larger and larger subtrees. Sibling nodes are always
  10267. separated from one another by a predefined minimal distance (the sibling
  10268. separation). Adjacent subtrees are separated by at least a predefined subtree
  10269. separation. Subtrees of a node are formed independently and placed as close
  10270. together as these separation values allow.
  10271. As the tree walk moves from the leaves to the apex, it combines smaller
  10272. subtrees and their root to form a larger subtree. For a given node, its
  10273. subtrees are positioned one-by-one, moving left to right. Imagine that its
  10274. newest subtree has been drawn and cut out of paper along its contour. Now
  10275. superimpose the new subtree atop its neighbor to the left, and move them apart
  10276. until no two points touch. Initially the subtrees' roots are separated by the
  10277. sibling separation value. The roots are then pushed apart until the adjacent
  10278. subtrees at the lower level are separated by the subtree separation value.
  10279. This process continues at successively lower levels until it arrives at the
  10280. bottom of the shorter subtree. Note that the new subtree being superimposed
  10281. may not always bump against a descendant of its nearest sibling to the left.
  10282. Siblings much farther to the left, but with many offspring, may cause the new
  10283. subtree to be pushed to the right. At some levels no movement may be
  10284. necessary, but at no level are subtrees moved closer together. When this
  10285. process is complete for all of the offspring of a node, the node is centered
  10286. over its leftmost and rightmost offspring.
  10287. Pushing a new, large subtree farther and farther to the right may open a gap
  10288. between the large subtree and smaller subtrees that had been positioned
  10289. correctly, but are now bunched on the left with an empty area to their right.
  10290. This undesirable left-to-right gluing was the failing of the algorithms by
  10291. Sweet, Wetherell and Shannon, and Tilford.
  10292. The algorithm presented here produces evenly distributed, proportional spacing
  10293. among subtrees. The distance that a large subtree is moved is apportioned to
  10294. smaller, interior subtrees, satisfying Aesthetic 3. Moving these subtrees is
  10295. accomplished as before -- by adding the proportional values to the preliminary
  10296. x-coordinate and modifier fields of the roots of the small interior subtrees.
  10297. For example, if three small subtrees are bunched at the left because a new
  10298. large subtree has been positioned to the right, the first small subtree is
  10299. shifted right by one-fourth of the gap, the second small subtree is shifted
  10300. right by one-half of the gap, and the third small subtree is shifted right by
  10301. three-quarters of the gap.
  10302. The second tree traversal, a preorder traversal, determines the final
  10303. x-coordinate for each node. It starts at the apex node and sums each node's
  10304. x-coordinate value with the combined sum of the modifier fields of its
  10305. ancestors. The second traversal also adds a value that guarantees centering
  10306. the display with respect to the position of the apex node.
  10307.  
  10308.  
  10309. Changing Root Orientation
  10310.  
  10311.  
  10312. The algorithm allows tree positionings where the apex is not at the top of the
  10313. drawing. One such orientation puts the root on the left and the siblings to
  10314. its right. Four such orientations of the root are the values taken by the
  10315. global constant RootOrientation, set before the algorithm is called.
  10316. NORTH -- root is at the top, as in the preceding algorithm
  10317. SOUTH -- root is at the bottom, its siblings are above it
  10318. EAST -- root is at the right, its siblings are to its left
  10319. WEST -- root is at the left, its siblings are to its right
  10320.  
  10321.  
  10322. An Extended Example
  10323.  
  10324.  
  10325. The algorithm's operation during the two walks can be best illustrated with an
  10326. example (see Figure 1). At least three levels are needed to illustrate its
  10327. operation, since a small subtree must be centered between larger sibling
  10328. subtrees. The following example has fifteen nodes lettered in the order that
  10329. they are visited in the first, postorder traversal. For this example, the mean
  10330. size of each node is two units and the sibling separation and subtree
  10331. separation values are four units.
  10332. In the postorder walk, nodes' preliminary x-coordinate value and modifier
  10333. values are calculated.
  10334. A is a leaf with no left sibling.
  10335. PRELIM(A) = 0
  10336. MODIFIER(A) = 0
  10337. B is also a leaf with no left sibling.
  10338. PRELIM(B) = 0
  10339. MODIFIER(B) = 0
  10340. C is the right sibling of node B and is separated from node B by the sibling
  10341. separation value plus the mean size of the two nodes.
  10342. PRELIM(C) = 0 + 4 + 2 = 6
  10343. MODIFIER(C) = 0
  10344. D is the parent of nodes B and C, and the right sibling of node A. D is
  10345. separated from node A by the sibling separation value plus the mean size of
  10346. the two nodes. D's modifier value is set so that applying it to nodes B and C
  10347. will center them under D. The modifier is determined by subtracting the mean
  10348. of the PRELIM (its mostly widely-separated offspring) values from PRELIM(D).
  10349. PRELIM(D) = 0 + 4 + 2 = 6
  10350. MODIFIER(D) = 6 - (0 + 6)/2 = 3
  10351. E is the parent of nodes A and D, and is centered over nodes A and D.
  10352. PRELIM(E) = (0 + 6)/2 = 3
  10353. MODIFIER(E) = 0
  10354. F is a right sibling of node E, and is separated from E by the sibling
  10355. separation value plus the mean size of the two nodes. That would place it
  10356. directly over node C. You can see now that node N's subtree will later be
  10357. placed much further to the right, leaving the spacing between nodes E and F
  10358. smaller, and hence different, than the spacing between nodes F and N. When
  10359. node N is finally positioned, the position of node F will be adjusted. But for
  10360. now,
  10361. PRELIM(F) = 3 + 4 + 2 = 9
  10362. MODIFIER(F) = 0
  10363. G and H are leaves with no left sibling.
  10364. PRELIM(G) = 0
  10365. MODIFIER(G) = 0
  10366.  
  10367. PRELIM(H) = 0
  10368. MODIFIER(H) = 0
  10369. I is the right sibling of node H. It is separated from H by the sibling
  10370. separation value plus the mean size of the two nodes.
  10371. PRELIM(I) = 0 + 4 + 2 = 6
  10372. MODIFIER(I) = 0
  10373. J is the right sibling of node I. As before, it is separated by the standard
  10374. spacing from node I.
  10375. PRELIM(J) = 6 + 4 + 2 = 12
  10376. MODIFIER(J) = 0
  10377. K is the right sibling of node J, and L is the right sibling of node K.
  10378. PRELIM(K) = 12 + 4 + 2 = 18
  10379. MODIFIER(K) = 0
  10380.  
  10381. PRELIM(L) = 18 + 4 + 2 = 24
  10382. MODIFIER(L) = 0
  10383.  
  10384. M is the parent of nodes H through L, and is the right sibling of node G. It
  10385. is separated from node G by the sibling separation value plus the mean size of
  10386. the two nodes. Its modifier is set so that when it is applied to nodes H
  10387. through L, they will appear centered underneath it.
  10388. PRELIM(M) = 0 + 4 + 2 = 6
  10389. MODIFIER(M) = 6 - (0 + 24)/2 = -6
  10390. N is the parent of nodes G and M, and is the right sibling of node F. N first
  10391. receives its standard positioning to the right of node F, with a modifier that
  10392. reflects the centering of its offspring beneath it.
  10393. PRELIM(N) = 9 + 4 + 2 = 15
  10394. MODIFIER(N) = 15 - (0 + 6)/2 = 12
  10395. Now verify that node E's subtree and node N's subtree are properly separated.
  10396. Move down one level. The leftmost descendant of node N, node G, currently has
  10397. a positioning of 0 + 12 = 12 (PRELIM(G) plus the MODIFIER(N), its parent). The
  10398. rightmost descendant of node E, node D is positioned at 6 + 0 = 6 (PRELIM(D)
  10399. plus the MODIFIER(E), its parent). Their difference is 12 - 6 = 6, which is
  10400. equal to the minimum separation (subtree separation plus mean node size), so
  10401. N's subtree does not need to be moved, since there is no overlap at this
  10402. level.
  10403. Move down another level. The leftmost descendant of node N is node H. It is
  10404. positioned at 0 + -6 + 12 = 6 (PRELIM(H) plus MODIFIER(M) and MODIFIER(N)).
  10405. The rightmost descendant of node E, node C, is positioned at 6 + 3 + 0 = 9
  10406. (PRELIM(C) plus MODIFIER(D) and MODIFIER(E)). Their difference is 6 - 9 = -3;
  10407. it should be 6, the minimum subtree separation plus the mean node size. Thus
  10408. node N and its subtree need to be moved to the right a distance of 6 - -3 = 9.
  10409. PRELIM(N) = 15 + 9 = 24
  10410. MODIFIER(N) = 12 + 9 = 21
  10411. Moving node N and its subtree opens a gap of size nine between sibling nodes E
  10412. and N. This difference must be evenly distributed to all contained sibling
  10413. nodes, and node F is the only one. It is moved to the right a distance of 9/2
  10414. = 4.5.
  10415. PRELIM(F) = 9 + 4.5 = 13.5
  10416. MODIFIER(F) = 0 + 4.5 = 4.5
  10417. 0 is the parent of nodes E, F, and N. It is positioned halfway between the
  10418. position of nodes E and N.
  10419. PRELIM(0) = (3 + 24)/2 = 13.5
  10420. MODIFIER(0) = 0
  10421. All the nodes are visited a second time in a preorder traversal. Their final
  10422. x-coordinates are determined by summing their preliminary x-coordinates with
  10423. the modifier fields of all of their ancestors. (See Table 1.)
  10424.  
  10425.  
  10426. The Algorithm
  10427.  
  10428.  
  10429. Since the algorithm operates by making two recursive walks of the tree,
  10430. several variables are global for the sake of runtime efficiency. These
  10431. variables are described below, alphabetically. All other variables are local
  10432. to their respective procedures and functions.
  10433. pLevelZero: The algorithm maintains a list containing the previous node at
  10434. each level, that is, the adjacent neighbor to the left. pLevelZero is a
  10435. pointer to the first entry in this list.
  10436. xTopAdjustment: A fixed distance used in the final walk of the tree to
  10437. determine the absolute x-coordinate of a node with respect to the apex node of
  10438. the tree.
  10439. yTopAdjustment: A fixed distance used in the final walk of the tree to
  10440. determine the absolute y-coordinate of a node with respect to the apex node of
  10441. the tree.
  10442. The following global constants must be set before the algorithm is called.
  10443. LevelSeparation: The fixed distance between adjacent levels of the tree. Used
  10444. in determining the y-coordinate of a node being positioned.
  10445. MaximumDepth: The maximum number of levels in the tree to be positioned. If
  10446. all levels are to be positioned, set this value to positive infinity (or an
  10447. appropriate numerical value).
  10448. SiblingSeparation: The minimum distance between adjacent siblings of the tree.
  10449. SubtreeSeparation: The minimum distance between adjacent subtrees of a tree.
  10450. For proper aesthetics, this value is normally somewhat larger than
  10451. SiblingSeparation.
  10452. Upon entry to function TreePosition, four hierarchical relationships are
  10453. required for each node. Also, the X and Y coordinates of the apex node are
  10454. required. Upon its successful completion, the algorithm has set the X and Y
  10455. coordinate values for each node in the tree.
  10456. The algorithm is invoked by calling function TreePosition, passing it a
  10457. pointer to the apex node of the tree. If the tree is too wide or too tall to
  10458. be positioned within the coordinate system being used, TreePosition returns
  10459. the boolean FALSE; otherwise it returns TRUE.
  10460. I use the internal tree notation described by Knuth [Reference 1, section
  10461. 2.3.3] for a triply-linked tree. Each node consists of three pointers,
  10462. *parent, *offspring, and *rightsibling, and its information in field *info.
  10463. Thus, if node T is the root of a binary tree, the root of its left subtree is
  10464. T->offspring
  10465. and the root of its right subtree is
  10466. T->offspring->rightsibling
  10467.  
  10468.  
  10469. Acknowledgements
  10470.  
  10471.  
  10472. I appreciate the written correspondence I received from the experts on this
  10473. topic (listed alphabetically): Brad A. Myers at the Computer Systems Research
  10474. Institute in Toronto, Andy Poggio at SRI International, Edward Reingold at the
  10475. University of Illinois, Bob Tarjan at AT&T Bell Laboratories, and C.S.
  10476. Wetherell at AT&T Information Systems. Thanks also to Jane Munn, Jim Staton,
  10477. Bob Gibson, John Broughton III, Steve Joyce, and Dr. Bill Wright at IBM. The
  10478. bulk of this article first appeared in Software -- Practice and Experience,
  10479. July 1990, Copyright 1990 by John Wiley and Sons, Ltd. Reprinted by
  10480. permisison.
  10481. References
  10482. [1] Knuth, D.E., The Art of Computer Programming, Volume 1: Fundamental
  10483. Algorithms, Addison-Wesley, Reading, MA, 1973.
  10484. [2] Radack, G.M., "Tidy Drawing of M-ary Trees," Department of Computer
  10485. Engineering and Science, Case Western Reserve University, Cleveland, Ohio,
  10486. Report CES-88-24, November 1988.
  10487. [3] Reingold, E.M. and J.S. Tilford., "Tidier Drawings of Trees," IEEE
  10488. Transactions on Software Engineering SE-7, 2, March 1981, 223-228.
  10489. [4] Supowit, K.J. and E.M. Reingold, "The complexity of drawing trees nicely,"
  10490. Acta Informatica 18, 4, January 1983, 377-392.
  10491. [5] Sweet, R.E., "Empirical estimates of program entropy." Department of
  10492. Computer Science, Stanford University, Stanford, CA, Report STAN-CS-78-698,
  10493. November 1978. Also issued as Report CSL-78-3, Xerox PARC, Palo Alto, CA,
  10494. September 1978.
  10495. [6] Tilford, J.S., "Tree drawing algorithms." M.S. Thesis, Department of
  10496. Computer Science, University of Illinois, Urbana, IL, Report
  10497. UIUCDCS-R-81-1055, April 1981. Available as document UILU-ENG-81-1711 from the
  10498. College of Engineering Document Center at the Univ. of Illinois.
  10499. [7] Walker II, J. Q., "A Node-Positioning Algorithm for General Trees,"
  10500. Software -- Practice and Experience 10, 1980 553-561.
  10501. [8] Wetherell, C.S. and A. Shannon, "Tidy Drawings of Trees," IEEE
  10502. Transactions on Software Engineering, SE-5, 5 September 1979, 514-520.
  10503. Figure 1
  10504. Table 1
  10505. Node Final X-coordinate
  10506.  (preliminary x-coordinate
  10507.  + modifiers of ancestors)
  10508.  
  10509.  
  10510. O 13.5
  10511. E 3 + 0 = 3
  10512. A 0 + 0 + 0 = 0
  10513. D 6 + 0 + 0 = 6
  10514. B 0 + 3 + 0 + 0 = 3
  10515. C 6 + 3 + 0 + 0 = 9
  10516. F 13.5 + 0 = 13.5
  10517. N 24 + 0 = 24
  10518. G 0 + 21 + 0 = 21
  10519. M 6 + 21 + 0 = 27
  10520. H 0 + -6 + 21 + 0 = 15
  10521. I 6 + -6 + 21 + 0 = 21
  10522. J 12 + -6 + 21 + 0 = 27
  10523. K 18 + -6 + 21 + 0 = 33
  10524. L 24 + -6 + 21 + 0 = 39
  10525.  
  10526. Listing 1
  10527. /***********************************************************
  10528. * Node-Positioning for General Trees, by John Q. Walker II
  10529. *
  10530. * Initiated by calling procedure TreePosition().
  10531. **********************************************************/
  10532.  
  10533. #include <stdlib.h>
  10534.  
  10535. /*----------------------------------------------------------
  10536. * Implementation dependent: Set the values for each of
  10537. * these variables.
  10538. *--------------------------------------------------------*/
  10539. #define NODE_WIDTH 20 /* Width of a node? */
  10540. #define NODE_HEIGHT 10 /* Height of a node? */
  10541. #define FRAME_THICKNESS 1 /* Fixed-sized node frame?*/
  10542. #define SUBTREE_SEPARATION 5 /* Gap between subtrees? */
  10543. #define SIBLING_SEPARATION 4 /* Gap between siblings? */
  10544. #define LEVEL_SEPARATION 5 /* Gap between levels? */
  10545. #define MAXIMUM_DEPTH 10 /* Biggest tree? */
  10546.  
  10547. /*----------------------------------------------------------
  10548. * Implementation dependent: The structure for one node
  10549. * - The first 4 pointers must be set for each node before
  10550. * this algorithm is called.
  10551. * - The X and Y coordinates must be set for only the apex
  10552. * of the tree upon entry; they will be set by this
  10553. * algorithm for all the other nodes.
  10554. * - The next three elements are used only for the duration
  10555. * of the algorithm.
  10556. * - Actual contents of the node depend on your application.
  10557. *--------------------------------------------------------*/
  10558. typedef int COORD; /* X,Y coordinate type */
  10559.  
  10560. typedef struct node {
  10561. struct node *parent; /* ptr: node's parent */
  10562. struct node *offspring; /* ptr: leftmost child */
  10563. struct node *leftsibling; /* ptr: sibling on left */
  10564. struct node *rightsibling; /* ptr: sibling on right */
  10565. COORD xCoordinate; /* node's current x coord */
  10566. COORD yCoordinate; /* node's current y coord */
  10567.  
  10568. struct node *prev; /* ptr: lefthand neighbor */
  10569.  
  10570. float flPrelim; /* preliminary x coord */
  10571. float flModifier; /* temporary modifier */
  10572.  
  10573. char info[80]; /* pick your contents! */
  10574. } *PNODE; /* ptr: a node structure */
  10575.  
  10576. /*----------------------------------------------------------
  10577. * Global variables used by the algorithm
  10578. *--------------------------------------------------------*/
  10579. typedef enum { FALSE, TRUE } BOOL;
  10580. typedef enum { FRAME, NO_FRAME } FRAME_TYPE;
  10581. typedef enum { NORTH, SOUTH, EAST, WEST } ROOT_ORIENTATION;
  10582.  
  10583. typedef struct prev_node { /* one list element */
  10584. PNODE pPreviousNode; /* ptr: previous at level */
  10585. struct prev_node *pNextLevel; /* ptr: next element */
  10586. } *PPREVIOUS_NODE;
  10587.  
  10588. static FRAME_TYPE FrameType = FRAME; /* Show a frame */
  10589. static ROOT_ORIENTATION RootOrientation = NORTH; /* At top*/
  10590. static PPREVIOUS_NODE pLevelZero = (PPREVIOUS_NODE)0;
  10591.  
  10592. static COORD xTopAdjustment; /* How to adjust the apex */
  10593. static COORD yTopAdjustment; /* How to adjust the apex */
  10594. static float flMeanWidth; /* Ave. width of 2 nodes */
  10595.  
  10596. #define FIRST_TIME (0) /* recursive proc flag */
  10597.  
  10598. /*----------------------------------------------------------
  10599. * Implemented as macros, but could be implemented as
  10600. * procedures depending on your particular node structures
  10601. *--------------------------------------------------------*/
  10602.  
  10603. #define FirstChild(node) ((PNODE)((node)->offspring))
  10604. #define LeftSibling(node) ((PNODE)((node)->leftsibling))
  10605. #define RightSibling(node) ((PNODE)((node)->rightsibling))
  10606. #define Parent(node) ((PNODE)((node)->parent))
  10607. #define LeftNeighbor(node) ((PNODE)((node)->prev))
  10608. #define IsLeaf(node) \
  10609. (((node)&&(!((node)->offspring)))?TRUE:FALSE)
  10610. #define HasChild(node) \
  10611. (((node)&&((node)->offspring))?TRUE:FALSE)
  10612. #define HasLeftSibling(node) \
  10613. (((node)&&((node)->leftsibling))?TRUE:FALSE)
  10614. #define HasRightSibling(node) \
  10615. (((node)&&((node)->rightsibling))?TRUE:FALSE)
  10616.  
  10617. static PNODE GetPrevNodeAtLevel (unsigned nLevelNbr)
  10618. {
  10619. /*------------------------------------------------------
  10620. * List Manipulation: Return pointer to previous node at
  10621. * this level
  10622. *----------------------------------------------------*/
  10623. PPREVIOUS_NODE pTempNode; /* used in the for-loop */
  10624. unsigned i = 0; /* level counter */
  10625.  
  10626. for (pTempNode = pLevelZero; (pTempNode);
  10627. pTempNode = pTempNode->pNextLevel) {
  10628. if (i++ == nLevelNbr)
  10629.  
  10630. /* Reached desired level. Return its pointer */
  10631. return (pTempNode->pPreviousNode);
  10632. }
  10633. return ((PNODE)0); /* No pointer yet for this level. */
  10634. }
  10635.  
  10636. static BOOL SetPrevNodeAtLevel (unsigned nLevelNbr,
  10637. PNODE pThisNode)
  10638. {
  10639.  
  10640. /*------------------------------------------------------
  10641. * List Manipulation: Set the list element to the
  10642. * previous node at this level
  10643. *----------------------------------------------------*/
  10644.  
  10645. PPREVIOUS_NODE pTempNode; /* used in the for-loop */
  10646. PPREVIOUS_NODE pNewNode; /* newly-allocated memory */
  10647. unsigned i = 0; /* level counter */
  10648.  
  10649. for (pTempNode = pLevelZero; (pTempNode);
  10650. pTempNode = pTempNode->pNextLevel) {
  10651. if (i++ == nLevelNbr) {
  10652. /* Reached desired level. Return its pointer */
  10653. pTempNode->pPreviousNode = pThisNode;
  10654. return (TRUE);
  10655. }
  10656. else if (pTempNode->pNextLevel==(PPREVIOUS_NODE)0) {
  10657. /* Looks like we need a new level: add it. */
  10658. /* We need to keep going--should be the next. */
  10659. pNewNode = (PPREVIOUS_NODE)
  10660. malloc(sizeof(struct prev_node));
  10661. if (pNewNode) {
  10662. pNewNode->pPreviousNode = (PNODE)0;
  10663. pNewNode->pNextLevel = (PPREVIOUS_NODE)0;
  10664. pTempNode->pNextLevel = pNewNode;
  10665. }
  10666. else return (FALSE); /* The malloc failed. */
  10667. }
  10668. }
  10669.  
  10670. /* Should only get here if pLevelZero is 0. */
  10671. pLevelZero = (PPREVIOUS_NODE)
  10672. malloc(sizeof(struct prev_node));
  10673. if (pLevelZero) {
  10674. pLevelZero->pPreviousNode = pThisNode;
  10675. pLevelZero->pNextLevel = (PPREVIOUS_NODE)0;
  10676. return (TRUE);
  10677. }
  10678. else return (FALSE); /* The malloc() failed. */
  10679. }
  10680.  
  10681. static void InitPrevNodeAtLevel (void)
  10682. {
  10683. /*------------------------------------------------------
  10684. * List Manipulation: Initialize the list of the
  10685. * previous node at each level to all zeros.
  10686. *----------------------------------------------------*/
  10687.  
  10688. PPREVIOUS_NODE pTempNode = pLevelZero; /* the start */
  10689.  
  10690.  
  10691. for ( ; (pTempNode); pTempNode = pTempNode->pNextLevel)
  10692. pTempNode->pPreviousNode = (PNODE)0;
  10693. }
  10694.  
  10695. static BOOL CheckExtentsRange(long lxTemp, long lyTemp)
  10696. {
  10697. /*-----------------------------------------------------
  10698. * Insert your own code here, to check when the
  10699. * tree drawing is going to be too big. My region is no
  10700. * more that 64000 units square.
  10701. *---------------------------------------------------*/
  10702.  
  10703. if ((labs(lxTemp) > 32000) (labs(lyTemp) > 32000))
  10704. return (FALSE);
  10705. else
  10706. return (TRUE);
  10707. }
  10708.  
  10709. static void TreeMeanNodeSize (PNODE pLeftNode,
  10710. PNODE pRightNode)
  10711. {
  10712. /*------------------------------------------------------
  10713. * Write your own code for this procedure if your
  10714. * rendered nodes will have variable sizes.
  10715. *------------------------------------------------------
  10716. * Here I add the width of the contents of the
  10717. * right half of the pLeftNode to the left half of the
  10718. * right node. Since the size of the contents for all
  10719. * nodes is currently the same, this module computes the
  10720. * following trivial computation.
  10721. *----------------------------------------------------*/
  10722.  
  10723. flMeanWidth = (float)0.0; /* Initialize this global */
  10724.  
  10725. switch (RootOrientation) {
  10726. case NORTH:
  10727. case SOUTH:
  10728. if (pLeftNode) {
  10729. flMeanWidth = flMeanWidth +
  10730. (float)(NODE_WIDTH/2);
  10731. if (FrameType != NO_FRAME)
  10732. flMeanWidth = flMeanWidth +
  10733. (float)FRAME_THICKNESS;
  10734. }
  10735. if (pRightNode) {
  10736. flMeanWidth = flMeanWidth +
  10737. (float)(NODE_WIDTH/2);
  10738. if (FrameType != NO_FRAME)
  10739. flMeanWidth = flMeanWidth +
  10740. (float)FRAME_THICKNESS;
  10741. }
  10742. break;
  10743. case EAST :
  10744. case WEST :
  10745. if (pLeftNode) {
  10746. flMeanWidth = flMeanWidth +
  10747. (float)(NODE_HEIGHT/2);
  10748. if (FrameType != NO_FRAME)
  10749.  
  10750. flMeanWidth = flMeanWidth +
  10751. (float)FRAME_THICKNESS;
  10752. }
  10753. if (pRightNode) {
  10754. flMeanWidth = flMeanWidth +
  10755. (float)(NODE_HEIGHT/2);
  10756. if (FrameType != NO_FRAME)
  10757. flMeanWidth = flMeanWidth +
  10758. (float)FRAME_THICKNESS;
  10759. }
  10760. break;
  10761. }
  10762. }
  10763.  
  10764. static PNODE TreeGetLeftmost(PNODE pThisNode,
  10765. unsigned nCurrentLevel,
  10766. unsigned nSearchDepth)
  10767. {
  10768. /*------------------------------------------------------
  10769. * Determine the leftmost descendant of a node at a
  10770. * given depth. This is implemented using a post-order
  10771. * walk of the subtree under pThisNode, down to the
  10772. * level of nSearchDepth. If we've searched to the
  10773. * proper distance, return the currently leftmost node.
  10774. * Otherwise, recursively look at the progressively
  10775. * lower levels.
  10776. *----------------------------------------------------*/
  10777.  
  10778. PNODE pLeftmost; /* leftmost descendant at level */
  10779. PNODE pRightmost; /* rightmost offspring in search */
  10780.  
  10781. if (nCurrentLevel == nSearchDepth)
  10782. pLeftmost = pThisNode; /* searched far enough. */
  10783. else if (IsLeaf(pThisNode))
  10784. pLeftmost = 0; /* This node has no descendants */
  10785. else { /* Do a post-order walk of the subtree. */
  10786. for (pLeftmost = TreeGetLeftmost(pRightmost =
  10787. FirstChild(pThisNode),
  10788. nCurrentLevel + 1,
  10789. nSearchDepth)
  10790.  
  10791. ;
  10792. (pLeftmost==0) && (HasRightSibling(pRightmost))
  10793. ;
  10794. pLeftmost = TreeGetLeftmost(pRightmost =
  10795. RightSibling(pRightmost),
  10796. nCurrentLevel + 1,
  10797. nSearchDepth)
  10798. ) { /* Nothing inside this for-loop. */ }
  10799. }
  10800. return (pLeftmost);
  10801. }
  10802.  
  10803. static void TreeApportion (PNODE pThisNode,
  10804. unsigned nCurrentLevel)
  10805. {
  10806. /*------------------------------------------------------
  10807. * Clean up the positioning of small sibling subtrees.
  10808. * Subtrees of a node are formed independently and
  10809.  
  10810. * placed as close together as possible. By requiring
  10811. * that the subtrees be rigid at the time they are put
  10812. * together, we avoid the undesirable effects that can
  10813. * accrue from positioning nodes rather than subtrees.
  10814. *----------------------------------------------------*/
  10815.  
  10816. PNODE pLeftmost; /* leftmost at given level*/
  10817. PNODE pNeighbor; /* node left of pLeftmost */
  10818. PNODE pAncestorLeftmost; /* ancestor of pLeftmost */
  10819. PNODE pAncestorNeighbor; /* ancestor of pNeighbor */
  10820. PNODE pTempPtr; /* loop control pointer */
  10821. unsigned i; /* loop control */
  10822. unsigned nCompareDepth; /* depth of comparison */
  10823. /* within this proc */
  10824. unsigned nDepthToStop; /* depth to halt */
  10825. unsigned nLeftSiblings; /* nbr of siblings to the */
  10826. /* left of pThisNode, including pThisNode, */
  10827. /* til the ancestor of pNeighbor */
  10828. float flLeftModsum; /* sum of ancestral mods */
  10829. float flRightModsum; /* sum of ancestral mods */
  10830. float flDistance; /* difference between */
  10831. /* where pNeighbor thinks pLeftmost should be */
  10832. /* and where pLeftmost actually is */
  10833. float flPortion; /* proportion of */
  10834. /* flDistance to be added to each sibling */
  10835.  
  10836. pLeftmost = FirstChild(pThisNode);
  10837. pNeighbor = LeftNeighbor(pLeftmost);
  10838.  
  10839. nCompareDepth = 1;
  10840. nDepthToStop = MAXIMUM_DEPTH - nCurrentLevel;
  10841.  
  10842. while ((pLeftmost) && (pNeighbor) &&
  10843. (nCompareDepth <= nDepthToStop)) {
  10844.  
  10845. /* Compute the location of pLeftmost and where it */
  10846. /* should be with respect to pNeighbor. */
  10847. flRightModsum = flLeftModsum = (float)0.0;
  10848. pAncestorLeftmost = pLeftmost;
  10849. pAncestorNeighbor = pNeighbor;
  10850. for (i = 0; (i < nCompareDepth); i++) {
  10851. pAncestorLeftmost = Parent(pAncestorLeftmost);
  10852. pAncestorNeighbor = Parent(pAncestorNeighbor);
  10853. flRightModsum = flRightModsum +
  10854. pAncestorLeftmost->flModifier;
  10855. flLeftModsum = flLeftModsum +
  10856. pAncestorNeighbor->flModifier;
  10857.  
  10858. }
  10859.  
  10860. /* Determine the flDistance to be moved, and apply*/
  10861. /* it to "pThisNode's" subtree. Apply appropriate*/
  10862. /* portions to smaller interior subtrees */
  10863.  
  10864. /* Set the global mean width of these two nodes */
  10865. TreeMeanNodeSize(pLeftmost, pNeighbor);
  10866.  
  10867. flDistance = (pNeighbor->flPrelim +
  10868. flLeftModsum +
  10869.  
  10870. (float)SUBTREE_SEPARATION +
  10871. (float)flMeanWidth) -
  10872. (pLeftmost->flPrelim + flRightModsum);
  10873.  
  10874. if (flDistance > (float)0.0) {
  10875. /* Count the interior sibling subtrees */
  10876. nLeftSiblings = 0;
  10877. for (pTempPtr = pThisNode;
  10878. (pTempPtr) &&
  10879. (pTempPtr != pAncestorNeighbor);
  10880. pTempPtr = Leftsibling(pTempPtr)) {
  10881. nLeftSiblings++;
  10882. }
  10883.  
  10884. if (pTempPtr) {
  10885. /* Apply portions to appropriate */
  10886. /* leftsibling subtrees. */
  10887. flPortion = flDistance/(float)nLeftSiblings;
  10888. for (pTempPtr = pThisNode;
  10889. (pTempPtr != pAncestorNeighbor);
  10890. pTempPtr = LeftSibling(pTempPtr)) {
  10891. pTempPtr->flPrelim =
  10892. pTempPtr->flPrelim + flDistance;
  10893. pTempPtr->flModifier =
  10894. pTempPtr->flModifier + flDistance;
  10895. flDistance = flDistance - flPortion;
  10896. }
  10897. }
  10898. else {
  10899. /* Don't need to move anything--it needs */
  10900. /* to be done by an ancestor because */
  10901. /* pAncestorNeighbor and */
  10902. /* pAncestorLeftmost are not siblings of */
  10903. /* each other. */
  10904. return;
  10905. }
  10906. } /* end of the while */
  10907.  
  10908. /* Determine the leftmost descendant of pThisNode */
  10909. /* at the next lower level to compare its */
  10910. /* positioning against that of its pNeighbor. */
  10911.  
  10912. nCompareDepth++;
  10913. if (IsLeaf(pLeftmost))
  10914. pLeftmost = TreeGetLeftmost(pThisNode, 0,
  10915. nCompareDepth);
  10916. else
  10917. pLeftmost = FirstChild(pLeftmost);
  10918. pNeighbor = LeftNeighbor(pLeftmost);
  10919. }
  10920. }
  10921.  
  10922. static BOOL TreeFirstWalk(PNODE pThisNode,
  10923. unsigned nCurrentLevel)
  10924. {
  10925. /*------------------------------------------------------
  10926. * In a first post-order walk, every node of the tree is
  10927. * assigned a preliminary x-coordinate (held in field
  10928. * node->flPrelim). In addition, internal nodes are
  10929.  
  10930. * given modifiers, which will be used to move their
  10931. * children to the right (held in field
  10932. * node->flModifier).
  10933. * Returns: TRUE if no errors, otherwise returns FALSE.
  10934. *----------------------------------------------------*/
  10935.  
  10936. PNODE pLeftmost; /* left- & rightmost */
  10937. PNODE pRightmost; /* children of a node. */
  10938. float flMidpoint; /* midpoint between left- */
  10939. /* & rightmost children */
  10940.  
  10941. /* Set up the pointer to previous node at this level */
  10942. pThisNode->prev = GetPrevNodeAtLevel(nCurrentLevel);
  10943.  
  10944. /* Now we're it--the previous node at this level */
  10945. if (!(SetPrevNodeAtLevel(nCurrentLevel, pThisNode)))
  10946. return (FALSE); /* Can't allocate element */
  10947.  
  10948. /* Clean up old values in a node's flModifier */
  10949. pThisNode->flModifier = (float)0.0;
  10950.  
  10951. if ((IsLeaf(pThisNode)) 
  10952. (nCurrentLevel == MAXIMUM_DEPTH)) {
  10953. if (HasLeftSibling(pThisNode)) {
  10954.  
  10955. /*--------------------------------------------
  10956. * Determine the preliminary x-coordinate
  10957. * based on:
  10958. * - preliminary x-coordinate of left sibling,
  10959. * - the separation between sibling nodes, and
  10960. * - mean width of left sibling & current node.
  10961. *--------------------------------------------*/
  10962. /* Set the mean width of these two nodes */
  10963. TreeMeanNodeSize(LeftSibling(pThisNode),
  10964. pThisNode);
  10965.  
  10966. pThisNode->flPrelim =
  10967. (pThisNode->Leftsibling->flPrelim) +
  10968. (float)SIBLING_SEPARATION +
  10969. flMeanWidth;
  10970. }
  10971. else /* no sibling on the left to worry about */
  10972. pThisNode->flPrelim = (float)0.0;
  10973. }
  10974. else {
  10975. /* Position the leftmost of the children */
  10976. if (TreeFirstWalk(pLeftmost = pRightmost =
  10977. FirstChild(pThisNode),
  10978. nCurrentLevel + 1)) {
  10979. /* Position each of its siblings to its right */
  10980. while (HasRightSibling(pRightmost)) {
  10981. if (TreeFirstWalk(pRightmost =
  10982. RightSibling(pRightmost),
  10983. nCurrentLevel + 1)) {
  10984. }
  10985. else return (FALSE); /* malloc() failed */
  10986. }
  10987.  
  10988. /* Calculate the preliminary value between */
  10989.  
  10990. /* the children at the far left and right */
  10991. flMidpoint = (pLeftmost->flPrelim +
  10992. pRightmost->flPrelim)/(2.0);
  10993.  
  10994. /* Set global mean width of these two nodes */
  10995. TreeMeanNodeSize(LeftSibling(pThisNode),
  10996. pThisNode);
  10997.  
  10998. if (HasLeftSibling(pThisNode)) {
  10999. pThisNode->flPrelim =
  11000. (pThisNode->leftsibling->flPreLim) +
  11001. (float)SIBLING_SEPARATION +
  11002. flMeanWidth;
  11003. pThisNode->flModifier =
  11004. pThisNode->flPrelim - flMidpoint;
  11005. TreeApportion(pThisNode, nCurrentLevel);
  11006. }
  11007. else pThisNode->flPrelim = flMidpoint;
  11008. }
  11009. else return (FALSE); /* Couldn't get an element */
  11010. }
  11011. return (TRUE);
  11012. }
  11013.  
  11014. static BOOL TreeSecondWalk(PNODE pThisNode,
  11015. unsigned nCurrentLevel)
  11016. {
  11017. /*------------------------------------------------------
  11018. * During a second pre-order walk, each node is given a
  11019. * final x-coordinate by summing its preliminary
  11020. * x-coordinate and the modifiers of all the node's
  11021. * ancestors. The y-coordinate depends on the height of
  11022. * the tree. (The roles of x and y are reversed for
  11023. * RootOrientations of EAST or WEST.)
  11024. * Returns: TRUE if no errors, otherwise returns FALSE.
  11025. *----------------------------------------- ----------*/
  11026.  
  11027. BOOL bResult = TRUE; /* assume innocent */
  11028. long lxTemp, lyTemp; /* hold calculations here */
  11029. float flNewModsum; /* local modifier value */
  11030. static float flModsum = (float)0.0;
  11031.  
  11032. if (nCurrentLevel <= MAXIMUM_DEPTH) {
  11033. flNewModsum = flModsum; /* Save the current value */
  11034. switch (RootOrientation) {
  11035. case NORTH:
  11036. lxTemp = (long)xTopAdjustment +
  11037. (long)(pThisNode->flPrelim + flModsum);
  11038. lyTemp = (long)yTopAdjustment +
  11039. (long)(nCurrentLevel * LEVEL_SEPARATION);
  11040. break;
  11041. case SOUTH:
  11042. lxTemp = (long)xTopAdjustment +
  11043. (long)(pThisNode->flPrelim + flModsum);
  11044. lyTemp = (long)yTopAdjustment -
  11045. (long)(nCurrentLevel * LEVEL_SEPARATION);
  11046. break;
  11047. case EAST:
  11048. lxTemp = (long)xTopAdjustment +
  11049.  
  11050. (long)(nCurrentLevel * LEVEL_SEPARATION);
  11051. lyTemp = (long)yTopAdjustment -
  11052. (long)(pThisNode->flPrelim + flModsum);
  11053. break;
  11054. case WEST:
  11055. lxTemp = (long)xTopAdjustment -
  11056. (long)(nCurrentLevel * LEVEL_SEPARATION);
  11057. lyTemp = (long)yTopAdjustment -
  11058. (long)(pThisNode->flPrelim + flModsum);
  11059. break;
  11060. }
  11061.  
  11062. if (CheckExtentsRange(lxTemp, lyTemp)) {
  11063. /* The values are within the allowable range */
  11064.  
  11065. pThisNode->xCoordinate = (COORD)lxTemp;
  11066. pThisNode->yCoordinate = (COORD)lyTemp;
  11067.  
  11068. if (HasChild(pThisNode)) {
  11069. /* Apply the flModifier value for this */
  11070. /* node to all its offspring. */
  11071. flModsum = flNewModsum =
  11072. flNewModsum + pThisNode->flModifier;
  11073. bResult = TreeSecondWalk(
  11074. FirstChild(pThisNode),nCurrentLevel + 1);
  11075. flNewModsum = flNewModsum -
  11076. pThisNode->flModifier;
  11077. }
  11078.  
  11079. if ((HasRightSibling(pThisNode)) && (bResult)) {
  11080. flModsum = flNewModsum;
  11081. bResult = TreeSecondWalk(
  11082. RightSibling(pThisNode), nCurrentLevel);
  11083. }
  11084. }
  11085. else bResult = FALSE; /* outside of extents */
  11086. }
  11087. return (bResult);
  11088. }
  11089.  
  11090. static BOOL TreePosition(PNODE pApexNode)
  11091. {
  11092. /*------------------------------------------------------
  11093. * Determine the coordinates for each node in a tree.
  11094. * Input: Pointer to the apex node of the tree
  11095. * Assumption: The x & y coordinates of the apex node
  11096. * are already correct, since the tree underneath it
  11097. * will be positioned with respect to those coordinates.
  11098. * Returns: TRUE if no errors, otherwise returns FALSE.
  11099. *----------------------------------------------------*/
  11100.  
  11101. if (pApexNode) {
  11102. /* Initialize list of previous node at each level */
  11103. InitPrevNodeAtLevel();
  11104.  
  11105. /* Generate the properly-placed tree nodes. */
  11106. /* TreeFirstWalk: a post-order walk */
  11107. /* TreeSecondWalk: a pre-order walk */
  11108. if (TreeFirstWalk (pApexNode, FIRST_TIME)) {
  11109.  
  11110. /* Determine how to adjust the nodes with */
  11111. /* respect to the location of the apex of the */
  11112. /* tree being positioned. */
  11113. switch (RootOrientation) {
  11114. case NORTH:
  11115. case SOUTH:
  11116. /* Create the adjustment from x-coord */
  11117. xTopAdjustment = pApexNode->xCoordinate -
  11118. (COORD)(pApexNode->flPrelim);
  11119. yTopAdjustment = pApexNode->yCoordinate;
  11120. break;
  11121. case EAST :
  11122. case WEST :
  11123. /* Create the adjustment from y-coord */
  11124. xTopAdjustment = pApexNode->xCoordinate;
  11125. yTopAdjustment = pApexNode->yCoordinate +
  11126. (COORD)(pApexNode->flPrelim);
  11127. break;
  11128. }
  11129. return (TreeSecondWalk(pApexNode, FIRST_TIME));
  11130. }
  11131. else return (FALSE); /* Couldn't get an element */
  11132. }
  11133. else return (TRUE); /* Easy: null pointer was passed */
  11134. }
  11135.  
  11136.  
  11137.  
  11138.  
  11139.  
  11140.  
  11141.  
  11142.  
  11143.  
  11144.  
  11145.  
  11146.  
  11147.  
  11148.  
  11149.  
  11150.  
  11151.  
  11152.  
  11153.  
  11154.  
  11155.  
  11156.  
  11157.  
  11158.  
  11159.  
  11160.  
  11161.  
  11162.  
  11163.  
  11164.  
  11165.  
  11166.  
  11167.  
  11168.  
  11169.  
  11170.  
  11171.  
  11172.  
  11173. Searching With Skip Lists
  11174.  
  11175.  
  11176. Ken Grogan
  11177.  
  11178.  
  11179. This article is not available in electronic form.
  11180.  
  11181.  
  11182.  
  11183.  
  11184.  
  11185.  
  11186.  
  11187.  
  11188.  
  11189.  
  11190.  
  11191.  
  11192.  
  11193.  
  11194.  
  11195.  
  11196.  
  11197.  
  11198.  
  11199.  
  11200.  
  11201.  
  11202.  
  11203.  
  11204.  
  11205.  
  11206.  
  11207.  
  11208.  
  11209.  
  11210.  
  11211.  
  11212.  
  11213.  
  11214.  
  11215.  
  11216.  
  11217.  
  11218.  
  11219.  
  11220.  
  11221.  
  11222.  
  11223.  
  11224.  
  11225.  
  11226.  
  11227.  
  11228.  
  11229.  
  11230.  
  11231.  
  11232.  
  11233.  
  11234. Removing Duplicate Files Across Disk Drives
  11235.  
  11236.  
  11237. Jerzy Tomasik
  11238.  
  11239.  
  11240. Jerzy Tomasik develops scientific application software for Dynamic Solutions
  11241. Corp. His prior experience includes designing scientific experiments and
  11242. developing data analysis tools in C, Oracle, and RS/1 under MS-DOS and VMS
  11243. operating systems. Mr. Tomasik started programming in FORTRAN as a student in
  11244. Krakow, Poland. You may contact him at 205 S. Redwood Ave. #29, Brea, CA
  11245. 92621.
  11246.  
  11247.  
  11248. All disk drives, regardless of their size, have one thing in common. Sooner
  11249. than you think, they will run out of storage space. Sometimes all the files on
  11250. your hard disk are really necessary, but more often you'll find a lot of files
  11251. you could remove. Some excellent disk maintenance utilities are available, but
  11252. they all lack one function. Few things are more frustrating than finding
  11253. multiple copies of the same file scattered among different subdirectories.
  11254. There are .EXE files that you have compiled, linked, and copied to your
  11255. utilities directory, leaving an extra copy in the development directory. There
  11256. are INSTALL and README files left from software installation that you no
  11257. longer need. TURBO C Professional Development package creates UNPACK.EXE and
  11258. README.COM in three different directories.
  11259. For developers, multiple copies of source files scattered around the disk and
  11260. different projects can be a real nightmare. Maybe you would like to put them
  11261. in a library, but how do you find them all? The problems for a single-user
  11262. disk drive pale in comparison to what you would face if you were responsible
  11263. for a network drive. Invariably, every user will have a personal copy of
  11264. something that could easily be shared. And don't forget that the smallest file
  11265. size is the cluster size -- on my disk a one-byte file occupies 4,096 bytes.
  11266. Multiple copies of a very small file add up quickly.
  11267. Since none of the popular disk maintenance utilities are able to weed out the
  11268. duplicates, I started thinking what I would need to make this task easy. I
  11269. believe in simple, reusable tools, and the power of piping, redirection and
  11270. batch files, but I could not come up with a scheme that would generate output
  11271. appropriate for the task. The MS-DOS DIR command does not even scan
  11272. subdirectories. CHKDSK/V scans subdirectories, but it does not provide any
  11273. file information beyond pathname and name. Third party and shareware directory
  11274. utilities are usually a great improvement over DIR, but they all produce
  11275. output that requires extensive processing. Clearly, I needed a custom program.
  11276.  
  11277.  
  11278. Design
  11279.  
  11280.  
  11281. The general flow of the program is simple:
  11282. read all files on the disk, recursing for subdirectories
  11283. find all duplicate names
  11284. print the duplicates, sorted by date/time and directory name.
  11285. MS-DOS supports hierarchical directory structure, where any directory (except
  11286. the root) has only one parent. Any directory can have zero or more children.
  11287. Both files and subdirectories of any directory are stored in a DOS directory
  11288. structure. (Advanced MS-DOS Programming by Ray Duncan describes DOS internals
  11289. in detail.)
  11290. DOS provides interrupt 21H, functions 4EH and 4FH, for locating directory
  11291. entries. Function 4EH finds the first file, and 4FH finds subsequent files
  11292. matching a wildcard pattern. Your program must communicate with these
  11293. functions through a Data Transfer Area (DTA), which can be set by interrupt
  11294. 21H, function 1AH.
  11295. Microsoft C provides library functions _dos_findfirst and _dos_findnext to
  11296. shelter the programmer from calling assembly routines. (Turbo C calls these
  11297. functions findfirst and findnext.) An additional advantage of calling these
  11298. functions is that _dos_findfirst implicitly sets up the DTA. Subsequent calls
  11299. to _dos_findnext search for files according to what is stored in the DTA.
  11300. After I discuss algorithms for scanning the disk, the role of the DTA will
  11301. become clearer.
  11302. There are two ways to process subdirectories while scanning the disk. When
  11303. reading directory entries you may find files or subdirectories. When you find
  11304. a subdirectory you have two options. Option one requires that you immediately
  11305. descend to that subdirectory and process it, returning to the parent upon
  11306. completion. The program must be able to handle this recursively. An
  11307. alternative way saves new subdirectories, never interrupting processing of the
  11308. current directory. The directories are saved in a structure that contains a
  11309. flag indicating processed directories. Whenever the program completes
  11310. processing of a directory, it marks that directory as processed and searches
  11311. the directory list for another unprocessed directory.
  11312. The first method requires that you save the current DTA, call the
  11313. _dos_findfirst with the new path, process the new directory, and restore the
  11314. DTA when you are done. After you restore the DTA, instead of calling
  11315. _dos_findfirst, continue calling _dos_findnext. Essentially you pick up where
  11316. you left off. The second method relieves you from saving and restoring the
  11317. DTA. This is not difficult with the help of Microsoft or Turbo C libraries,
  11318. but I find it to be too close to the operating system. This method will
  11319. process all directories in the order of nesting, starting with root. It will
  11320. process all first-level sub-directories, then second-level subdirectories,
  11321. continuing until it reaches the deepest level.
  11322. If there is any performance difference between the two methods, it is minimal.
  11323. On my 386/25 machine with an 80Mb drive, the program scans the entire disk
  11324. faster than the Norton Utilities FileFind.
  11325. After choosing the directory processing algorithm, I had to design data
  11326. structure(s), not a trivial task under Intel 80x86 segmented architecture.
  11327. Storing full pathnames with each file (DirEntry structure) is out of the
  11328. question due to size limitations. It is also unnecessary. Each directory entry
  11329. can maintain a pointer to a record in a directory list (DirList structure),
  11330. which allows storage of only a single copy of each pathname. (Note: You could
  11331. further reduce the required storage space by storing only directory name and a
  11332. pointer to the parent, instead of the entire path. With deeply nested
  11333. subdirectories and long pathnames, the gain may be significant.)
  11334. Even without the pathnames, the DirEntry structure occupies 28 bytes under the
  11335. compact memory model. My 80Mb disk drive currently holds about 5,000 files,
  11336. which translates to over 140Kb of RAM, since malloc allocates 28 bytes for
  11337. each structure plus a block header for heap management.
  11338. Notice that the DirEntry will not be stored as a contiguous block in the
  11339. memory, since DirList entries will be interspersed throughout the same memory
  11340. area. malloc allocates space for DirList or DirEntry, and you have no control
  11341. of the allocation order. Whatever is found on the disk gets a chunk of memory.
  11342. Although there are many ways to find multiple records, I chose to sort all the
  11343. records and find identical adjacent records. The library version of qsort
  11344. requires a contiguous array as input. Since the DirEntry records are not
  11345. contiguous, you would have to add pointers to previous and next record to each
  11346. DirEntry, then write a custom qsort.
  11347. Instead of using a doubly linked list for DirEntry and writing my own sort
  11348. routine, I opted for an array of pointers (PtrList) to the DirEntry records.
  11349. In relational terminology, you have two tables, DirList and DirEntry with a
  11350. "one to many" relationship. (For each record in the DirList, you have zero or
  11351. more records in DirEntry, although you actually use the relation in the
  11352. opposite direction, i.e., DirEntry to DirList.) The PtrList is an index for
  11353. the DirEntry table. PtrList can be sorted using the qsort function. All you
  11354. need is a comparison function.
  11355. The PtrList is an array limited to 15,000 files, although you will run out of
  11356. DOS base memory before reaching that limit.
  11357.  
  11358.  
  11359. Language
  11360.  
  11361.  
  11362. I currently use Turbo C at work and Microsoft C at home. Since both are very
  11363. good compilers, I don't have a strong preference. Unfortunately, while both
  11364. compilers are very close to ANSI C, they differ significantly in the MS-DOS
  11365. specific area. The functionality is similar, but function names, argument
  11366. order, structure definitions, and compiler defines are different. Porting a
  11367. program from one to another could prove time consuming. I attempted using a C
  11368. preprocessor to hide the differences.
  11369. This exercise proved to be worthwhile. I managed to hide all the differences
  11370. in a header file (Listing 1). The executable code (Listing 2) contains no
  11371. compiler-specific references. Several solutions I've found are worth passing
  11372. on, since they may save a lot of your time.
  11373. I have seen many listings that laboriously define their own macros with names
  11374. like MICROSOFT_C or TURBO_C. Both compilers have predefined values, which are
  11375. there for you to use. Microsoft defines _MSC_VER and TURBO defines __TURBOC__.
  11376. By using these values you don't have to modify the code or pass a compile-time
  11377. define. This can be extremely valuable if you plan to distribute source code
  11378. supporting more than one compiler.
  11379. Another useful trick is using macros to substitute not only function names but
  11380. also argument order, as done for findfirst and _dos_findfirst. I chose to use
  11381. Turbo nomenclature in the code, but the choice is arbitrary. You may define
  11382. your own names and provide defines for all the compilers you want to support.
  11383. Notice also how structures ffblk and find_t are translated along with their
  11384. members. These defines are a little more dangerous, since they may start
  11385. colliding with your variable names (in this case size and name are likely
  11386. candidates). If you decide to use this technique for larger programs, I
  11387. recommend using a systematic approach to name substitutions. Using this
  11388. systematic approach guarantees that the names you want to translate are
  11389. unique.
  11390. To make your code even more maintainable, you could put compiler-specific
  11391. defines in their own headers, and include them in your program headers as:
  11392. #if defined( _MSC_VER )
  11393. #include <local\msc.h>
  11394. #elif defined( __TURBOC__ )
  11395. #include <local\turboc.h>
  11396. #endif
  11397.  
  11398.  
  11399. Conclusion
  11400.  
  11401.  
  11402. The first programming language I learned was FORTRAN, followed by BASIC. When
  11403. I started learning C, I had difficulties with the pointers. However, my
  11404. difficulties were in seeing what the pointers were used for, even after I
  11405. learned how to use them. Most of the pointer examples in popular C books show
  11406. string functions, and BASIC has great string handling without the need for
  11407. pointers, so I admit I had my doubts. Try to imagine implementing this program
  11408. without pointers and structures. I'm sure it can be done, but readability and
  11409. performance will suffer.
  11410. This program can serve as a skeleton for other custom programs, as it is
  11411. essentially a barebone directory scanning program. You could enhance the
  11412. program by processing wildcards other than *.*, offering multiple disk drive
  11413. support, and/or spawning a user program (e.g., for file comparison). As a more
  11414. advanced exercise, you could add virtual memory support.
  11415.  
  11416.  
  11417. Listing 1 (no_dup.h)
  11418. /*
  11419. Program relies on definitions of _MSC_VER or __TURBOC______LINEEND____
  11420. to account for compiler differences between Turbo C and
  11421. Microsoft C. These values are predefined.
  11422. */
  11423.  
  11424. #if defined( _MSC_VER )
  11425.  
  11426. #include <malloc.h>
  11427. #include <direct.h>
  11428.  
  11429. /* Turbo */ /* Microsoft */
  11430. /* translate structure name and members */
  11431. #define ffblk find_t
  11432. #define ff_reserved reserved
  11433. #define ff_attrib attrib
  11434. #define ff_ftime wr_time
  11435. #define ff_fdate wr_date
  11436. #define ff_fsize size
  11437. #define ff_name name
  11438. /* translate attribute mask defines */
  11439. #define FA_RDONLY _A_RDONLY
  11440. #define FA_HIDDEN _A_HIDDEN
  11441. #define FA_SYSTEM _A_SYSTEM
  11442. #define FA_LABEL _A_VOLID
  11443. #define FA_DIREC _A_SUBDIR
  11444. #define FA_ARCH _A_ARCH
  11445. /*
  11446. use macros to substitute functions and
  11447. swap argument order
  11448. */
  11449. #define findfirst( a , b , c ) _dos_findfirst((a),(c),(b))
  11450. #define findnext( a ) _dos_findnext((a))
  11451.  
  11452. /* end of _MSC_VER */
  11453. #elif defined( __TURBOC__ )
  11454.  
  11455. #include <dir.h>
  11456. #include <alloc.h>
  11457.  
  11458. #endif /* __TURBOC__ */
  11459.  
  11460. #include <dos.h>
  11461. #include <stdio.h>
  11462. #include <stdlib.h>
  11463. #include <string.h>
  11464. #include <time.h>
  11465.  
  11466. /* defines */
  11467. #define VERSION "NO_DUP Ver 1.0, Jerzy Tomasik (c) 1990"
  11468. #define ALL_FILES 0xFFFF /* flag for _dos_find??? */
  11469. #define MAX_LINE 80
  11470. #define MAX_PATH 80
  11471. #define MAX_FLEN 13
  11472. #define MAX_FILES 15000
  11473. #define SEPARATOR "************\n"
  11474. /* data definitions and declarations */
  11475.  
  11476.  
  11477. typedef enum { False, True } Flag;
  11478.  
  11479. /*
  11480. DirEntry structure holds data for all files
  11481. */
  11482. typedef struct DirEntry
  11483. {
  11484. char *path;
  11485. char name[13];
  11486. char attrib;
  11487. unsigned time;
  11488. unsigned date;
  11489. long size;
  11490. Flag dir_processed;
  11491. } DirEntry;
  11492. /*
  11493. DirList structure holds the listing of all
  11494. subdirectories on a disk
  11495. */
  11496. typedef struct DirList
  11497. {
  11498. char pathname[MAX_PATH];
  11499. Flag dir_processed;
  11500. struct DirList *prev;
  11501. struct DirList *next;
  11502. } DirList;
  11503. typedef struct
  11504. {
  11505. Flag dir;
  11506. Flag file;
  11507. unsigned int file_count;
  11508. unsigned long total_file_size;
  11509. } GlobalOpt;
  11510. typedef char *PtrList;
  11511.  
  11512. /* function prototypes */
  11513. DirEntry *get_direntry ( char * );
  11514. DirList *make_path ( char *, char *, DirList * );
  11515. DirList *get_path ( DirList * );
  11516. int name_comp ( const void *, const void * );
  11517. char *datestr ( unsigned d, char *buf );
  11518. char *timestr ( unsigned t, char *buf );
  11519. void fprint_direntry ( FILE *, DirEntry * );
  11520.  
  11521.  
  11522. Listing 2 (no_dup.c)
  11523. /*
  11524. Find multiple copies of the same file on a single disk
  11525. drive. Compiled under Microsoft C 6.0 using:
  11526. cl /Fs /W4 /Ox /AC no_dup.c
  11527. Compiles without modifications undder TURBO C integrated
  11528. environment.
  11529. In both cases requires compact memory model libraries.
  11530. */
  11531.  
  11532. #include "no_dup.h"
  11533.  
  11534. Flag first_call = True;
  11535.  
  11536.  
  11537. GlobalOpt globalopt = { False, False, 0, 0L };
  11538.  
  11539. int main( int argc, char **argv )
  11540. {
  11541. DirEntry *current_file, *next_file;
  11542. DirList *path, *root, *new_path;
  11543. char current_path[MAX_PATH], file_spec[MAX_FLEN];
  11544. PtrList *ptrbase, *ptrlist;
  11545. Flag duplicate = False;
  11546.  
  11547. for( ; argc > 1; argc-- )
  11548. {
  11549. if(argv[argc-1][1] == 'f' argv[argc-1][1] == 'F')
  11550. globalopt.file = True;
  11551. else if( argv[argc-1][1] == 'd'
  11552.  argv[argc-1] [1] == 'D' )
  11553. globalopt.dir = True;
  11554. else
  11555. fprintf( stderr,
  11556. "Invalid command line switch: %s\n",
  11557. argv[argc-l] );
  11558. }
  11559.  
  11560. strcpy( file_spec, "*.*" );
  11561. current_file = NULL;
  11562. (void) getcwd( current_path, MAX_PATH );
  11563. fprintf( stderr, "%s\n", VERSION );
  11564. printf( "Processing %s\n", current_path );
  11565. path = make_path( current_path, "", NULL );
  11566. new_path = root = path;
  11567. if( (ptrbase = malloc( sizeof( PtrList ) * MAX_FILES ))
  11568. == NULL )
  11569. {
  11570. fprintf( stderr, "Insufficient near memory.\n" );
  11571. exit( EXIT_FAILURE );
  11572. }
  11573. ptrlist = ptrbase;
  11574.  
  11575. while( (path = get_path( root ) ) != NULL )
  11576. {
  11577. strcpy( current_path, path->pathname );
  11578. strcat( current_path, file_spec );
  11579. first_call = True;
  11580.  
  11581. while( (current_file = get_direntry(current_path))
  11582. ! = NULL )
  11583. {
  11584. current_file->path = path->pathname;
  11585. if( current_file->attrib & FA_DIREC )
  11586. {
  11587. new_path = make_path( path->pathname,
  11588. current_file->name, new_path );
  11589. }
  11590. else
  11591. {
  11592. ++globalopt.file_count;
  11593. globalopt.total_file_size
  11594. += current_file->size;
  11595.  
  11596. if( globalopt.file_count == MAX_FILES )
  11597. {
  11598. fprintf( stderr,
  11599. "Too many files\n"
  11600. "Program is limited to %u files.\n",
  11601. MAX_FILES );
  11602. exit( EXIT_FAILURE );
  11603. }
  11604. *ptrlist = ( char * ) current_file;
  11605. ++ptrlist;
  11606. }
  11607. }
  11608. path->dir_processed = True;
  11609. }
  11610. *ptrlist = NULL;
  11611. printf( "Processed %d files occupying %lu bytes.\n",
  11612. globalopt.file_count,
  11613. globalopt.total_file_size );
  11614.  
  11615. qsort( (void *) ptrbase,
  11616. (size_t) globalopt.file_count,
  11617. (size_t) sizeof( PtrList ), name_comp );
  11618.  
  11619. if( globalopt.file ) /* optionally list files */
  11620. {
  11621. printf( SEPARATOR );
  11622. printf( "Listing of all files below %s\n",root);
  11623. for( ptrlist = ptrbase; *ptrlist; ptrlist++ )
  11624. fprint_direntry( stdout,
  11625. (DirEntry *) *ptrlist );
  11626. }
  11627.  
  11628. printf( SEPARATOR );
  11629. printf( "Duplicate files below %s\n", root );
  11630. for( ptrlist = ptrbase, duplicate = False;
  11631. *ptrlist; ptrlist++ )
  11632. {
  11633. current_file = ( DirEntry * ) *ptrlist;
  11634. next_file = ( DirEntry * ) *(ptrlist+1);
  11635. if( !strcmp(current_file->name,next_file->name))
  11636. {
  11637. duplicate = True;
  11638. fprint_direntry( stdout, current_file );
  11639. {
  11640. else if( duplicate == True )
  11641. {
  11642. /* print the last file in a group */
  11643. /* otherwise we'll miss it */
  11644. current_file = ( DirEntry * ) *ptrlist;
  11645. fprint_direntry( stdout, current_file );
  11646. printf( SEPARATOR );
  11647. duplicate = False;
  11648. }
  11649. }
  11650.  
  11651. return EXIT_SUCCESS;
  11652. }
  11653.  
  11654. /*
  11655.  
  11656. get file info from DOS directory and allocate memory
  11657. */
  11658. DirEntry *get_direntry( char *path )
  11659. {
  11660. static struct ffblk file_info;
  11661. DirEntry *new_entry;
  11662.  
  11663. if( first_call == True )
  11664. {
  11665. if( findfirst( path, &file_info, FA_DIREC ) )
  11666. return NULL;
  11667. first_call = False;
  11668. }
  11669. else
  11670. {
  11671. if( findnext( &file_info ) )
  11672. {
  11673. return NULL;
  11674. }
  11675. }
  11676. if( (new_entry = malloc(sizeof(DirEntry))) == NULL )
  11677. {
  11678. fprintf( stderr, "Insufficient far memory.\n" );
  11679. exit( EXIT_FAILURE );
  11680. }
  11681.  
  11682. strcpy( new_entry->name, file_info.ff_name );
  11683. new_entry->attrib = file_info.ff_attrib;
  11684. if( new_entry->attrib & FA_DIREC )
  11685. new_entry->dir_processed = False;
  11686. new_entry->time = file_info.ff_ftime;
  11687. new_entry->date = file_info.ff_fdate;
  11688. new_entry->size = file_info.ff_fsize;
  11689.  
  11690. return new_entry;
  11691. }
  11692.  
  11693. /*
  11694. build a directory path by appending subdir to basedir
  11695. */
  11696. DirList *make_path( char *basedir, char *subdir,
  11697. DirList *prev )
  11698. {
  11699. DirList *new_dir;
  11700.  
  11701. if( (new_dir = malloc( sizeof( DirList ) ) ) == NULL )
  11702. {
  11703. fprintf( stderr, "Insufficient near memory.\n" );
  11704. exit( EXIT_FAILURE );
  11705. }
  11706. strcpy( new_dir->pathname, basedir );
  11707. strcat( new_dir->pathname, subdir );
  11708. if( *(new_dir->pathname + strlen(new_dir->pathname) - 1)
  11709. != '\\' )
  11710. strcat( new_dir->pathname, "\\" );
  11711. if( strcmp( subdir, "." ) && strcmp( subdir, ".." ) )
  11712. {
  11713. /* only "real" directories meet the condition */
  11714. new_dir->dir_processed = False;
  11715.  
  11716. if( globalopt.dir )
  11717. printf( "Directory:%s\n", new_dir->pathname );
  11718. }
  11719. else
  11720. new_dir->dir_processed = True;
  11721. if( prev )
  11722. {
  11723. new_dir->prev = prev;
  11724. new_dir->next = NULL;
  11725. prev->next = new_dir;
  11726. }
  11727. else
  11728. {
  11729. new_dir->prev = NULL;
  11730. new_dir->next = NULL;
  11731. }
  11732. return new_dir;
  11733. }
  11734.  
  11735. /*
  11736. find an unprocessed directory
  11737. */
  11738. DirList *get_path( DirList *root )
  11739. {
  11740. DirList *path;
  11741.  
  11742. path = root;
  11743. while( path->next && (path->dir_processed == True) )
  11744. {
  11745. path = path->next;
  11746. }
  11747. if( path->dir_processed == True)
  11748. return NULL;
  11749. else
  11750. return path;
  11751.  
  11752. }
  11753.  
  11754. /*
  11755. DirEntry comparison function
  11756. */
  11757. int name_comp( const void *p1, const void *p2 )
  11758. {
  11759. PtrList *ptr1, *ptr2;
  11760. DirEntry *f1, *f2;
  11761. int status;
  11762.  
  11763. ptr1 = (PtrList *) p1;
  11764. ptr2 = (PtrList *) p2;
  11765. f1 = (DirEntry *) *ptr1;
  11766. f2 = (DirEntry *) *ptr2;
  11767.  
  11768. if( status = strcmp( f1->name, f2->name ))
  11769. return( status );
  11770. else if( status = f1->date - f2->date )
  11771. return( status );
  11772. else if( status = f1->time - f2->time )
  11773. return( status );
  11774. else
  11775.  
  11776. return( strcmp( f1->path, f2->path ) );
  11777. }
  11778.  
  11779. /*
  11780. print file information to the ouptut stream
  11781. */
  11782. void fprint_direntry( FILE *fout, DirEntry *current_file )
  11783. {
  11784. char date_buf[10], time_buf[10];
  11785.  
  11786. fprintf( fout, "%-14s %6ld %c%c %s %s %s\n",
  11787. current_file->name,
  11788. current_file->size,
  11789. (current_file->attrib & FA_RDONLY ) ? 'R' : '.',
  11790. (current_file->attrib & FA_ARCH ) ? 'A' : '.',
  11791. datestr( current_file->date, date_buf ),
  11792. timestr( current_file->time, time_buf ),
  11793. current_file->path );
  11794.  
  11795. }
  11796.  
  11797. /*
  11798. The following functions are copied from QC 2.0 online
  11799. help. These functions convert wr_time and wr_date into
  11800. strings.
  11801. */
  11802. char *timestr( unsigned t, char *buf )
  11803. {
  11804. int h = (t >> 11) & 0x1f, m = (t >> 5) & 0x3f;
  11805. sprintf( buf, "%2.2d:%02.2d", h, m );
  11806. return buf;
  11807. }
  11808.  
  11809. char *datestr( unsigned d, char *buf )
  11810. {
  11811. sprintf( buf, "%2.2d/%02.2d/%02.2d",
  11812. (d >> 5) & 0x0f, d & 0x1f, (d >> 9) + 80 );
  11813. return buf;
  11814. }
  11815.  
  11816.  
  11817.  
  11818.  
  11819.  
  11820.  
  11821.  
  11822.  
  11823.  
  11824.  
  11825.  
  11826.  
  11827.  
  11828.  
  11829.  
  11830.  
  11831.  
  11832.  
  11833.  
  11834.  
  11835.  
  11836.  
  11837.  
  11838.  
  11839. Reviving The UNIX sbrk Function
  11840.  
  11841.  
  11842. David A. Schmitt
  11843.  
  11844.  
  11845. Dave Schmitt was a founder and president of Lattice, the well-known C compiler
  11846. company, from 1983 until 1990. Prior to that, he worked at Bell Telephone
  11847. Laboratories where he was involved in the design of fault-tolerant operating
  11848. systems, including a nonstop version of UNIX. He is currently a free-lance
  11849. author and consultant. You may contact him at Pivot, (708) 469-2235.
  11850.  
  11851.  
  11852. Early versions of UNIX offered C programmers a simple memory allocator using a
  11853. linear heap and two functions named sbrk and brk. (Later systems renamed the
  11854. latter rbrk.) This heap management system is fast and efficient because,
  11855. unlike more sophisticated random heap systems, it requires no overhead to keep
  11856. track of allocated and free blocks.
  11857. A random heap manager usually adds a pointer and an integer to each block. The
  11858. integer contains the block size, and the pointer is used to chain the block
  11859. into a linked list. I've seen some applications in which nearly 20 percent of
  11860. the memory was lost to this overhead information. This typically occurs when
  11861. you need to allocate a lot of very small blocks, such as when building a
  11862. compiler symbol table.
  11863. Nonetheless, a linear heap is not as generally useful as a random heap, so the
  11864. ANSI committee did not include sbrk and rbrk in the standard C library. They
  11865. perpetuated only the UNIX random heap functions named malloc, free, calloc,
  11866. and realloc. As a result, compiler vendors are gradually dropping sbrk and
  11867. rbrk from their libraries.
  11868. Despite its omission from the ANSI standard, the linear heap has advantages in
  11869. some applications. This article shows how you can implement sbrk and rbrk in
  11870. an environment that offers only the ANSI random heap functions.
  11871.  
  11872.  
  11873. Linear Heap Management
  11874.  
  11875.  
  11876. A linear heap is a contiguous memory area divided into allocated and
  11877. unallocated portions as shown in Figure 1. A break pointer contains the
  11878. address of the unallocated portion. It initially points to the beginning
  11879. (i.e., the low address) of the heap. You allocate memory by moving the pointer
  11880. upwards (i.e., by increasing its address), and you free memory by moving the
  11881. pointer downwards. The sbrk function handles both of these operations, as
  11882. shown in Listing 1.
  11883. The first two calls to sbrk allocate a 100-byte block and a 200-byte block,
  11884. assigning the block addresses to pointers p and q, respectively. The third and
  11885. fourth calls free both blocks by telling sbrk to move the break pointer first
  11886. 200 bytes and then 100 bytes in the negative direction. You could combine
  11887. these into a single call moving the break pointer down 300 bytes. Also, you
  11888. can call rbrk to reset the break pointer to its initial position, thereby
  11889. freeing all allocated blocks.
  11890.  
  11891.  
  11892. Linear Heap Applications
  11893.  
  11894.  
  11895. The example in Listing 1 shows the primary disadvantage of the linear heap:
  11896. You must always free blocks in the reverse order that they were allocated. If
  11897. you no longer need the 100-byte block, you cannot release it until you are
  11898. done with all the blocks allocated after it. Indeed, the linear heap manager
  11899. knows nothing about blocks; it merely moves a pointer up and down.
  11900. Despite this disadvantage, the linear heap is well-suited to some
  11901. applications. Suppose you're building a symbol table for a compiler. Each
  11902. symbol is represented by a structure having the format shown in Listing 2.
  11903. When the compiler encounters a new symbol, it builds a SYMBOL structure on the
  11904. stack and then calls a function named savesym to save this information in the
  11905. heap. When the function returns, the compiler links the new SYMBOL structure
  11906. into the appropriate place in an alphabetical list. These structures remain
  11907. allocated throughout the compilation and are all released at the same time
  11908. when the compiler is finished. This is clearly a case where the overhead of a
  11909. random heap is unnecessary.
  11910. To show the difference between the two heap methods, the savesym function can
  11911. be written to use either sbrk or malloc, as shown in Listing 3.
  11912. This function uses sbrk if the symbol LHEAP is defined; otherwise, it uses
  11913. malloc. Note that savesym returns NULL if no space can be allocated. The
  11914. strange if statement after the sbrk call is necessary because sbrk does not
  11915. return NULL when it fails. Instead, it conforms to the original UNIX
  11916. definition by returning the value -1 cast to a pointer.
  11917. The little test program in Listing 4 shows how many symbols we can save under
  11918. various conditions. Figure 2 shows the results of running this test program
  11919. with the linear and random heap, using the Lattice compiler and various symbol
  11920. sizes. The left column indicates the size of the symbol. A size of 8 produces
  11921. a 19-byte SYMBOL structure, while a size of 255 produces a 266-byte structure.
  11922. The percentage column is computed by taking the difference between the sbrk
  11923. and malloc results, divided by the malloc results.
  11924. This test indicates that, given a typical implementation, sbrk is about 25
  11925. percent more space efficient than malloc when allocating small blocks. As the
  11926. block size increases, this advantage gradually disappears.
  11927. Since many programming languages use small symbols of 32 bytes or less, the
  11928. linear heap method can make a noticable difference in compiler memory
  11929. consumption. In general, you should consider using sbrk if your application
  11930. allocates a lot of small blocks that it keeps until termination or until the
  11931. beginning of another processing phase. In such an application, you can also
  11932. get a performance improvement with a linear heap because it allows you to
  11933. release all memory with a single call to rbrk rather than with multiple calls
  11934. to free.
  11935.  
  11936.  
  11937. Implementing A Linear Heap
  11938.  
  11939.  
  11940. If your favorite C compiler doesn't support sbrk and rbrk, you can easily
  11941. implement them on top of malloc and free. The code is shown in Listing 5.
  11942. The operation of sbrk is straightforward. On the first call, it uses malloc to
  11943. get a block whose size is the larger of the requested size and the global
  11944. parameter _LHINCR. Local variables base and size are used to save the base
  11945. address and size of this block, respectively. The variable xbrk is the offset
  11946. of the break location within the block. It is initially set to zero. In other
  11947. words, the current break address is &base[xbrk]. In keeping with the UNIX
  11948. definition of sbrk, a new block is cleared -- memset is called if n is greater
  11949. than 0.
  11950. The rbrk function is even simpler. If a linear heap has been created, it frees
  11951. the block and resets all the local variables. Otherwise, it does nothing.
  11952. Notice that _LHINCR normally causes the linear heap to contain 8 kilobytes.
  11953. You can change this to a different value before the first call to sbrk or
  11954. after calling rbrk. You might wonder why this approach is used rather than
  11955. having sbrk call realloc to expand the linear heap when it cannot honor a
  11956. request. The problem is that realloc may move the heap, thereby invalidating
  11957. the pointers returned by all previous sbrk calls.
  11958.  
  11959.  
  11960. Final Thoughts
  11961.  
  11962.  
  11963. The latest compiler from Microsoft does not provide sbrk and rbrk, so you can
  11964. use the functions from Listing 5 with no problems.
  11965. Borland provides a verison of sbrk that performs about the same as Lattice's.
  11966. However, Borland's C compilers do not have an rbrk function. This is not a big
  11967. problem if you maintain a total of all the space you have allocated via sbrk.
  11968. You can then simulate rbrk by calling sbrk with the negative of this total
  11969. value.
  11970. Watcom and Zortech also provide sbrk but not rbrk. However, you may not want
  11971. to use their linear heaps because of the implementation's overhead. When I ran
  11972. the Listing 4 test program with these two compilers, I discovered that the
  11973. sbrk version of savesym stored fewer symbols than the malloc version.
  11974. This discovery led me to examine the source code that Zortech supplies in
  11975. their Developer's Edition. It turns out that each time you call their sbrk
  11976. function, they invoke DOS to get more memory. Since DOS allocates in multiples
  11977. of 16-byte paragraphs, each sbrk request is rounded up to the next multiple of
  11978. 16. This is considerably more overhead than Zortech's malloc, which rounds
  11979. each request up to the next multiple of four.
  11980. Zortech's implementation of sbrk also violates the spirit of the original UNIX
  11981. version, where the requested size was not rounded up and each allocated block
  11982. was immediately above its predecessor. To be fair, however, Zortech says this
  11983. about sbrk in their manual: "Applications should avoid using it." If that's
  11984. the case, I wonder why they bothered to include it in their library and
  11985. documentation.
  11986. The Watcom version of sbrk behaves so much like Zortech's that I suspect it
  11987. uses a similar strategy, although I did not have access to their library
  11988. source. It's clear that if you can benefit from the linear heap approach, you
  11989. should use the routines from Listing 5 even in the Watcom and Zortech
  11990. environments, which supposedly support this feature. To be sure of linking in
  11991. the correct version, you might want to put a leading underscore in front of
  11992. your sbrk and rbrk routines, or give them completely different names.
  11993. Figure 1 Linear Heap
  11994. Figure 2 Test Results
  11995. SIZE sbrk malloc RATIO
  11996.  
  11997.  
  11998. 8 2964 2325 27.5%
  11999. 12 2448 1993 22.8%
  12000. 16 2085 1743 19.6%
  12001. 20 1816 1550 17.2%
  12002. 24 1609 1395 15.3%
  12003. 28 1444 1268 13.8%
  12004. 32 1309 1162 12.7%
  12005. 128 405 387 4.6%
  12006. 255 211 205 2.9%
  12007.  
  12008. Listing 1 Using srbk
  12009. void *sbrk(int n); // prototype for sbrk
  12010.  
  12011. char *p,*q; // two pointers for allocated
  12012. blocks
  12013. p = (char *)sbrk(100); // allocate a 100-byte block
  12014. q = (char *)sbrk(200); // allocate a 200-byte block
  12015.  
  12016. sbrk(-200); // free the 200-byte block
  12017. sbrk(-100); // free the 100-byte block
  12018.  
  12019.  
  12020. Listing 2 The SYMBOL Structure
  12021. typedef struct _SYMBOL
  12022. {
  12023. struct _SYMBOL *next;// linkage to next symbol
  12024. unsigned long type; // type flags
  12025. unsigned long value; // value
  12026. char symbol[1]; // symbol string (variable size)
  12027. } SYMBOL;
  12028.  
  12029.  
  12030. Listing 3 The savesym Function
  12031. SYMBOL *savesym(SYMBOL *s)
  12032. {
  12033. int size;
  12034. SYMBOL *p;
  12035.  
  12036. size = sizeof (SYMBOL) + strlen(s->symbol);
  12037. #ifdef LHEAP
  12038. p = sbrk(size);
  12039. if(p == (void *)(-1)) return(NULL);
  12040. #else
  12041. p = malloc(size);
  12042. #endif
  12043. if(p) memcpy(p,s,size);
  12044. return(p);
  12045. }
  12046.  
  12047.  
  12048. Listing 4 Test Program
  12049. void main (void)
  12050. {
  12051. char b[32]; // Buffer for user input
  12052. int size; // Symbol size
  12053. int i; // Loop counter
  12054. union // Space for test symbol
  12055. {
  12056. SYMBOL x;
  12057.  
  12058. char y[sizeof(SYMBOL)+255];
  12059. } sym;
  12060.  
  12061. memset(&sym,0,sizeof(sym)); // Clear symbol area
  12062. printf("Symbol size? "); // Get symbol size from user
  12063. gets(b);
  12064. size = atoi(b);
  12065. if(size < 0) size = 0; // Make sure size is in bounds
  12066. if(size > 255) size = 255;
  12067. memset(sym.x.symbol,'X',size); // Set symbol to X's
  12068.  
  12069. for(i = 0; savesym(&sym.x) != NULL; i++); // Allocate all memory
  12070. printf("\n%d symbols allocated\n",i); // Print number of symbols
  12071. }
  12072.  
  12073.  
  12074. Listing 5 The srbk and rbrk Functions
  12075. #include <stdlib.h>
  12076. /**
  12077. *
  12078. * name sbrk -- Adjust linear heap break pointer
  12079. * rbrk -- Reset linear heap
  12080. *
  12081. * synopsis b = sbrk(n);
  12082. * rbrk();
  12083. * void *b; block address
  12084. * int n; number of bytes
  12085. *
  12086. * description This is an implementation of sbrk/rbrk
  12087. * based upon the ANSI memory allocation
  12088. * routines. Note that it uses a public
  12089. * item named _LHINCR to define the
  12090. * default size of the linear heap. This
  12091. * size is used to allocate space via
  12092. * malloc on the first call to sbrk
  12093. * unless the requested size is larger.
  12094. *
  12095. **/
  12096. #define SBRK_ERR (void *)(-1)
  12097. /**
  12098. *
  12099. * Linear heap parameters
  12100. *
  12101. **/
  12102.  
  12103. unsigned _LHINCR = 8192; // allocation size of
  12104. linear heap
  12105.  
  12106. static char *base = 0; // base of linear heap
  12107. static unsigned size; // size of linear heap
  12108. static unsigned xbrk = 0; // current break index
  12109. /**
  12110. *
  12111. * Allocate space from the linear heap.
  12112. *
  12113. **/
  12114. void *sbrk(int n)
  12115. {
  12116. unsigned x;
  12117.  
  12118.  
  12119. if(base == 0)
  12120. {
  12121. if(n <= 0) return(SBRK_ERR);
  12122. size = (n > _LHINCR) ? n : _LHINCR;
  12123. base = malloc(size);
  12124. if(base == 0)
  12125. {
  12126. size = 0;
  12127. return(SBRK_ERR);
  12128. }
  12129. xbrk = 0;
  12130. }
  12131. if(n < 0)
  12132. {
  12133. if((xbrk + n) < 0) return(SBRK_ERR);
  12134. }
  12135. else
  12136. {
  12137. if((xbrk + n) > size) return(SBRK_ERR);
  12138. }
  12139. x = xbrk;
  12140. xbrk += n;
  12141. if(n > 0) memset(&base[x],0,n);
  12142. return(&base[x]);
  12143. }
  12144. /**
  12145. *
  12146. * Reset the linear heap
  12147. *
  12148. **/
  12149. void rbrk(void)
  12150. {
  12151. if(base != 0)
  12152. {
  12153. free(base);
  12154. base = 0;
  12155. size = 0;
  12156. xbrk = 0;
  12157. }
  12158. }
  12159.  
  12160.  
  12161.  
  12162.  
  12163.  
  12164.  
  12165.  
  12166.  
  12167.  
  12168.  
  12169.  
  12170.  
  12171.  
  12172.  
  12173.  
  12174.  
  12175.  
  12176.  
  12177.  
  12178.  
  12179.  
  12180.  
  12181. Automated Software Testing
  12182.  
  12183.  
  12184. Robert McLaughlin
  12185.  
  12186.  
  12187. Robert McLaughlin is Principal Engineer for Check*mate. He has been a C
  12188. programmer for 12 years. Bob has written several articles and is an author of
  12189. "Fix Your Own PC," MIS Press 1990. His interests include collecting
  12190. mathematical puzzles and ocean watching. Mr. McLaughlin can be reached at PRA,
  12191. 1953 Gallows Rd. Suite 350, Vienna VA 22182. (703) 883-2522.
  12192.  
  12193.  
  12194. Software development is generally characterized by a cycle as shown in Figure
  12195. 1. The first step is developing a specification. The second step is validating
  12196. this specification. The third step has two parts that occur at the same time:
  12197. coding based on the specification, and developing a test plan. These two parts
  12198. are generally done by independent groups. The fourth step is validating the
  12199. coding through debugging. The fifth step is validating that the code meets the
  12200. specification. The sixth step is customer acceptance, verifying that the
  12201. delivered product works and meets the customer's interpretation of the
  12202. specification.
  12203. In his paper, "Applying Automation to the Test Process" [1], David Godfrey
  12204. discusses how changes increase in cost exponentially as they are made later in
  12205. the development cycle. The more work put into the specification, the less the
  12206. software will cost, since fewer omissions will have to be corrected. The
  12207. sooner bugs are caught, the less it will cost to fix them. Also, the fewer
  12208. bugs in the delivered software, the greater the customers' confidence that the
  12209. code works, speeding up customer acceptance.
  12210. The process of specification development is human-to-human interaction. No
  12211. program can ever make this process easier. All you can hope for is that the
  12212. specification can be made clearer. Prototyping helps for example, or using an
  12213. abstract language such as ASN.1 [2]. Until users understand the specification
  12214. process enough to ensure that the specification is correct, specification
  12215. development will be the area in which the greatest number of bugs are
  12216. introduced.
  12217. The benefits of automating the testing process are two-fold. It helps ensure
  12218. that the code delivered meets the specification and is free of most bugs.
  12219. Automated testing is not a panacea. It cannot ensure that your code is 100
  12220. percent bug-free, nor can it solve problems caused by a poor specification.
  12221. Automated testing is a program testing a program. If there is a bug in your
  12222. testing program, it will cause good code to be declared bad.
  12223.  
  12224.  
  12225. Automated Testing
  12226.  
  12227.  
  12228. One way to develop and automate a test plan is to use a product called
  12229. Check*mate. Check*mate is a set of Microsoft C library routines that enable
  12230. automated testing of serial telecommunications through two serial ports. The
  12231. product is one of many automated testing packages on the market. Check*mate's
  12232. niche is telecommunications.
  12233. Other testers exist for different applications. I suggest that you examine
  12234. them all before purchasing one. None of these packages are actually
  12235. all-purpose. Because each product has its own market niche, one particular
  12236. product may meet your needs far better than the others.
  12237. Two other automated testing packages I will mention here are Autotester and
  12238. the Atron Evaluator. Autotester, from Software Recording Corp., is a
  12239. playback-capture tool for telecommunications. It captures keystrokes and
  12240. responses to the program under test to allow playback for regression testing.
  12241. The Atron Evaluator is a playback-capture tool used on PCs running OS/2 or
  12242. MS-DOS. The Evaluator works with one PC controlling another PC using special
  12243. hardware. The Evaluator can play back the details of a session, including
  12244. mouse movements. Both packages allow you to edit captured keystroke scripts.
  12245. Autotester also allows a certain amount of branching.
  12246. Regression testing is the process of retesting. Say, for example, you change
  12247. the code that controls the way your program handles queues. Ideally, each
  12248. subroutine that calls the queuing features should be retested. This retesting
  12249. process is called regression testing.
  12250. This can quickly become tiresome. In fact, it is in regression testing that
  12251. programmers take the greatest liberty with "Oh yeah, it works." So automating
  12252. this process is very useful, since it ensures that changes have not introduced
  12253. new bugs.
  12254.  
  12255.  
  12256. Review Of Testing Methods
  12257.  
  12258.  
  12259. A great deal of money is spent on testing, currently over half the budget of
  12260. most large scale projects so a lot of thought has gone into how to improve
  12261. testing.
  12262. In spite of the efforts to improve testing, there is no cure-all. Just as PL/I
  12263. was going to save us all from ourselves 14 years ago, each new idea in testing
  12264. is promoted to save us from ourselves. Computers do only what we tell them to
  12265. do. If our methodology is bad, then no tool will save us from ourselves. If
  12266. the specification process is sloppy, no test procedure will help us deliver
  12267. the code the customer needs.
  12268. Testing should always be done from the standpoint of the customer. In a
  12269. database application your concern may be in file structures. But in the
  12270. customer's mind, bugs are screens that don't ask the right questions or
  12271. reports that are hard to read. In a telecommunications application, you must
  12272. demonstrate that the product conforms to some standard, such as X.25 [3]. You
  12273. can do this by performing tests in accordance with some other standard, such
  12274. as ISO-8882 [4].
  12275.  
  12276.  
  12277. Testing All The Code
  12278.  
  12279.  
  12280. The difficulty that many programmers have with testing is developing a test
  12281. plan. They do not like the idea of someone looking over their shoulder.
  12282. However, a good test plan should be based entirely on the specification and
  12283. should be done by a group independent of the programmers. A test plan will not
  12284. work if the test code is based on the code to be tested or written by those
  12285. that have knowledge of the code that is to be tested. This means that a second
  12286. group parallel to the programmers is required. This second group helps ensure
  12287. that the code meets specification.
  12288. A test plan must both exercise all the features of the program and all the
  12289. segments of the code. This task is not as simple as it sounds. A typical
  12290. feature-based test plan tests only 80 percent of the code. One reason is that
  12291. some of the code is borrowed from other programs and never used in the current
  12292. program. Also, a feature test does not test error conditions and other
  12293. paranoid conditions that programmers write code for.
  12294. The difficulty even with well-tested code is that conditions occur in the
  12295. field that were not tested for. The only way to ensure that the code does not
  12296. break down in an unexpected manner is to use a profiler, such as the UNIX
  12297. utility PROF to ensure that every line is being exercised, that all calls to
  12298. all routines are used. If this is not done then a program that passes a test
  12299. plan may break down in the field because a subroutine was not tested under all
  12300. conditions. This is especially true for routines that handle queuing or link
  12301. lists.
  12302. The development of a test plan is not an easy matter. Several decisions must
  12303. be made. To what extent is the software to be tested? That is, how much
  12304. testing needs to be done to get a feeling that the program works? How
  12305. important is it that features meet the specification exactly? How much
  12306. regression testing must be done to ensure that a bug fix does not introduce
  12307. more bugs?
  12308. With these questions answered, it is possible to devise a test plan. The test
  12309. is generally based on the specification and is designed to allow no more than
  12310. one bug in 1,000 lines of code. Some applications require that bugs occur no
  12311. more frequently than one bug per 1,000,000 lines of code. The allowed bug rate
  12312. and how fuzzily features can match the specification determine the type and
  12313. extent of testing. In telecommunications, the code must have the exact
  12314. features of the specification and generally not more than one bug in 10,000
  12315. lines of code.
  12316.  
  12317.  
  12318. Some Words On Check*mate
  12319.  
  12320.  
  12321. Check*mate is a program within a program. It is composed of two parts. One
  12322. part is an environment that handles I/O and does a certain amount of
  12323. multitasking under MS-DOS. The second part is the user's script.
  12324. The script is a Microsoft C program that is compiled and linked into an .EXE
  12325. file using the standard Microsoft tools. For it to mesh with the environment
  12326. part, it requires an include called cm.h. Also at link time the Check*mate
  12327. libraries must be linked in.
  12328. The use of C gives the tester great power, but this great power can drive
  12329. people mad. For simple regression testing -- testing where keys need to be
  12330. played back and exact responses compared -- Autotester is far better. In a
  12331. situation where different responses can be returned depending on the state of
  12332. the process under test, such as is the case in X.25, then using C makes sense.
  12333. The example of automated testing (Figure 1) is devised more from the view of
  12334. clarity than practical utility.
  12335. I assume the program under test is running on a UNIX machine that can be
  12336. dialed into, and that the program can be run over a modem. It does not matter
  12337. for my purposes where the program under test lives. It does matter that I
  12338. ensure my testing program sets up the serial port correctly, logs into the
  12339. machine, starts the program to be tested running, tests the program, logs off,
  12340. and resets the serial port as required. Since setting up and resetting the
  12341. serial port are common to many tests, I have placed them in an include file.
  12342. For the same reason, I have done the same with logging on and off the target
  12343. machine.
  12344. The customer specification is seen in the box Customer Request. It is short
  12345. and to the point. It does not, however, discuss how the program is to end.
  12346. In reply to the customer request, a Specification is written. It is shown in
  12347. the Specification box. It says what the Customer Request says but in technical
  12348. phrasing. Note that it, too, omits how the program is to end.
  12349. Listing 1 is the program as coded according to the specification. It reads in
  12350. a character. If the character passes islower, it is turned into an uppercase
  12351. letter, using toupper. No matter what, the character is echoed. Since nothing
  12352. ever specified how the program is to end, the program does not end.
  12353. Based on the specification, the quality control department writes a test plan,
  12354. which is shown in the Test Plan box. This may be written in English, as I have
  12355. done here, or in TTCN or some other abstract language. In telecommunications,
  12356. TTCN is the language of choice for test plans. Since the specification says
  12357. nothing about how the program is to end, it does not mention testing if the
  12358. program ends correctly.
  12359. Listing 2 is the Check*mate-based testing program. The testing program is
  12360. bigger and more complex than the program under test, which is often the case.
  12361. This is tolerable because in many testing situations the code is too complex
  12362. for any other means to be reliable. The test code assumes that in the include
  12363. files setups.h and log.h are the routines to set-up the serial port and to log
  12364. on and off the target machine.
  12365. The test code uses three Check*mate routines, TX_chr, RX_chr, and CM_log.
  12366. TX_chr places the character in a transmit queue, Check*mate sends that
  12367. character when it is next up in the queue. RX_chr gets the next character from
  12368. the receive queue. CM_log puts a formatted message in a log file. We will
  12369. examine the log file to see how well the test did. Check*mate includes
  12370. routines to sort through the receive queue for a string, to time response,
  12371. etc.
  12372.  
  12373. Listing 2 goes slightly beyond the test plan. It tests not only to see if a
  12374. lowercase letter is turned into uppercase, but also if other letters are
  12375. echoed back as they were sent -- something implied by the test plan. The
  12376. testing program uses a BREAK to cause the program under test to end. The
  12377. testing program logs a failure by noting that a character returned was not the
  12378. one expected. A count of correctly returned characters is kept to obtain a
  12379. sense of how badly the program failed.
  12380. In the Sample Session (Figure 2), the Log box shows the log that the test
  12381. program would create. The first five lines note that Check*mate was loaded
  12382. under what MS-DOS, with how much free memory, and with how many serial ports.
  12383. The last four lines are the log messages of the test. The first MSG line notes
  12384. that the test has started. The second and third MSG lines note failures. The
  12385. fourth MSG line notes the number of correct characters sent and received. The
  12386. fifth MSG line notes the end of the test.
  12387. The failures are of interest. The first failure is possibly line noise. The
  12388. second failure is clearly a bug. See if you can find it. In general, the test
  12389. should be run several times to ensure that no gremlin has caused the random
  12390. number generator to produce only non-lowercase letters.
  12391.  
  12392.  
  12393. Summary
  12394.  
  12395.  
  12396. I have attempted to demonstrate the wonders and pitfalls of automated testing.
  12397. Automated testing does not lessen the difficult job of program specification
  12398. -- it will not help you when you are sloppy. Since automated testing brings in
  12399. another body of people, quality control, the specification needs to be
  12400. tighter.
  12401. In the example, the failure to find out how the program was to end resulted in
  12402. the writing of a program that could not be cleanly ended, and a testing
  12403. program that would pass it. Clearly the customer did not intend the program to
  12404. run forever. When the code is delivered, the customer will, to the surprise of
  12405. all, reject it. All that automated testing did in this case is reduce the cost
  12406. of testing the code according to the specification.
  12407. Automated testing cannot test your specification process. However, the
  12408. mistakes made in this phase will cost the most to fix if they are not caught
  12409. until the customer acceptance phase.
  12410. In this simple testing program, more that 1,000 tests of the program were
  12411. done. Imagine sitting at a keyboard and typing in 1,000 random letters in less
  12412. than five minutes. Imagine doing this several times in a row. Imagine finding
  12413. a bug, and having to do it all over again.
  12414. Automated testing will allow greater assurance at a reasonable cost that your
  12415. code meets specification with minimum bugs. It does not help you assure that
  12416. the specification meets the customer needs. This area is the next frontier in
  12417. software engineering.
  12418. References
  12419. [1] David Godfrey, "Applying Automation to the Test Process," Proceedings of
  12420. the 6th International Conference on Testing Computer Software, US Professional
  12421. Development Institute, Silver Spring MD, 1989.
  12422. [2] CCITT Blue Book. X.208 and X.209.
  12423. [3] CCITT Blue Book. X.25
  12424. [4] ISO Draft Standard 8882.
  12425. CHE01 Check*mate Users Guide.
  12426. Figure 1
  12427.  Customer Request
  12428.  
  12429.  A way to echo
  12430.  small letters as
  12431.  capitals.
  12432.  
  12433.  Specification
  12434.  
  12435.  We will write a C program that will read a
  12436.  character from a terminal device and echo
  12437.  back to the terminal device a capital when
  12438.  small letters are detected. Printable
  12439.  characters other than small letters will be echoed
  12440.  back unchanged.
  12441.  
  12442.  Test Plan
  12443.  
  12444. Randomly generate characters. Pass if printable
  12445. characters other than small letters are echoed back
  12446. exactly as sent. Pass if small letters are echoed back
  12447. as capitals. Fail if other occurs.
  12448. Figure 2
  12449. 12:00:00.00 SYS \BIN\CM.EXE
  12450. 12:00:00.05 SYS 073190: Checkmate 1.1 loaded
  12451.  under DOS 3.3
  12452. 12:00:00:10 SYS 640KB total memory, 140KB
  12453.  available memory
  12454. 12:00:00.15 SYS COM1 present
  12455. 12:00:00.20 SYS COM2 present
  12456. 12:00:03.11 MSG Start test of lower-to-upper
  12457. 12:03:14.03 MSG Sent q, received ~. -- FAILURE
  12458. 12:04:01.53 MSG Sent z, received z. -- FAILURE
  12459. 12:05:34:23 MSG Received 999 correct characters.
  12460. 12:05:45:11 MSG Test Complete
  12461.  
  12462. Listing 1
  12463. /*Program to turn small letters into Caps.*/
  12464. /*R. McLaughlin*/
  12465.  
  12466.  
  12467. /*Includes*/
  12468. #include <stdio.h>
  12469. #include <string.h>
  12470. #include <ctype.h>
  12471. /*Data Structures*/
  12472. #define FALSE 0
  12473. #define FOREVER 1
  12474. #define LOWER "a"
  12475. #define UPPER "z"
  12476. #define MASK 0x20
  12477. int input;
  12478.  
  12479. /*Code*/
  12480. main()
  12481. {
  12482. while(FOREVER)
  12483. {
  12484. input=getchar();
  12485. if (input>LOWER)&&(input<UPPER)
  12486. {
  12487. input=input^MASK;
  12488. }
  12489. printf(input);
  12490. }
  12491. }
  12492.  
  12493.  
  12494. Listing 2
  12495. /*Program Test program to turn small letters
  12496. into Caps.*/
  12497. /*R. McLaughlin*/
  12498.  
  12499. /*Includes*/
  12500. #include <stdio.h>
  12501. #include <string.h>
  12502. #include <ctype.h>
  12503. #include <stdlib.h>
  12504. #include <cm.h> /*CHECK*MATE include*/
  12505. #include <setups.h> /*routines to set up serial port*/
  12506. #include <log.h> /*routines to log on and off remote
  12507. system*/
  12508. /*Data Structures*/
  12509. #define FALSE 0
  12510. int i, correct;
  12511. char char_in[2], char_out [2];
  12512. char output[81];
  12513. char msg[100];
  12514.  
  12515. /*Code*/
  12516. main()
  12517. {
  12518. CM_log("Start test of lower-to-upper");
  12519. /*Put start message in test log.*/
  12520. Set_line(); /*implementation dependent*/
  12521. Log_in(); /*implementation dependent*/
  12522. for(i=0;i<1000;i++)
  12523. {
  12524. while(isprint(char_out)=FALSE)
  12525. {
  12526.  
  12527. char_out= char rand(); /*send only printable
  12528. characters.*/
  12529. }
  12530. TX_chr(DEV1,char_out);
  12531. TX_chr(DEV1,"\n");
  12532. RX_chr(DEV1,char_in);
  12533. if (islower(char_out)!=FALSE))
  12534. {
  12535. if((isupper(char_in)==FALSE) 
  12536. (tolower(char_in)==char_out))
  12537. {
  12538. sprintf(msg,"Sent %c, received %c. --
  12539. FAILURE",char_out,char_in);
  12540. CM_log(msg);
  12541. }
  12542. else
  12543. {
  12544. correct++;
  12545. }
  12546. else
  12547. {
  12548. if(char_out==char_in)
  12549. {
  12550. correct++;
  12551. }
  12552. else
  12553. {
  12554. sprintf(msg,"Sent %c, received %c.
  12555. -- FAILURE",char_out,char_in);
  12556. CM_log(msg);
  12557. }
  12558. }
  12559. }
  12560. sprintf(msg,"Received %d correct
  12561. characters.",correct);
  12562. CM_log(msg);
  12563. CM_log("Test Complete");
  12564. Log_off(); /*implementation dependent*/
  12565. Reset_devices(); /*implementation dependent*/
  12566. exit(0);
  12567. }
  12568.  
  12569. Note: Implementation dependent code is not shown.
  12570.  
  12571.  
  12572.  
  12573.  
  12574.  
  12575.  
  12576.  
  12577.  
  12578.  
  12579.  
  12580.  
  12581.  
  12582.  
  12583.  
  12584.  
  12585.  
  12586.  
  12587.  
  12588.  
  12589.  
  12590. A Login Shell For MS-DOS
  12591.  
  12592.  
  12593. Leor Zolman
  12594.  
  12595.  
  12596. Leor Zolman bought his first microcomputer (an IMSAI 8080) while in high
  12597. school in L.A., carried it to M.I.T., withdrew, and wrote the BDS C compiler
  12598. with it in assembly language. That was enough assembly language hacking to
  12599. last a lifetime, so now he enjoys UNIX/Xenix system administration, article
  12600. writing, and raising his newborn daughter Katelyn. You can reach him at
  12601. leor@rdpub. com or uunet!bdsoft!rdpub!leor.
  12602.  
  12603.  
  12604. Generic DOS is a single-user, single-process operating system. Only one person
  12605. can directly use a particular client (as opposed to network server) DOS system
  12606. interactively at a time, and that user may only run one program at a time.
  12607. If a DOS system is used by more than one person, users will quickly discover
  12608. that there are no built-in file management mechanisms for associating
  12609. individual files with particular users. The users must keep track of their own
  12610. files (if they understand the DOS directory structure well enough), or all the
  12611. software applications must manage file ownership, perhaps by prompting users
  12612. for their identity and then associating users with their own uniquely-named
  12613. directories or maintaining ownership data in special system files.
  12614.  
  12615.  
  12616. Logging In
  12617.  
  12618.  
  12619. In contrast, systems designed to support multiple users (whether
  12620. simultaneously via multi-processing or not), such as the UNIX family of
  12621. operating systems, maintain file ownership information for every program and
  12622. data file on the system. These systems always require a user to log in at the
  12623. beginning of any session. When logging in, a user must provide a public user
  12624. id along with an optional, secret password before he can use the system. The
  12625. user id is typically an alphanumeric string derived from the user's name
  12626. (although some systems, such as CompuServe, use a numeric format). The
  12627. password is a text string known only to the user and protected by the system.
  12628. Once a user has given an acceptable login id and password, the system sets
  12629. some globally-accessible variables (so that other software on the system can
  12630. identify the user) and executes an initial command sequence designed
  12631. exclusively for that user. (On UNIX systems, the login program generally
  12632. starts up one of several available command processors, and that command
  12633. processor becomes responsible for executing customized startup commands.)
  12634. I wrote the program presented in this article to make some rudimentary
  12635. multi-user (although not multi-processing) system features available under
  12636. DOS. The program is named login, for the UNIX facility after which it has been
  12637. patterned. As with the UNIX version, a password file containing information
  12638. about each legal user must be present somewhere on the system. Password files
  12639. typically contains one physical ASCII line of information for each user,
  12640. including at least the user's id and optional password. My version supports
  12641. only these two fields, while the UNIX version contains other fields for
  12642. controlling features not supported by DOS.
  12643. This DOS login offers at least two useful features, even on a single-user (at
  12644. a time) system: security and simplified user interface.
  12645. Login is a start toward preventing unauthorized use of a machine, although
  12646. there probably aren't any 100 percent-reliable security measures for DOS. (In
  12647. fact, my friend Jay Sage of ZCPR3 fame claims perhaps justifiably that the
  12648. ZCPR3 system -- an eight-bit derivative of CP/M -- is inherently much more
  12649. secure an operating system than DOS could ever become.)
  12650. Probably the more practical benefit of login is the ability to simplify the
  12651. user interface for users who are "DOS illiterate." The user doesn't need to
  12652. know about directories and command prompts. All he needs to remember is his
  12653. login id, password, and how to use the applications his customized startup
  12654. sequence launches for him.
  12655.  
  12656.  
  12657. What the User Sees
  12658.  
  12659.  
  12660. From the user's point of view, my login, as well as UNIX's, behaves in the
  12661. following manner. When invoked, login prints the string
  12662. login:
  12663. and waits for the user to enter a login id. When the user presses return,
  12664. login checks to see if the user entered a valid id. If the login-id is
  12665. correct, and the password file contains no password associated with that user,
  12666. then the login is approved. (Obviously, security can't be an issue under such
  12667. a configuration.) If the login id requires a password, or even if the login id
  12668. was invalid, login first displays
  12669. password:
  12670. then turns off keyboard echoing and waits for the user to enter a password. By
  12671. prompting for a password even when the login id was invalid, login doesn't
  12672. yield information concerning valid login ids.
  12673. After the user enters a password and presses the return key, login checks that
  12674. the password is correct for the given user id. If so, then the login is
  12675. approved. If it isn't, or if the user id was unrecognized, login says
  12676. login incorrect.
  12677. and repeats the whole process with
  12678. login:
  12679. This pattern continues until a login is approved. You can exit the program
  12680. only by logging in correctly or rebooting the machine. If the machine has a
  12681. bootable floppy drive, then you can bypass login by inserting a floppy boot
  12682. disk (that didn't have login as part of the startup in AUTOEXEC. BAT) and
  12683. rebooting -- so much for bullet-proof security. By configuring the floppy as
  12684. B: on some systems, however, or if used on systems without any floppies, even
  12685. most DOS-literate users would be compelled to play the login game.
  12686.  
  12687.  
  12688. Starting Up the User
  12689.  
  12690.  
  12691. Once the user enters the correct login sequence, login performs two more tasks
  12692. to get the session underway.
  12693. First login writes the user's id string into the special file CURRENT_USER in
  12694. the STARTUP_DIR directory (these names are all #defined constants you may
  12695. modify in the source code.) Applications can read the file and determine the
  12696. current user.
  12697. Second, login looks for a "startup file" named USER.BAT where USER is the
  12698. user's login id, in the STARTUP_DIR directory. If found, the file is passed to
  12699. COMMAND.COM (DOS's command processor) for execution via the system() library
  12700. function. This startup file would be the place to put the name of the
  12701. command(s) you want executed when that user logs in; the command might be an
  12702. individual application, a menu system, or some other kind of program.
  12703. To maintain security, the last line of the user's startup file should specify
  12704. the login command itself, to close the loop.
  12705. For another blow to the security issue, remember that most major applications
  12706. have commands that allow "shell escapes" to DOS...and that most applications
  12707. in general can be brought down hard (i.e., crashed) without too much effort,
  12708. presenting the user with an invitation from DOS to press Control-C to abort
  12709. the current batch file and return to a system prompt. Sigh.
  12710.  
  12711.  
  12712. How It Works
  12713.  
  12714.  
  12715. For console I/O, login contains several functions that talk directly to the
  12716. low-level console interface routines available via DOS's BDOS to disable the
  12717. interrupt keys Control-C / Control-Break.
  12718. zgetch() reads a character from the keyboard, mapping carriage returns to
  12719. newline characters for convenience. zgets() reads an entire line from the
  12720. keyboard into a character buffer provided as a parameter, using zgetch() to
  12721. read characters. The parameter echo to zgets() controls whether or not the
  12722. keystrokes are echoed back to the screen as they are typed. The format of the
  12723. line collected up by zgets() is the same as for the standard gets() function,
  12724. i.e., null-terminated with no newline character at the end.
  12725. zputs() writes a line to the screen. The library function putch() is used to
  12726. write each character, and newlines are expanded to CR-LF sequences.
  12727. The main program begins by reading in all information from the password file
  12728. into the array of structures named users. As each line is read in from the
  12729. file using fscanf(), the name and passwd elements of users are assigned the
  12730. first and second strings on the line, respectively. If at least one string is
  12731. not found on a line, the program assumes the end of file has been reached (see
  12732. line 67). After each line has been loaded successfully, the loop counter
  12733. variable nusers is incremented.
  12734. If debugging is enabled, lines 69-73 display all the password information as
  12735. it is read from the file. To enable debugging, insert the line
  12736.  
  12737. #define DEBUG 1
  12738. at the top of the source file.
  12739. Once all password information has been loaded (and lines 75-76 have verified
  12740. that at least one valid user name is present), the password file is closed and
  12741. the main loop is started. At this point, the variable nusers tells you how
  12742. many records were found in the password file.
  12743. Lines 82 and 83 prompt for a user id and read in a line of text from the
  12744. console into the character buffer named linbuf, with keystroke echo enabled.
  12745. Lines 85-87 loop through the list and compare the strings to see if the
  12746. entered string matches any of the known user names. A match forces an
  12747. immediate break out of the loop, leaving the counter variable i equal to the
  12748. index into users of the matching user id (between 0 and nusers -- 1.) If no
  12749. matching id was found, i ends up with the value of nusers.
  12750. Lines 90-91 handle the case in which no password is required by skipping over
  12751. the password checking code. Lines 90-91 skip the password checking code if and
  12752. only if the following two conditions are met:
  12753. a valid user id was entered (i != nusers), and
  12754. the password for that user is a null string (!*users [i].passwd).
  12755. Otherwise, password checking springs into action. The password prompt is
  12756. displayed and a line of text is read from the keyboard again, but this time
  12757. with the echo turned off (line 94.)
  12758. Another two conditions must be met for the entered password to be accepted as
  12759. valid:
  12760. the user id supplied above must be recognized (i != nusers), and
  12761. the entered string must match the password from the password file.
  12762. Unless both conditions are met, the incorrect login message is displayed and
  12763. the main loop keeps repeating.
  12764.  
  12765.  
  12766. Upon Success
  12767.  
  12768.  
  12769. We reach line 101 after a correct login sequence. Login prints a few blank
  12770. lines for the sake of aesthetics, then lines 103-110 attempt to create the
  12771. CURRENT_USER file and write out to it the name of the user who just logged in.
  12772. Finally, lines 112-122 construct the name of the appropriate batch file to run
  12773. for the user. The name is created by starting with the name of the startup
  12774. directory, appending the user id, and then appending the .BAT extension onto
  12775. the end. Login opens the file to see if it is present; if so, the file is
  12776. closed and the filename is passed to the system() function for execution.
  12777.  
  12778.  
  12779. Logging Out
  12780.  
  12781.  
  12782. This program is only a starting point. To use the facility most effectively,
  12783. you must still be able to read information from the CURRENT_USER file into
  12784. your applications. Whether used to its capacity or not, secured or otherwise,
  12785. there is still some value in being able to customize each user's initial
  12786. environment to suit his or her needs in the most user-friendly way.
  12787.  
  12788.  
  12789. Exercise
  12790.  
  12791.  
  12792. As written, login writes a file (specified by CURRENT_USER) containing the
  12793. name of the active user. Another way to identify the current user to the
  12794. system is to set an environment variable, say CURUSER, to the user's id. This
  12795. method would be faster to execute, but you would then need the appropriate
  12796. tools to access the environment information from the application programs.
  12797. Using the Master Environment Package (from CUJ 11/89), modify login to set an
  12798. environment variable instead of (or in addition to) writing a file to disk.
  12799.  
  12800. Listing 1
  12801. 1: /*
  12802. 2: * Login management program for DOS
  12803. 3: *
  12804. 4: * Written by Leor Zolman, 5/1/89
  12805. 5: *
  12806. 6: * Usage (typcially in autoexec.bat):
  12807. 7: * login
  12808. 8: *
  12809. 9: * Control file (PASSWD_FILE) format:
  12810. 10: *
  12811. 11: * -----------------------------------
  12812. 12: * name [password]
  12813. 13: * name [password]
  12814. 14: * .
  12815. 15: * .
  12816. 16: * .
  12817. 17: * -----------------------------------
  12818. 18: *
  12819. 19: * The directory STARTUP_DIR should contain a batch
  12820. 20: * file for each user, named name.BAT. Upon successful
  12821. 21: * login, the batch file named for the user will be
  12822. 22: * be executed.
  12823. 23: * The file named by CURRENT_USER (c:\etc\startup\user.id
  12824. 24: * as configured below) will be written containing the
  12825. 25: * user id after a successful login.
  12826. 26: */
  12827.  
  12828. 27:
  12829. 28: #include <stdio.h>
  12830. 29: #include <conio.h>
  12831. 30: #include <stdlib.h>
  12832. 31: // name of startup batch directory, current
  12833. 32: // id file, and password control files:
  12834. 33: #define STARTUP_DIR "c:\\etc\\startup\\"
  12835. 34: #define CURRENT_USER STARTUP_DIR"user.id"
  12836. 35: #define PASSWD_FILE STARTUP_DIR"passwd.dat"
  12837. 36:
  12838. 37:
  12839. 38: #define MAXUSERS 100 // Max number of different users
  12840. 39: #define MAXLINE 100 // For login line input buffer
  12841. 40:
  12842. 41: #define ECHO 1 // Parameters to zgets()
  12843. 42: #define NOECHO 0
  12844. 43:
  12845. 44: char *zgets(char *buffer, int echo); // prototypes
  12846. 45: int zputs(char *str);
  12847. 46:
  12848. 47: struct { // name/password structure
  12849. 48: char name[15];
  12850. 49: char passwd[15];
  12851. 50: } users[MAXUSERS];
  12852. 51:
  12853. 52: void main()
  12854. 53: {
  12855. 54: int i;
  12856. 55: FILE *fp;
  12857. 56: int nusers;
  12858. 57: char linbuf[MAXLINE];
  12859. 58: // open password file
  12860. 59: if ((fp = fopen(PASSWD_FILE, "r")) == NULL)
  12861. 60: exit(cprintf("Can't open %s\a\n", PASSWD_FILE));
  12862. 61:
  12863. 62: // read in user name/password data
  12864. 63: for (nusers = 0; nusers < MAXUSERS; nusers++)
  12865. 64: { // default to null password:
  12866. 65: *users[nusers].passwd = '\0';
  12867. 66: if (fscanf(fp, "%s %s", users[nusers].name,
  12868. 67: users[nusers].passwd) < 1) // scan a line
  12869. 68: break; // break if empty
  12870. 69: #if DEBUG
  12871. 70: else // for debugging, show data
  12872. 71: cprintf("read user name: \"%s\", password: \"%s\"\n",
  12873. 72: users[nusers].name, users[nusers].passwd);
  12874. 73: #endif
  12875. 74: }
  12876. 75: if (!nusers)
  12877. 76: exit(zputs("No valid entries in log file.\a\n"));
  12878. 77:
  12879. 78: fclose(fp); // close password file
  12880. 79:
  12881. 80: while (1) // mail loop
  12882. 81: {
  12883. 82: zputs("login: "); // initial prompt
  12884. 83: zgets(linbuf, ECHO); // get user id w/echo
  12885. 84:
  12886. 85: for (i = 0; i < nusers; i++) // look it up
  12887.  
  12888. 86: if (!strcmp(users[i].name, linbuf))
  12889. 87: break;
  12890. 88:
  12891. 89: // found user id. need password?
  12892. 90: if (i != nusers && !*users[i].passwd)
  12893. 91: break; // if not, don't prompt
  12894. 92:
  12895. 93: zputs("\npassword: "); // prompt for password
  12896. 94: zgets(linbuf, NOECHO); // read w/o echo
  12897. 95: if (i != nusers && !strcmp(linbuf, users[i].passwd))
  12898. 96: break; // if correct, break out of loop
  12899. 97:
  12900. 98: zputs("\nlogin incorrect.\n");
  12901. 99: }
  12902. 100:
  12903. 101: zputs("\n\n"); // success!
  12904. 102: // write id file
  12905. 103: if ((fp = fopen(CURRENT_USER, "w")) == NULL)
  12906. 104: cprintf("Couldn't create %s\a\n", CURRENT_USER);
  12907. 105: else
  12908. 106: {
  12909. 107: if (fputs(users[i].name, fp) == EOF)
  12910. 108: cprintf("Couldn't write to %s\n", CURRENT_USER);
  12911. 109: fclose(fp);
  12912. 110: }
  12913. 111:
  12914. 112: strcpy(linbuf, STARTUP_DIR); // construct startup batch
  12915. 113: strcat(linbuf, users[i].name); // filename
  12916. 114: strcat(linbuf, ".bat");
  12917. 115: if ((fp = fopen(linbuf, "r")) != NULL) // is one there?
  12918. 116: {
  12919. 117: fclose(fp); // yes. close it up.
  12920. 118: if (system(linbuf)) // attempt to run it
  12921. 119: cprintf("Couldn't execute %s\a\n", linbuf);
  12922. 120: }
  12923. 121: else
  12924. 122: cprintf("Couldn't find %s\a\n", linbuf);
  12925. 123: }
  12926. 124:
  12927. 125:
  12928. 126: /*
  12929. 127: * function zgets():
  12930. 128: * Read a string from the console with optional echo,
  12931. 129: * and all Ctrl-C / Ctrl-Breka checks disabled:
  12932. 130: */
  12933. 131:
  12934. 132: char *zgets(char *str, int echo)
  12935. 133: {
  12936. 134: char c, *save = str; // save address of buffer
  12937. 135:
  12938. 136: while ((c = zgetch()) != '\n') // read a char
  12939. 137: {
  12940. 138: *str++= c;
  12941. 139: if (echo) // echo if required
  12942. 140: putch(c);
  12943. 141: }
  12944. 142: *str = '\0'; // terminate string
  12945. 143: return save;
  12946. 144: }
  12947.  
  12948. 145:
  12949. 146:
  12950. 147: /*
  12951. 148: * function zgetch():
  12952. 149: * Read a character from the keyboard, without echo,
  12953. 150: * performing newline conversion:
  12954. 151: */
  12955. 152:
  12956. 153: int zgetch()
  12957. 154: {
  12958. 155: char c;
  12959. 156:
  12960. 157: c = bdos(7,0,0); // Use a direct BDOS call
  12961. 158: return (c == '\r') ? '\n' : c; // Convert CR to newline
  12962. 159: }
  12963. 160:
  12964. 161:
  12965. 162: /*
  12966. 163: * function zputs():
  12967. 164: * Print a string to the console, with newlines expanded:
  12968. 165: */
  12969. 166:
  12970. 167: int zputs(char *str)
  12971. 168: {
  12972. 169: char c;
  12973. 170:
  12974. 171: while (c = *str++)
  12975. 172: {
  12976. 173: if (c == '\n')
  12977. 174: putch('\r');
  12978. 175: putch(c);
  12979. 176: }
  12980. 177: return 0;
  12981. 178: }
  12982.  
  12983.  
  12984.  
  12985.  
  12986.  
  12987.  
  12988.  
  12989.  
  12990.  
  12991.  
  12992.  
  12993.  
  12994.  
  12995.  
  12996.  
  12997.  
  12998.  
  12999.  
  13000.  
  13001.  
  13002.  
  13003.  
  13004.  
  13005.  
  13006.  
  13007.  
  13008.  
  13009.  
  13010.  
  13011. Standard C
  13012.  
  13013.  
  13014. The Header <limits.h>
  13015.  
  13016.  
  13017.  
  13018.  
  13019. P.J. Plauger
  13020.  
  13021.  
  13022. P.J. Plauger is senior editor of The C Users Journal. He is secretary of the
  13023. ANSI C standards committee, X3J11, and convenor of the ISO C standards
  13024. committee, WG14. His latest book is Standard C, which he co-authored with Jim
  13025. Brodie. You can reach him at pjp@plauger.uunet.
  13026.  
  13027.  
  13028.  
  13029.  
  13030. History
  13031.  
  13032.  
  13033. One of the first attempts at standardizing any part of the C programming
  13034. languages began in 1980. It was begun by an organization then called
  13035. /usr/group, now called Usenix. As the first organization founded to promote
  13036. UNIX commercially, /usr/group had a stake in vendor-independent standards.
  13037. Technical developments couldn't simply go off in all directions, nor could
  13038. they be dictated solely by AT&T. Either way, it was hard to maintain an open
  13039. marketplace.
  13040. So /usr/group began the process of defining what it means to call a system
  13041. UNIX or UNIX-like. They formed a standards committee that focused, at least
  13042. initially, on the C programming environment. That's where nearly all
  13043. applications were written, anyway. The goal was to describe a set of C
  13044. functions that you could expect to find in any UNIX-compatible system. The
  13045. descriptions, of course, had to be independent of any particular architecture.
  13046. A chunk of what /usr/group described was the set of C-callable functions that
  13047. let you access UNIX system services. An even larger chunk, however, was the
  13048. set of functions common to all C environments. That larger chunk served as the
  13049. basis for the language portion of the C Standard. Since Kernighan and Ritchie
  13050. chose not to discuss the library except in passing, the /usr/group standard
  13051. was of immense help to X3J11. It saved us many months, possibly even years, of
  13052. additionl labor.
  13053. As an aside, the /usr/group effort served another very useful purpose. IEEE
  13054. committee 1003 was formed to turn this industry product into an official
  13055. standard. The IEEE group turned over responsibility for the system-independent
  13056. functions to X3J11 and focused on the UNIX-specific portion. You know the
  13057. resultant standard today as IEEE 1003.1, a.k.a. POSIX.
  13058. Part of building an architecture-independent description is to recognize what
  13059. changes across machines. You want to avoid any unnecessary differences, to be
  13060. sure. The rest you want to identify and to circumscribe. Some critical value
  13061. might change when you move an application program to another flavor of UNIX.
  13062. So you give it a name. You lay down rules for testing the named value in a
  13063. program. And you define the limits that the value can range between.
  13064. A long-standing tradition in C is that scalar data types are represented in
  13065. ways natural to each machine. The fundamental type int is particularly
  13066. elastic. It wants to be a size that supports efficient computation, at least
  13067. within broad limits. That may be great for efficiency, but it's a real
  13068. nuisance for portability.
  13069. /usr/group invented the standard header <limits.h> to capture many important
  13070. properties that can change across architectures. It so happens that this
  13071. header deals exclusively with the ranges of values of integer types. That was
  13072. all that /usr/group chose to address. When X3J11 decided to add similar data
  13073. on the floating types, we elected not to overwhelm the existing contents of
  13074. <limits.h>. Instead, we added the standard header <float.h>. Perhaps we should
  13075. have renamed the existing standard header <integer.h>, but we didn't. Tidiness
  13076. yielded to historical continuity.
  13077.  
  13078.  
  13079. Using <limits.h>
  13080.  
  13081.  
  13082. You can use <limits.h> one of two ways. The simpler way assures that you do
  13083. not produce a silly program. Let's say, for example, that you want to
  13084. represent some signed data that ranges in value between VAL_MIN and VAL_MAX.
  13085. You can keep the program from compiling incorrectly by including in the text:
  13086. #include <assert.h>
  13087. #include <limits.h>
  13088. #if VAL_MIN < INT_MIN \
  13089.  INT_MAX < VAL_MAX
  13090. #error values out of range
  13091. #endif
  13092. You can safely store the data in variables declared with type int if the error
  13093. directive is skipped.
  13094. A more elaborate way to use <limits.h> is to control the choice of types in a
  13095. program. You can alter the example above to read:
  13096. #include <assert.h>
  13097. #include <limits.h>
  13098. #if VAL_MIN < LONG_MIN \
  13099.  LONG_MAX < VAL_MAX
  13100. typedef double Val_t;
  13101. #elif VAL_MIN < INT_MIN \
  13102.  INT_MAX < VAL_MAX
  13103. typedef long Val_t;
  13104. #else
  13105. typedef int Val_t;
  13106. #endif
  13107. You then declare all variables that must hold this range of values as having
  13108. type Val_t. The program will use the computationally most efficient type for a
  13109. given target environment.
  13110. The presence of <limits.h> is also designed to discourage an old programming
  13111. trick that is extremely non-portable. Some programs attempted to sniff out the
  13112. properties of the target environment by writing tricky if directives, as in:
  13113. #if (-1 + 0x0) >> 1 > 0x7fff
  13114. /* must have ints greater than 16 bits */
  13115. .....
  13116. #endif
  13117.  
  13118. This code assumes that whatever arithmetic the preprocessor performs is the
  13119. same as what occurs in the execution environment. Those of us who deal heavily
  13120. with cross compilers know well that the translation environment can differ
  13121. markedly from the execution environment. For tricks like this one to work, the
  13122. C Standard would have to require that the translator mimic the execution
  13123. environment very closely. And compiler families with a common front end would
  13124. have to adapt translation-time arithmetic to suit the target.
  13125. X3J11 discussed such requirements at length. In the end, we decided that the
  13126. preprocessor was not the creature to burden with such stringent requirements.
  13127. The translator must closely model the execution environment in many ways, to
  13128. be sure. It must compute constant expressions -- the things you use to
  13129. initialize static data objects, for example -- to at least as wide a range and
  13130. precision as the target. But it can largely define its own internal
  13131. environment for the arithmetic within if and elif directives.
  13132. So to test the execution environment you can't do experiments on the
  13133. preprocessor. You must include <limits.h> and test the values of the macros it
  13134. provides.
  13135.  
  13136.  
  13137. What The Standard Says
  13138.  
  13139.  
  13140. Here is what the Standard has to say about <limits.h>. The library portion
  13141. contains only a brief reference:
  13142.  
  13143.  
  13144. 4.1.4 Limits <float.h> and <limits.h>
  13145.  
  13146.  
  13147. The headers <float.h> and <limits.h> define several macros that expand to
  13148. various limits and parameters.
  13149. The macros, their meanings, and the constraints (or restrictions) on their
  13150. values are listed in 2.2.4.2. [end of extract]
  13151. That's it. For the meat, you have to go back to the environment section:
  13152.  
  13153.  
  13154. 2.2.4.2 Numerical Limits
  13155.  
  13156.  
  13157. A conforming implementation shall document all the limits specified in this
  13158. section, which shall be specified in the headers <limits.h> and <float.h>.
  13159.  
  13160.  
  13161. 2.2.4.2.1 Sizes of Integral Types
  13162.  
  13163.  
  13164.  
  13165.  
  13166. <limits.h>
  13167.  
  13168.  
  13169. The values given below shall be replaced by constant expressions suitable for
  13170. use in #if preprocessing directives. Moreover, except for CHAR_BIT and
  13171. MB_LEN_MAX, the following shall be replaced by expressions that have the same
  13172. type as would an expression that is an object of the corresponding type
  13173. converted according to the integral promotions. Their implementation-defined
  13174. values shall be equal or greater in magnitude (absolute value) to those shown,
  13175. with the same sign.
  13176. number of bits for smallest object that is not a bit-field (byte)
  13177. CHAR_BIT 8
  13178. minimum value for an object of type signed char
  13179. SCHAR_MIN -127
  13180. maximum value for an object of type signed char
  13181. SCHAR_MAX +127
  13182. maximum value for an object of type unsigned char
  13183. UCHAR_MAX 255
  13184. minimum value for an object of type char
  13185. CHAR_MIN see below
  13186. maximum value for an object of type char
  13187. CHAR_MAX see below
  13188. maximum number of bytes in a multibyte character, for any supported locale
  13189. MB_LEN_MAX 1
  13190. minimum value for an object of type short int
  13191. SHRT_MIN -32767
  13192. maximum value for an object of type short int
  13193. SHRT_MAX +32767
  13194. maximum value for an object of type unsigned short int
  13195. USHRT_MAX 65535
  13196. minimum value for an object of type int
  13197. INT_MIN -32767
  13198. maximum value for an object of type int
  13199. INT_MAX +32767
  13200. maximum value for an object of type unsigned int
  13201.  
  13202. UINT_MAX 65535
  13203. minimum value for an object of type long int
  13204. LONG_MIN -2147483647
  13205. maximum value for an object of type long int
  13206. LONG_MAX +2147483647
  13207. maximum value for an object of type unsigned long int
  13208. ULONG_MAX 4294967295
  13209. If the value of an object of type char is treated as a signed integer when
  13210. used in an expression, the value of CHAR_MIN shall be the same as that of
  13211. SCHAR_MIN and the value of CHAR_MAX shall be the same as that of SCHAR_MAX.
  13212. Otherwise, the value of CHAR_MIN shall be 0 and the value of CHAR_MAX shall be
  13213. the same as that of UCHAR_MAX.9
  13214. Footnote
  13215. 9. See 3.1.2.5 [end of extract]
  13216. The only significant addition to this header from the days of /usr/group is
  13217. the addition of MB_LEN_MAX. I will discuss multibyte characters at length in a
  13218. future column.
  13219. Several people reviewing the Standard complained that names such as USHRT_MAX
  13220. are barbaric. It is silly to omit a single vowel in the interest of terseness,
  13221. particularly in this age of 31-character (or longer) names. I can only plead,
  13222. on behalf of X3Jll, the same excuse we gave for not changing the name of the
  13223. header itself. We couldn't justify abandoning prior art just to be a bit
  13224. tidier. Besides, fixing such barbarisms only in this place is like eating one
  13225. peanut.
  13226.  
  13227.  
  13228. Implementing <limits.h>
  13229.  
  13230.  
  13231. The only code you have to provide for this header is the header itself. All
  13232. the macros defined in <limits.h> are testable within an if directive and are
  13233. unlikely to change during execution. (The same is not true of most of the
  13234. macros defined in <float.h>.)
  13235. Most modern computers have eight-bit bytes, two-byte shorts, and four-byte
  13236. longs. There are several common variations on this principal theme:
  13237. An int is either two or four bytes.
  13238. A char has the same range of values as either signed char or unsigned char.
  13239. Signed values are encoded most frequently in 2's complement, which has only
  13240. one form of zero but one negative value that has no corresponding positive
  13241. value. Less common encodings are 1's complement and signed magnitude, which
  13242. have two forms of zero but no extra negative value.
  13243. The number of bytes for a single multibyte character can be any value greater
  13244. than zero.
  13245. I found it convenient, therefore, to write a version of <limits.h> that
  13246. expands to any of these common forms. It includes, as needed, a configuration
  13247. file called <yvals.h>. Among other things, this file defines the macros:
  13248. _ILONG -- nonzero if a long has four bytes
  13249. _CSIGN -- nonzero if a char is signed
  13250. _2C -- 1 if the encoding is 2's complement, else 0
  13251. _MBMAX -- the worst-case length of a single multibyte character.
  13252. Listing 1 shows the code for <limits.h>.
  13253. The use of the macro _2C obscures an important subtlety. On a 2's-complement
  13254. machine, you cannot simply write the obvious value for INT_MIN. Why not? On a
  13255. 16-bit machine, for example, the sequence of characters -32768 parses as two
  13256. tokens -- a minus sign and the integer constant with value 32,768. The latter
  13257. has type long because it is too large to represent properly as type int.
  13258. Negating this value doesn't change its type. The Standard requires, however,
  13259. that INT_MIN have type int. Otherwise, you can be astonished by the behavior
  13260. of a statement as innocent looking as:
  13261. printf("range is from %d to %d\n",
  13262. INT_MIN, INT_MAX);
  13263. The only safe thing is to sneak up on the value by writing an expression such
  13264. as (-32767-1). Given the way I chose to parametrize <limits.h>, you get this
  13265. trickery for free.
  13266. One other subtlety should not be overlooked. I made the point earlier that
  13267. preprocessor arithmetic need not model that of the execution environment. You
  13268. can, in principle, compile on a host with 32-bit longs for a target with
  13269. 36-bit longs. Nevertheless, the host is obliged to get the values in
  13270. <limits.h> right. That means that it must do preprocessor arithmetic to at
  13271. least 36 bits. The latitude spelled out for implementors by X3J11 isn't so
  13272. broad after all.
  13273.  
  13274.  
  13275. Testing <limits.h>
  13276.  
  13277.  
  13278. Listing 2 is a brief sanity check you can run on <limits.h>. It is by no means
  13279. exhaustive, but it does tell you whether the header is basically sane.
  13280. Note that all the action occurs at translation time. That's because all the
  13281. macros must be usable within if directives. If this test compiles, it will
  13282. surely run, print its success message, and exit with happy status.
  13283. You might try this test on your favorite compiler. I can only comment that I
  13284. have known it to fail on some popular offerings.
  13285.  
  13286. Listing 1
  13287. /* limits.h standard header -- 8-bit version
  13288. * copyright (c) 1991 by P.J. Plauger
  13289. */
  13290. #ifndef _LIMITS
  13291. #define _LIMITS
  13292.  
  13293. #ifndef _YVALS
  13294. #include <yvals.h>
  13295. #endif
  13296. /* char properties */
  13297. #define CHAR_BIT 8
  13298. #if _CSIGN
  13299. #define CHAR_MAX 127
  13300. #define CHAR_MIN (-127-_2C)
  13301. #else
  13302.  
  13303. #define CHAR_MAX 255
  13304. #define CHAR_MIN 0
  13305. #endif
  13306. /* int properties */
  13307. #if _ILONG
  13308. #define INT_MAX 2147483647
  13309. #define INT_MIN (-2147483647-_2C)
  13310. #else
  13311. #define INT_MAX 32767
  13312. #define INT_MIN (-32767-_2C)
  13313. #endif
  13314. /* long properties */
  13315. #define LONG_MAX 2147483647
  13316. #define LONG_MIN (-2147483647-_2C)
  13317. /* multibyte properties */
  13318. #define MB_LEN_MAX_MBMAX
  13319. /* signed char properties */
  13320. #define SCHAR_MAX 127
  13321. #define SCHAR_MIN (-127-_2C)
  13322. /* short properties */
  13323. #define SHRT_MAX 32767
  13324. #define SHRT_MIN (-32767-_2C)
  13325. /* unsigned properties */
  13326. #define UCHAR_MAX 255
  13327. #define UINT_MAX 4294967295
  13328. #define ULONG_MAX 4294967295
  13329. #define USHRT_MAX 65535
  13330. #endif
  13331.  
  13332.  
  13333. Listing 2
  13334. /* test limits macros
  13335. * copyright (c) 1991 by P.J. Plauger
  13336. */
  13337. #include <limits.h>
  13338. #include <stdio.h>
  13339.  
  13340. /* test basic properties of limits.h macros
  13341. */
  13342. int main()
  13343. {
  13344. #if CHAR_BIT < 8 CHAR_MAX < 127 0 < CHAR_MIN \
  13345.  CHAR_MAX != SCHAR_MAX && CHAR_MAX != UCHAR_MAX
  13346. #error bad char properties
  13347. #endif
  13348. #if INT_MAX < 32767 -32767 < INT_MIN \
  13349.  INT_MAX < SHRT_MAX
  13350. #error bad int properties
  13351. #endif
  13352. #if LONG_MAX < 2147483647 \
  13353.  -2147483647 < LONG_MIN \
  13354.  LONG_MAX < INT_MAX
  13355. #error bad long properties
  13356. #endif
  13357. #if MB_LEN_MAX < 1
  13358. #error bad MB_LEN_MAX
  13359. #endif
  13360. #if SCHAR_MAX < 127 -127 < SCHAR_MIN
  13361. #error bad signed char properties
  13362.  
  13363. #endif
  13364. #if SHRT_MAX < 32767 -32767 < SHRT_MIN \
  13365.  SHRT_MAX < SCHAR_MAX
  13366. #error bad short properties
  13367. #endif
  13368. #if UCHAR_MAX < 255 UCHAR_MAX <= 2 * SCHAR_MAX
  13369. #error bad unsigned char properties
  13370. #endif
  13371. #if UINT_MAX < 65535 UINT_MAX <= 2 * INT_MAX \
  13372.  UINT_MAX < USHRT_MAX
  13373. #error bad unsigned int properties
  13374. #endif
  13375. #if ULONG_MAX < 4294967295 \
  13376.  ULONG_MAX <= 2 * LONG_MAX \
  13377.  ULONG_MAX < UINT_MAX
  13378. #endif
  13379. #if USHRT_MAX < 65535 USHRT_MAX <= 2 * SHRT_MAX \
  13380.  USHRT_MAX < UCHAR_MAX
  13381. #error bad unsigned short properties
  13382. #endif
  13383. puts("SUCCESS testing <limits.h>");
  13384. return (0);
  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.  
  13418.  
  13419.  
  13420.  
  13421.  
  13422.  
  13423.  
  13424.  
  13425.  
  13426. Doctor C's Pointers(R)
  13427.  
  13428.  
  13429. Puzzles, Part 4
  13430.  
  13431.  
  13432.  
  13433.  
  13434. Rex Jaeschke
  13435.  
  13436.  
  13437. Rex Jaeschke is an independent computer consultant, author and seminar leader.
  13438. He participates in both ANSI and ISO C Standards meetings and is the editor of
  13439. The Journal of C Language Translation, a quarterly publication aimed at
  13440. implementors of C language translation tools. Readers are encouraged to submit
  13441. column topics and suggestions to Rex at 2051 Swans Neck Way, Reston, VA 22091
  13442. or via UUCP at uunet! aussie! rex or aussie! rex@uunet.uu.net.
  13443.  
  13444.  
  13445. This month we'll continue with our "quality of implementation" puzzles. Try to
  13446. debug them yourself first and see what messages your compiler produces.
  13447.  
  13448.  
  13449. The Puzzles
  13450.  
  13451.  
  13452. 1. A typical example of C.
  13453. /* 1*/#include <stdio.h>
  13454.  
  13455. /* 3*/main()
  13456. /* 4*/{
  13457. /* 5*/ char *pc;
  13458.  
  13459. /* 7*/ printf("Enter up to 3
  13460. characters: ");
  13461. /* 8*/ scanf("%3s", pc);
  13462. /* 9*/ printf("Input is %s\n",
  13463. pc);
  13464. /*10*/}
  13465.  
  13466. Enter up to 3 characters: asd
  13467. Input is asd
  13468. I've often seen publications introduce C using an example of a typical C
  13469. program such as that above. It's typical because it has bugs. I also see it in
  13470. many of my student's projects.
  13471. 2. So why shouldn't it work?
  13472. /* 1*/#include <stdio.h>
  13473.  
  13474. /* 3*/main()
  13475. /* 4*/{
  13476. /* 5*/ char *pc = "abcdef";
  13477.  
  13478. /* 7*/ *pc = 'X';
  13479.  
  13480. /* 9*/ printf("pc points to
  13481.  %s\n", pc);
  13482. /*10*/}
  13483.  
  13484. pc points to Xbcdef
  13485. This program runs as shown on many systems. It also dies miserably on others,
  13486. and both outcomes are acceptable, at least by ANSI C.
  13487. 3. Why isn't 'end' recognized?
  13488. /* 1*/#include <stdio.h>
  13489.  
  13490. /* 3*/main()
  13491. /* 4*/{
  13492.  
  13493. /* 5*/ char str[4];
  13494.  
  13495. /* 7*/ printf("Enter up to 3
  13496. characters: ");
  13497. /* 8*/ scanf("%3s", str);
  13498.  
  13499. /*10*/ if (str == "end")
  13500. /*11*/ printf("'end' entered\n");
  13501. /*12*/ else
  13502. /*13*/ printf("'end' was not entered\n");
  13503. /*14*/}
  13504.  
  13505. Enter up to 3 characters: 123
  13506. 'end' was not entered
  13507.  
  13508. Enter up to 3 characters: end
  13509. 'end' was not entered
  13510. 4. More on string literals.
  13511. /* 1*/ #include <stdio.h>
  13512.  
  13513. /* 3*/ main()
  13514. /* 4*/ {
  13515. /* 5*/ if ("abc" == "abc")
  13516. /* 6*/ printf("Equal\n");
  13517. /* 7*/ else
  13518. /* 8*/ printf("Not Equal\n");
  13519. /* 9*/ }
  13520.  
  13521. The two possible outcomes are:
  13522.  
  13523. Not Equal
  13524. Equal
  13525. 5. Hey, who broke my sizeof?
  13526. /* 1*/ #include <stdio.h>
  13527.  
  13528. /* 3*/ main()
  13529. /* 4*/ {
  13530. /* 5*/ static int j[10];
  13531. /* 6*/ void f(int []);
  13532.  
  13533. /* 8*/ f(j);
  13534. /* 9*/ }
  13535.  
  13536. /*11*/ void f(int x[])
  13537. /*12*/ {
  13538. /*13*/ int i;
  13539.  
  13540. /*15*/ for (i = 0; i < sizeof(x)/sizeof(x[0]); ++i)
  13541. /*16*/ printf("i = %d\n", i);
  13542. /*17*/}
  13543.  
  13544. i = 0 /* small memory model */
  13545.  
  13546. i = 0 /* large memory model */
  13547. i = 1
  13548.  
  13549. i = 0 /* 32-bit mode compiler */
  13550. This very common example attempts to use sizeof on a formal array parameter to
  13551. determine the number of elements in that array. Unfortunately, this construct
  13552. compiles without error and gives you something other than what you initially
  13553. expect.
  13554.  
  13555.  
  13556.  
  13557. The Solutions
  13558.  
  13559.  
  13560. 1. It is very unlikely this will fail to execute correctly on DOS. However,
  13561. increasing significantly the number of characters accepted will certainly
  13562. increase the chances of actual undefined behavior. Many of my seminars are
  13563. conducted using VAX/VMS, a multi-user memory protected operating system. In
  13564. such environments, weird behavior is likely to occur from the start.
  13565. scanf expects a pointer to char and that's what we give it. scanf then
  13566. proceeds to read in characters storing them at the location pointed to by that
  13567. pointer. The problem is, just where does that pointer point? Being of
  13568. automatic storage duration, pc's initial value is undefined -- it could be any
  13569. bit pattern -- and may look like a valid or invalid address. So scanf tries to
  13570. store the characters read in some arbitrary location. Without memory
  13571. protection, DOS allows you to overwrite any location in memory, including DOS
  13572. itself and device registers, particularly in the larger memory models. On
  13573. VAX/VMS, if pc contains an address outside the addresses allocated to this
  13574. program, or in the range 0-511, or to part of the program that is located in a
  13575. read-only program section (such as instructions and const data objects), a
  13576. memory access violation results.
  13577. Several compilers did give useful warnings when asked.
  13578. line 8 - Warning: Possible use
  13579. of 'pc' before assigned a value
  13580.  
  13581. line 9 - Warning: Possible use
  13582. of 'pc' before assigned a value
  13583.  
  13584. line 9 - Warning! 'pc' has been referenced but
  13585. never assigned a value
  13586. The (undefined) value of pc was used in two places, lines 8 and 9. One
  13587. compiler correctly warned about both but the other warned only about line 9.
  13588. 2. By initializing pc to a string, the code does not make it obvious that in
  13589. *pc = 'X' we are attempting to modify the first character of a string literal.
  13590. Perhaps if we wrote "abcdef"[0] = 'X' that would be more obvious. (By the way,
  13591. this is a perfectly well-formed expression that achieves the same result.) The
  13592. problem is even more obscure if pc is passed to a function that intends to
  13593. modify what pc points to (as the first argument to strcpy, for example).
  13594. Based on its name you would expect a string literal to literally be stored
  13595. exactly as written. In fact, most of us probably really think of string
  13596. literals as being constants; and that's not unreasonable. The problem is that
  13597. over the years some compilers have stored them in read-only memory while
  13598. others placed them in read-write memory. As a result, ANSI C permits either,
  13599. so it's your problem either to avoid intentionally modifying such strings or
  13600. to know which of your target compilers permit this.
  13601. Since DOS has no memory protection capability, DOS-based compilers always
  13602. allow strings to be overwritten. More sophisticated systems either force or
  13603. allow strings to be placed in read-only memory at runtime. (Apparently, 32-bit
  13604. compilers running under DOS extenders can also take advantage of the segment
  13605. protection of the Intel 386 architecture.)
  13606. 3. None of my compilers gave any warnings. However, PC-Lint produced the
  13607. following interesting message:
  13608. line 10 - Warning: Constant value Boolean
  13609. It's telling me that str == "end" is a constant expression which, therefore,
  13610. can be evaluated at compile-time meaning that either the true or false path
  13611. will be forever ignored. Since this presumably was not our intent, what's the
  13612. real problem?
  13613. What we really want to do is to compare the null-terminated character array
  13614. str with the string "end". Now as we know, in almost all cases where we use
  13615. the name of an array (or, in fact, any expression that designates an array)
  13616. that expression is converted to a pointer to the first element. Therefore, the
  13617. expression str becomes &str[0] and the address of an array's element is a
  13618. constant throughout the life of that array. (This is true even for automatic
  13619. arrays. Granted that each time the same auto object is allocated it can be in
  13620. a different place in memory, but for any one of its lives it will stay in the
  13621. same location.) Similarly, "end" becomes &"end"[0] since "end" is the 'name'
  13622. of an unnamed array of 4 chars.
  13623. As a result, (str == "end") becomes (&str[0] == &"end"[0]) which is a constant
  13624. expression. And since, by definition, objects don't overlap in storage (unions
  13625. excepted), the address of any element of one array can never be equal to that
  13626. of an element in a different array. The resulting type of this expression is
  13627. int (by definition) and its value is 0 (false).
  13628. Of course, what we really must use is strcmp(str, "end") == 0.
  13629. 4. In the previous problem we learned that what is really going on is that the
  13630. addresses of the first character in each string is being compared, not the
  13631. value of the strings. So why are the addresses the same with some compilers
  13632. and not others?
  13633. Whether identical string literals occupy the same memory (they are pooled) or
  13634. are treated as being distinct is up to the implementation. It is more work for
  13635. a compiler to compare every string literal token with every other. Most
  13636. compilers simply treat each string as being distinct. A few provide options to
  13637. go either way.
  13638. Consider the following program:
  13639. #include <stdio.h>
  13640.  
  13641. main()
  13642. {
  13643. printf("%p %p\n", "abc", "abc");
  13644. }
  13645.  
  13646. 1bca:0026 1bca:0026 /* only one
  13647. copy */
  13648. 1B20:0046 1B20:0042 /* two copies
  13649. */
  13650. Compilers then, can support one or more of the four possible combinations of
  13651. string literals being read-only or writeable, and pooled or not.
  13652. 5. When an array is passed to a function, what is actually passed is a pointer
  13653. to its first element. No information is passed about the number of dimensions
  13654. or their sizes. Once the called function takes control, the shape information
  13655. for that array is lost -- the function simply knows it has a pointer to some
  13656. type.
  13657. If we write the function definition in a different, but equivalent, form, the
  13658. problem should be obvious:
  13659. /*11*/ void f(int *x) {...}
  13660. Now we have explicitly admitted that x really is a pointer to the first
  13661. element of the array passed in. Therefore, sizeof(x) gives us the size of that
  13662. pointer, not the underlying array. Now we could use sizeof(*x), but that only
  13663. gives us the size of the object underlying x (an int) not the size of the
  13664. whole underlying array.
  13665. In the small memory model and in 32-bit mode, pointers and ints are the same
  13666. size (16 and 32 bits, respectively). In large mode, pointers are 32 bits and
  13667. ints are 16.
  13668. So how can we solve the problem correctly if sizeof won't do? If the array
  13669. passed were a null-terminated char array we could use strlen. For all other
  13670. array types we would have to pass in the array size as an argument or use a
  13671. global variable, unless there was some special terminating value we could
  13672. check for.
  13673.  
  13674.  
  13675.  
  13676.  
  13677.  
  13678.  
  13679.  
  13680.  
  13681.  
  13682.  
  13683.  
  13684.  
  13685.  
  13686.  
  13687.  
  13688.  
  13689.  
  13690. On The Networks
  13691.  
  13692.  
  13693. Special Issue: Network News
  13694.  
  13695.  
  13696.  
  13697.  
  13698. Sydney S. Weinstein
  13699.  
  13700.  
  13701. Sydney S. Weinstein, CDP, CCP is a consultant, columnist, author, and
  13702. president of Datacomp Systems, Inc., a consulting and contract programming
  13703. firm specializing in databases, data presentation and windowing, transaction
  13704. processing, networking, testing and test suites, and device management for
  13705. UNIX and MS-DOS. He can be contacted care of Datacomp Systems, Inc., 3837
  13706. Byron Road, Huntingdon Valley, PA 19006-2320 or via electronic mail on the
  13707. Internet/Usenet mailbox syd@DSI. COM (dsinc!syd for those who cannot do
  13708. Internet addressing).
  13709.  
  13710.  
  13711. It now has been a year that I have been writing this column for The C Users
  13712. Journal. I think it's time for an update on what the networks are and how you
  13713. can get on the ones that this column references. In last January's issue, I
  13714. discussed the internet vs. the Internet, USENET, Network News, and freely
  13715. distributed software. I will review and update this information before
  13716. crossing into some new ground. Again, this column is my personal view of both
  13717. the history and the current state of the networks. It is not intended to be a
  13718. proper chronological history of USENET or Network News.
  13719.  
  13720.  
  13721. Network News: A Review
  13722.  
  13723.  
  13724. In this column, I refer to several USENET Network News groups, including
  13725. comp.sources.unix, comp.sources.misc, comp.sources.games, and alt.sources. I
  13726. mention software, usually in C, that has been posted to these groups that
  13727. might interest CUJ readers. This leads to several questions: What is USENET
  13728. Network News? How do I get to the software? What is the phone number to
  13729. download the software?
  13730. USENET is a moniker given to the UNIX Users Network. It's an outdated name,
  13731. but still survives. USENET started as a small collection of UNIX computers
  13732. connected with UUCP. Today, USENET is an amorphous mass of computers connected
  13733. by a variety of protocols. The computers run different operating systems,
  13734. including many of the popular personal computer operating systems. What makes
  13735. a computer a member of USENET (or at least connected to USENET)? Generally a
  13736. computer is considered connected to USENET if it can exchange electronic mail
  13737. with other computers that are connected to USENET. A rather broad definition,
  13738. and one that has blurred lately as other networks exchange mail with USENET
  13739. computers via gateways. Perhaps it should be restricted to computers where you
  13740. can exchange electronic mail with other computers on USENET without going
  13741. through a gateway. This is a revised version of the older definition of a
  13742. collection of computers that communicate using UUCP.
  13743. Joining USENET is easy. All you do is find a member who is willing to connect
  13744. you via his or her computer. Sort of a Catch-22 -- you need to know who is
  13745. already a member to become one. A partial list of connected sites is published
  13746. only via USENET Network News, which you cannot receive until you are
  13747. connected. Read on, anyway, and I'll present the solution to this Catch-22.
  13748. If USENET is an electronic mail network, what is Network News? Most readers
  13749. might be familiar with the concept of a Bulletin Board System. A BBS is a
  13750. computer that holds a database of messages posted by other members of the
  13751. system. Your computer dials the BBS computer, and you read and post messages.
  13752. Generally these are local systems, and usually small. Message bases range in
  13753. the megabytes of information. One collection of BBSs has formed a worldwide
  13754. organization called Fidonet that exchanges messages between systems, but most
  13755. are small computers run by individuals. Network News is similar and yet
  13756. different from a BBS. With Network News, users post messages, but that is
  13757. where the similarity ends. On a BBS when you post a message, that message sits
  13758. on the local computer to be read by others until it expires and is deleted.
  13759. With USENET Network News the entire message base exists on every computer in
  13760. the network. Thus the message you post is sent to every other computer. You
  13761. read messages on your own computer, even though they were posted by others
  13762. elsewhere.
  13763. How do USENET and USENET Network News differ? USENET is any computer connected
  13764. for electronic mail to other USENET computers. A subset of these computers
  13765. also agree to exchange one or more of the categories of Network News.
  13766. Currently there are about 1,000 different news categories, called newsgroups.
  13767. The newsgroups are broken down into several hierarchies. These include the
  13768. traditional major hierarchies of news, comp, rec, sci, soc and talk; regional
  13769. hierarchies such as na, usa, ba, pa, nj, and specialized hierarchies such as
  13770. bionet, biz, bit, unix-pc, and u3b. The major hierarchies are the widest
  13771. distributed, accessing over several hundred thousand computers worldwide. The
  13772. regional hierarchies are used for messages only of regional interest, such as
  13773. North America (na), the United States (usa), the San Francisco Bay Area (ba),
  13774. the state of Pennsylvania (pa), or New Jersey (nj), just to name a few. The
  13775. specialized hierarchies serve communities with special interests.
  13776. Each hierarchy has its own set of rules, which are enforced by consensus.
  13777. USENET has no governing body, just a set of guidelines that individual
  13778. computer owners or administrators follow as they see fit. It seems to work
  13779. most of the time, as the net runs without too much chaos. There is even a
  13780. hierarchy that runs without rules, called alt.
  13781. You are a recipient of network news if you subscribe to one or more of the
  13782. newsgroups in any of the hierarchies. Some sites only receive a handful of the
  13783. groups, and some receive most or all of the groups. This involves a large
  13784. amount of information -- more than 20 megabytes of new postings every day.
  13785. This is up from about two megabytes of posting per day only two years ago. At
  13786. 20 megabytes per day, each site can only keep a small portion of the feed on
  13787. line at a time, and at that, only a few days worth.
  13788. With so much information coming in each day, it would seem like a lot of work
  13789. just maintaining it, or finding something worthwhile to read. It's not that
  13790. bad. The software to run the news system controls itself almost automatically.
  13791. It includes facilities to send only those groups to which you subscribe, as
  13792. well as expiring old articles to recover space. However, being a major site in
  13793. the USENET Network News distribution system requires a large amount of disk
  13794. space and modem time.
  13795. The source distribution groups may interest readers of this column. These
  13796. groups are generally grouped under the comp hierarchy in a collection called
  13797. sources, thus the names comp.sources.unix and comp.sources.misc. Originally
  13798. comp.sources.unix was for freely distributable sources designed to run on UNIX
  13799. systems. Many of the sources posted there now are also able to run on personal
  13800. computers and other operating systems, but all can run on UNIX. The other
  13801. sources groups currently in the comp hierarchy are: amiga -- for software
  13802. specific to amiga systems; atari.st -- for software specific to these
  13803. computers; games -- restricted to game software which is in turn restricted to
  13804. this group; mac -- software specifically for Apple Macs; misc -- general
  13805. software, not necessarily for UNIX systems; sun -- for Sun Microsystems
  13806. workstations; and x -- software for the X windowing system. This column
  13807. generally restricts itself to the unix, misc, and games groups.
  13808. Authors submit their sources to a moderator, who is the only person allowed to
  13809. post to the group. The moderator bundles the sources for distribution, checks
  13810. that they are complete, and posts them. The moderator also assigns volume and
  13811. issue numbers to each of the postings. A submission might take several issues,
  13812. because an issue is limited to about 60,000 bytes. The moderator also posts
  13813. periodic indices of the sources posted to his group. No discussion is allowed
  13814. in these groups, and only postings approved by the moderator are accepted.
  13815. Because of the high "signal to noise ratio" in these groups, some computer
  13816. sites around the world save the sources for future access. These sites are
  13817. called archive sites. Each archive site decides what groups to archive and how
  13818. long to keep them. Contact these archive sites to obtain the sources mentioned
  13819. in the column. You must contact the archive sites because the individual
  13820. members of USENET, unless they archive these groups, will have deleted the
  13821. sources to make room for the newer postings, usually within a week of the
  13822. original posting.
  13823.  
  13824.  
  13825. 20Mb A Day
  13826.  
  13827.  
  13828. A bit of simple math yields some impressive numbers. If your computer
  13829. exchanges Network News with two neighbors (a small site), you are receiving
  13830. 20Mb a day for a full feed, and sending that 20Mb each day to the second site.
  13831. Network News broadcasts all articles to all sites that have not yet received
  13832. them. Any article you get from one site, you send to all others that you are
  13833. connected to and that have not received the article. Now, 20Mb per day on a
  13834. phone line at 2,400 bps (240 characters per second maximum speed) yields
  13835. approximately 24.25 hours on the phone per day. This isn't possible as there
  13836. are only 24 hours in a day. In addition UUCP doesn't achieve the 240 cps
  13837. maximum for 2400 bps due to delays and overhead. This problem is solved by
  13838. sending articles in compressed batches. The compression is usually about 50-70
  13839. percent effective, cutting that 24.25 hours to eight to twelve hours. Still a
  13840. big phone bill. How do sites cut that down even further? Most big sites run
  13841. special modems, such as V.32 (at 960cps) or Telebit Trailblazers (at
  13842. 1,200-1,400cps), which cuts it down to about two to three hours per day. It
  13843. you talk to two sites, expect that to be four to six hours total.
  13844. A major site might exchange news with 20 or more neighbors. They can
  13845. accomplish this several ways -- using a bank of modems, exchanging partial
  13846. feeds of selections from the list of 1,000 newsgroups, or using the Internet.
  13847.  
  13848.  
  13849. The Internet
  13850.  
  13851.  
  13852. Modems and serial protocols are not the only way to connect computers. Many
  13853. larger computer sites, including corporations, universities, and government
  13854. installations, have banded together to form a large network called the
  13855. Internet. This network uses the TCP/IP protocol, designed by the Department of
  13856. Defense and its contractors, to communicate over very high-speed links. Usual
  13857. links for Internet sites range from 9,600bps (about 1,000cps), to 56kbps
  13858. (about 5,400 cps), to 1.544Mbps. A full news feed at 1.544 Mbps doesn't take
  13859. very long at all -- just a couple of minutes if it were sent as a big batched
  13860. file transfer. The newest network connections that are the backbone of the
  13861. Internet are switching from 1.544Mbps to 45Mbps, allowing for even greater
  13862. capacity.
  13863. Network News on the Internet uses a special protocol on top of TCP/IP for more
  13864. efficient distribution. This is NNTP or the Network News Transfer Protocol.
  13865. Switching from a backbone network of mostly UUCP connections to using NNTP
  13866. over the Internet has improved the flow of network news and allowed for its
  13867. dramatic growth over the past few years. Just two years ago, an article took
  13868. several days to propagate to all the USENET computers (at that time tens of
  13869. thousands of computers). Now it takes less than a day -- and usually only
  13870. several hours -- to reach most of the hundreds of thousands of computers.
  13871.  
  13872.  
  13873. The Catch-22 Revisited
  13874.  
  13875.  
  13876. I promised to explain how you too can join the systems on USENET. It isn't
  13877. that hard, and you can use several approaches.
  13878. First, you can try to find some site near you that is already connected, and
  13879. then connect to them. You might begin with local user groups. One of the
  13880. newsgroups is comp.mail.maps, a collection of a list of sites on USENET. As I
  13881. have offered in the past, if you send me a self addressed envelope with
  13882. sufficient postage, I will run a program I have that will search the maps for
  13883. sites in your area code. Those that live in major cities should provide two
  13884. stamps, other areas need only one stamp. This list will include the site
  13885. contact person for each of the sites in your area code. (Please provide your
  13886. area code for me.) Many of them may not be willing to allow you to connect,
  13887. for various reasons, but usually you can find one who will cooperate. These
  13888. maps only list sites that use the UUCP protocol, so your computer must be able
  13889. to communicate using UUCP. If you have access to electronic mail, it is easier
  13890. for me to respond to electronic mail for the request. Note that you can reach
  13891. me via AT&T Mail, Compuserve, Easynet, MCI Mail, or Sprintmail. Ask your
  13892. network administrator how to send mail to an Internet address.
  13893. A second approach is to connect via a major site that sells reconnect
  13894. services. This would include sites like mine (Datacomp Systems, Inc,
  13895. 215-947-9900), UUNET Communications Services (703- 876-5050), or Portal
  13896. Communications Company (408-973-9111). Each offers UUCP connections for a fee.
  13897. These fees vary by the amount of information (time connected) transferred and
  13898. can range from about $20 a month to several hundred dollars per month.
  13899. A third method is to read network news on another computer that is already
  13900. connected. Several "public access" sites exist. They vary from free, to just a
  13901. couple of dollars a month, to several dollars per hour. Portal is an example
  13902. of one of these public access sites. Another is the Whole Earth 'Lectronic
  13903. Link (415-332-4335). Both charge for their connect time. Public access sites
  13904. also have their own hierarchy of network news and list many almost free
  13905. connections. Generally the number of modems available for customers goes up
  13906. with the price of the connection.
  13907.  
  13908.  
  13909. Back to USENET
  13910.  
  13911.  
  13912.  
  13913. Okay, enough on News. After all, USENET started with electronic mail. But how
  13914. do you send electronic mail via USENET? If you've read enough of my columns,
  13915. you see in the author's reference section an address called an electronic mail
  13916. address. This is the key to sending mail via USENET. My address is syd@DSl.COM
  13917. as listed in that section. How does that relate to USENET?
  13918. UNIX computers have included mail facilities from almost the beginning. This
  13919. mail allowed for sending messages from one user to another using their login
  13920. names as the address. When UUCP came along, the address was extended to
  13921. include the site name and the login name separated by an '!', as in my UUCP
  13922. address dsinc!syd. However, to send to dsinc!syd, you had to be directly
  13923. connected to dsinc. So dsinc had to be an entry in your UUCP node table in
  13924. order for your computer to call my computer and exchange the mail.
  13925. As the number of computers grew, this method became impractical. So the
  13926. concept of routing was introduced. If you talked (had the site in your UUCP
  13927. site file) to abc, and I talked to abc, then you could send me mail as
  13928. abc!dsinc!syd. Extend this and you end up with the long routes associated with
  13929. UUCP such as abc!def!ghi!jkl!mno!pqr!dsinc!syd. Each is a hop on the message's
  13930. journey from your machine to mine. But how do you determine, in advance, the
  13931. routing that the message needs to take to efficiently travel from your
  13932. computer to mine? Some sites might only exchange mail once a week. If you
  13933. choose one of those as an intermediate hop, the message could be delayed for a
  13934. long time.
  13935. USENET Network News comes to the rescue. The newsgroup comp.mail.maps contains
  13936. periodic postings of information on the connections of each site registered on
  13937. USENET. (Unfortunately, not all sites bother to register, although the process
  13938. is painless and free.) A freely distributable computer program called
  13939. pathalias takes this data and makes a file listing how to get from your site
  13940. to each of the other sites in the maps. It produces a rather large file. With
  13941. this paths file, the mail transport agent (the program that delivers the mail)
  13942. on your computer can route the message for you, speeding it on its way.
  13943. If you only have a couple of connections, there is an easier way. You can
  13944. always send the message to one of your neighbors, who can route it for you.
  13945. This is known as hop routing. If your neighbor cooperates, hop routing can be
  13946. a very effective method. Each neighbor then sends the message to a more
  13947. intelligent neighbor until someone knows how to route it. This method is
  13948. recommended for small sites, as it gives the larger sites a chance to optimize
  13949. the delivery of the messages. The mail software supports hop routing via the
  13950. concept of a smart-host. Your smart-host is a site you send the mail that you
  13951. don't know how to route.
  13952. So far so good, this handles dsinc!syd. What do I do with syd@DSI.COM, which
  13953. is not a UUCP address? UUCP addresses are not guaranteed to be unique. Two
  13954. sites could easily choose the same name, which can and does lead to problems.
  13955. Secondly, not all sites use UUCP for delivery.
  13956. Over the years, the Internet has developed a mail addressing scheme that uses
  13957. the notation user@site, where user is either their login name, or some
  13958. nickname that the site will translate into their login name. Site is a
  13959. dot-separated list of items that make up a fully qualified domain name (FQDN).
  13960. FQDNs are advantageous because they are guaranteed to be unique. Also, they
  13961. don't need to be routed since each site can deliver them using hop routing.
  13962. Some UUCP sites can't handle FQDNs. The maps give a list of sites that can.
  13963. You just forward them to your smart-host or to one of those sites.
  13964. Thanks to the interconnection and speed of the Internet backbone, using FQDNs
  13965. has several advantages. Mail is now delivered with very few hops, making for
  13966. much quicker delivery. The Internet doesn't need maps since it can now
  13967. distribute the lookup over the entire network. A small explanation will show
  13968. why the UUCP domain doesn't always work. My base computer is dsinc.dsi.com. If
  13969. you are somewhere on the Internet, (anywhere in the world) and want to contact
  13970. me at that machine, your computer will do a Domain Name Service query, asking
  13971. who is dsinc.dsi.com. It does this by sending a request to its local (on site)
  13972. program for nameservice queries asking that program who is dsinc.dsi.com. That
  13973. program looks in its in-memory cached information for that name. If it finds a
  13974. match, it returns my address. If it doesn't find a match, it looks for dsi.com
  13975. to see if it finds a match. Again, if no match, it looks for com (one of the
  13976. root domains). There is a fixed list of servers for the com domain, each of
  13977. which is given the registration information for every "second-level domain"
  13978. such as dsi. The nameservice query program asks that server for information on
  13979. dsi.com and gets told which machine to ask to find out about machines in the
  13980. dsi.com domain. It then makes one more request to that machine for
  13981. dsinc.dsi.com's address. In two queries, it has my address. It then connects
  13982. directly to my address, very much the way you dial a phone to connect directly
  13983. to someone else's phone. Thus, every machine on the Internet is a single hop
  13984. from every other.
  13985. So far, so good, but some FQDNs are not on the Internet, including the popular
  13986. compuserve.com. Domains not directly on the Internet list a mail forwarder for
  13987. themselves, which is on the Internet. Their mail is delivered to the mail
  13988. forwarder who handles the final delivery.
  13989.  
  13990.  
  13991. If It Were That Easy
  13992.  
  13993.  
  13994. Looks easy. All you need to do is send the mail up the chain to a smart site
  13995. and bingo, it's delivered, right? Wrong, unfortunately. The UUCP world and the
  13996. Internet world have a small problem talking to each other. This problem is
  13997. routing. In the UUCP world, each message is source routed, as in
  13998. abc!dsinc!syd. In the Internet world, each message is directly routed, as in
  13999. user@site. What if you want to send a message to a site off of mine, called
  14000. abc. Would that be abc!user@dsi.com, or abc@dsi.com!user or dsi.com!abc!user,
  14001. or what? There is no standard because there is no consensus of software. Some
  14002. software evolved doing it one way, some another. There is a recommendation but
  14003. no way to enforce that recommendation. On the Internet, if you don't follow
  14004. the rules, you can be disconnected. Sort of like having your phone removed for
  14005. misuse. UUCP hops are cooperative, not by edict of a ruling body. There is no
  14006. standard answer. The recommendation is to use abc!user@dsi.com, but many sites
  14007. will try to send that to the site abc and then from there to user@dsi.com.
  14008. Some sites support the notion of dsi.com!abc!user, and some don't. Others
  14009. pretend to be user@abc.UUCP and let other sites figure it out. Sometimes that
  14010. works, sometimes it doesn't. If the site doing the routing understands that
  14011. .UUCP is a fake domain, governed by the USENET maps and not by the normal
  14012. Domain Name Service lookup servers, it works. Others just give up and throw
  14013. the message away.
  14014. If you ask your neighbors what they can handle, and try to coordinate things,
  14015. you can usually works around these problems. Just don't expect the solution
  14016. that works for you and your neighbors to work everywhere.
  14017.  
  14018.  
  14019. The Commonly Asked Questions
  14020.  
  14021.  
  14022. It's time to give some quick answers to the most common questions I am asked.
  14023. 1. Whats the phone number for USENET? Oh boy, my favorite! Please see the
  14024. paragraphs above about how to find a site to connect with. There is no single
  14025. phone number, but many options on how you can connect. Some very inexpensive
  14026. or free, some costly.
  14027. 2. How do I join the Internet? Most sites don't have to. Usually Network News
  14028. and USENET access is sufficient. But if you really need direct access to many
  14029. other computers for sharing information, and have the money to support the
  14030. connection (varies by region, but usually not less than $1,000/month with
  14031. installation charges upwards of $10,000), there are regional networks in each
  14032. part of the country that you would connect to. Send me mail if you are
  14033. interested and I can try to find out the regional contact in your area.
  14034. 3. Where is the nearest archive site? This is a hard question, partially
  14035. because near is relative, and partially because it varies based on what
  14036. newsgroup is desired. Three popular archive sites are osu-cis (a UUCP
  14037. reachable archive site listed in the maps), Portal, and UUNET. Ohio State is a
  14038. free archive; Portal and UUNET charge for access.
  14039. 4. Can you send me a posting you described in a prior column? Usually I can't.
  14040. My site is not an archive site. After I write about a posting, it gets deleted
  14041. (unless I keep it to use it on one or more of our computers). By the time the
  14042. column appears, the posting is long deleted. So even if I could make floppies
  14043. or tapes for you, the original file is gone. All I can do is refer you to an
  14044. archive site.
  14045. 5. How can I find the electronic mail address for a user at some site? The
  14046. best way is to call that person and ask for his or her electronic mail
  14047. address. If you post a request to the network, it gets sent to each of several
  14048. hundred thousand computers, costing many thousands of dollars of everyone's
  14049. money. Make a simple phone call and ask.
  14050. Lastly, remember that Network News is not a BBS. If you post something to a
  14051. newsgroup, it is broadcast to hundreds of thousands of computers all over the
  14052. world. Some of the readers will not have English as their native language,
  14053. even though most postings are in English. Therefore, think twice before you
  14054. write something. Check your facts, try not to inflame others, and remember
  14055. that the comment that you took personally may only be the author's problem in
  14056. phrasing a reply in a short, written English message. USENET Network News is a
  14057. cooperative venture, and we all need to cooperate for it to work.
  14058. There will always be those that abuse available resources, and those that are
  14059. quick to reply, insulting others. However, if we try to ignore them, the
  14060. bandwidth saved may let USENET and Network News survive another year. Good
  14061. luck finding a connection to USENET. When you do, say hello. Most of us are
  14062. more than willing to help.
  14063.  
  14064.  
  14065.  
  14066.  
  14067.  
  14068.  
  14069.  
  14070.  
  14071.  
  14072.  
  14073.  
  14074.  
  14075.  
  14076.  
  14077.  
  14078.  
  14079.  
  14080.  
  14081.  
  14082.  
  14083.  
  14084.  
  14085.  
  14086.  
  14087.  
  14088.  
  14089.  
  14090.  
  14091.  
  14092.  
  14093.  
  14094.  
  14095. Questions & Answers
  14096.  
  14097.  
  14098. YACC And Lex
  14099.  
  14100.  
  14101.  
  14102.  
  14103. Ken Pugh
  14104.  
  14105.  
  14106. Kenneth Pugh, a principal in Pugh-Killeen Associates, teaches C language
  14107. courses for corporations. He is the author of C Language for Programmers and
  14108. All On C, and was a member on the ANSI C committee. He also does custom C
  14109. programming for communications, graphics, image databases, and hypertext. His
  14110. address is 4201 University Dr., Suite 102, Durham, NC 27707. You may fax
  14111. questions for Ken to (919) 493-4390. When you hear the answering message,
  14112. press the * button on your telephone. Ken also receives email at
  14113. kpugh@dukemvs.ac.duke.edu (Internet).
  14114.  
  14115.  
  14116. Q
  14117. What are YACC and Lex? I am currently developing a mapping package that reads
  14118. xyz values, posts them on a map grid, contours them, and does various other
  14119. manipulations with the data. It turned out to be that the number of options
  14120. you have with the mapping part is enormous, e.g. title blocks, which pen to
  14121. use, linear grid or geographic, etc...
  14122. I am using with all my programs the getopt function for command line options
  14123. processing. I don't think setting all the available possible options from the
  14124. command line would be practical.
  14125. I am looking for a utility that would allow me to create my own macro language
  14126. with specific keywords that accepts options, sort of like many getopts. This
  14127. would obviously call for another utility to parse my macros.
  14128. Will YACC and Lex help? If you think they will, I would appreciate a quick
  14129. review on what they are and how they can solve my problems. If they won't, can
  14130. you please suggest something else.
  14131. Sami Kurdy
  14132. United Arab Emirates
  14133. A
  14134. Lex can help you with your problem. What you want to do is to recognize
  14135. different strings of characters that satisfy certain rules.
  14136. The Lex (LEXical analyzer) program produces a C source file that looks for
  14137. specified sequences of characters and produces actions when it finds the
  14138. sequences. The output of Lex is compiled into your C program. The resultant
  14139. program may be longer and slower than one you could write, but it will take
  14140. you less time to develop it.
  14141. Let me summarize some of the features of Lex. You specify any number of
  14142. regular expressions. For example, abcd matches the string abcd. [abcd] matches
  14143. a single character that is a or b or c or d. [abcd]* matches a string
  14144. containing any combination of a, b, c, and d, such as abcd, bbcda, or
  14145. aabbbbcccc. [a-zA-Z]* matches any string containing upper or lower case
  14146. letters, such as ABCabcd.
  14147. For each regular expression, you specify a C code fragment to be executed when
  14148. the expression is found. For example:
  14149. [abcd] printf("Found a
  14150. string of abcd");
  14151. To resolve problems in recognizing strings, Lex uses the longest string
  14152. possible that matches. If there are still ambiguities, it executes the first
  14153. rule that matches.
  14154. Lex creates a C routine called yylex( ). When you call this routine from your
  14155. program, it reads the input for characters. When it recognizes a specified
  14156. character sequences, it performs the matching action, if any. For example, if
  14157. you were using it to recognize keywords, you could have it return the
  14158. corresponding numeric token values, which you could then process. You might
  14159. specify this as:
  14160. #define IDENTIFIER 10
  14161. #define PLUS 9
  14162. #define INTEGER_NUMBER 11
  14163. #define SET_COMMAND 12
  14164.  
  14165. [_a-zA-Z][_a-zA-Z0-9]* return IDENTIFIER;
  14166. '+' return PLUS;
  14167. [0-9]* return INTEGER_NUMBER;
  14168. SET return SET_COMMAND;
  14169. Lex may be sufficient for your needs, if the command language consists of a
  14170. number of commands with optional parameters following. The string that Lex has
  14171. matched is kept in an array called yytext[], so you could call sscanf to do
  14172. conversion of numeric values that were found. If you used the above values,
  14173. you might write a code fragment that looked like the code in Listing 1.
  14174. This would look for the word SET followed by an integer number. If an integer
  14175. number did not follow SET, then a message would be generated and the program
  14176. would exit. With a simple command language, using Lex to analyze the input for
  14177. commands would decrease the amount of your coding.
  14178. From the above, you see that Lex can be used to generate tokens. YACC (Yet
  14179. Another Compiler Compiler) can use these tokens with specified rules to
  14180. produce additional actions. You could write your own compiler, if you wish,
  14181. using YACC to do the parsing.
  14182. The YACC set of rules can be more complex than the Lex rules, since one rule
  14183. can depend on the results of other rules. The rules can specify a grammar,
  14184. rather than simple pattern matching. The general form of YACC input is:
  14185. declarations
  14186. %%
  14187. rules
  14188. %%
  14189. subroutines
  14190. The rules look somewhat like the Backus-Naur Form (BNF) for a programming
  14191. language. For example, the BNF rules for C can be found in the ANSI C
  14192. specification or in Appendix D of All on C. Using the above Lex tokens, you
  14193. might make up YACC specifications as:
  14194. %token IDENTIFIER
  14195. %token PLUS
  14196. %token INTEGER_NUMBER
  14197. %token SET_COMMAND
  14198. %%
  14199. ADD_EXPRESSION : IDENTIFIER PLUS IDENTIFIER
  14200. { printf("Add expression")} ;
  14201. SET_EXPRESSION : SET_COMMAND INTEGER_NUMBER
  14202.  
  14203. %%
  14204. yylex() /* Generated by lex or your own */
  14205. ;
  14206. Developing a language (with or without YACC) is a bit complicated. However, if
  14207. your command language involves computing expressions or control loops, using
  14208. YACC is simpler than trying to write your own parser.
  14209. Language construction is a bit beyond the scope of this column. Perhaps Robert
  14210. or P.J. can get an article written about that topic. (KP)
  14211. Q
  14212. I am trying to develop a TSR screen saving routine, but I am running into
  14213. problems. I have an IBM compatible, 80286 AT computer with VGA graphics. I
  14214. would like to be able to save a screen from the DAK soul snatcher program VID.
  14215. EXE. Can you offer a solution in Turbo C++?
  14216. Also, what is a debugger (like Turbo Debugger), and what is it used for?
  14217. Matt Nowicki
  14218. Millersville, MD
  14219. A
  14220. There are many aspects to TSR programs, which would greatly exceed the space
  14221. in this column. I suggest you obtain one of the libraries of TSR functions
  14222. (e.g. Blaise Tools), or check the past articles in The C Users Journal or
  14223. Computer Language for how to create TSRs.
  14224. A debugger allows you to step through your C program, either by one line at a
  14225. time or by blocks of code. At each step, you can examine the values of
  14226. variables. You can set a breakpoint in the code, which will allow you to
  14227. execute all the code up until that point and then permit you to go into a
  14228. single step mode. A debugger is an excellent way to learn how C works, as well
  14229. as a way of checking out small routines or programs.
  14230. Debuggers vary in their features. For example, most debuggers permit you to
  14231. set breakpoints on a particular line of code. Whenever your program reaches
  14232. that line, a break will occur and you need to type a key to resume execution.
  14233. Other debuggers allow you to specify an expression (such as "i = = 50"), which
  14234. must be true for the break to occur. This can eliminate a lot of keystrokes.
  14235. (KP)
  14236.  
  14237.  
  14238. qsort Comparison Function
  14239.  
  14240.  
  14241. Two months ago, Firdaus Irani of Chestnut Hill, MA wrote about an error he got
  14242. when trying to compile the program in Listing 2. The error read:
  14243. Turbo C++ Version 1.00 Copyright (c) 1990 Borland
  14244. International q_sort.c:
  14245. Error q_sort.c 12: Type mismatch in parameter
  14246. '__fcmp' in call to 'qsort' in function main
  14247. *** 1 errors in Compile ***
  14248. I checked with Robert Jervis, who is another member of the ANSI C committee.
  14249. As I expected, the error is due to a function prototype nested in a prototype.
  14250. This is the expected behavior, and it results from how function pointers in
  14251. function prototypes are evaluated.
  14252. For simple function parameters, such as ints and doubles or data pointers, if
  14253. the actual parameter is assignment compatible with the formal parameter, the
  14254. conversion takes place silently. A parameter which is a pointer to a function
  14255. has a type that includes the types of its parameters. For example, for a
  14256. function prototyped as
  14257. qsort(void *array, int element_size,
  14258. int element_count, int _fcmp(void *, void *));
  14259. ***** which is first, size or
  14260. you can call this with:
  14261. char char_array[10];
  14262. int compare_function(void *, void *);
  14263.  
  14264. qsort(char_array, 10.1, (char) 1, compare_function);
  14265. The compiler will ensure that the parameters are initialized as:
  14266. array = char_array
  14267. element_count = 10/* Converted and rounded to 10 */
  14268. element_size = 1 /* Increased to a int */
  14269. _fcmp = compare_function
  14270. However, you cannot call it with:
  14271. int compare_function(char *, char *);
  14272. since the types which the function expects differ (even though they are
  14273. assignment compatible).
  14274. Note this means that if you attempt to use strcmp( ) as a parameter to qsort(
  14275. ) and you include <string.h>, you will get this parameter mismatch error.
  14276. You could change the prototype definition to read:
  14277. int _fcmp( )
  14278. This way, it will accept a pointer to any function, regardless of type or
  14279. number of parameters. (This is ANSI, but disparaged.) (KP)
  14280.  
  14281.  
  14282. Readers' Replies
  14283.  
  14284.  
  14285.  
  14286.  
  14287. External Name Headers
  14288.  
  14289.  
  14290. I am a Staff Systems Software Engineer at Seagate Technologies. Several
  14291. engineers in my department read the journals, and find The C Users Journal to
  14292. be one of the best.
  14293. I read the question by Andreas Lang in the October 1990 issue of The C Users
  14294. Journal regarding keeping external variables in one .h file. I found a
  14295. technique in Dr. Dobb's Journal, curiously the same month of October 1990, in
  14296. a letter by Robert White. Following is a memo I wrote on this technique, using
  14297. it to solve the problem M. Lang wrote about:
  14298.  
  14299. I ran across a letter in the October 90 The C Users Journal asking how
  14300. variable declarations and the extern declarations of those variables could be
  14301. kept in sync, especially when you want to initialize the variable at compile
  14302. time.
  14303. The whole point of the technique is to declare all the information you need in
  14304. all cases in a macro call, then define the proper macro to extract those
  14305. parameters (pieces of information) that you want for the specific case and put
  14306. them in the desired format.
  14307. For the case of variable declaration, see Listing 3. You select one of the
  14308. sets of macros via a defined symbol or its lack of a definition as shown in
  14309. Listing 4. This would generate two different cases, which are shown in Listing
  14310. 5. This is the cleanest solution I have found yet to this problem.
  14311. Don bandit Gangwere
  14312. Scotts Valley, CA
  14313. The letter that Don referred to in Doctor Dobb's Journal used a similar
  14314. technique to keep enum values and corresponding strings straight. See Listing
  14315. 6.
  14316. In an include file, say table.h, are the values:
  14317. TABLE_VALUE(sunday, "SUNDAY"),
  14318. TABLE_VALUE(monday, "MONDAY"),
  14319. TABLE_VALUE(tuesday, "TUESDAY"),
  14320. To use these values, you could use the declarations shown in Listing 7.
  14321. This seems like a good idea to keep enums and strings in sync. (KP)
  14322. A response in your October Q?/A! column in The C Users Journal told Andreas
  14323. Lang that there was no solution to having one .h file serve for both
  14324. definition and externals declaration. It isn't especially elegant, but the
  14325. code in Listing 8 works for me.
  14326. This isn't original with me, but I can't remember to whom to attribute it.
  14327. Terry Shankland
  14328. Mesa, AZ
  14329. The one specific comment I would make on your coding is to avoid declaring a
  14330. structure template and a variable at the same time. For example, it ought to
  14331. look like:
  14332. struct range
  14333. {
  14334. int xmin, xmax, ymin, ymax;
  14335. };
  14336.  
  14337. GLOBAL
  14338. struct range data_range
  14339. #ifdef ALLOCATE_SPACE
  14340. = { 0, 0, 0, 0 }
  14341. #endif
  14342. ;
  14343. The next letter mentions my preference for a separate header file. Either that
  14344. or duplicating the defintion just reads better to me. For example, to keep
  14345. everything in the same source file, I might write it as shown in Listing 9. I
  14346. made the 0 value explicit for variable2, just for consistency's sake.
  14347. I find the fewer #ifdefs that I have to deal with, the easier it is for me to
  14348. understand. (KP)
  14349. I'm responding to the letter from Andreas Lang in the October issue of The C
  14350. Users Journal. Lang writes: "I am trying to keep all external variables in
  14351. just one .H file." He offers his solution but notes, "it isn't very elegant."
  14352. Lang's goal and his problem draw attention to organization, the heart of the C
  14353. language. In C, the question is always, "How shall I organize my code and my
  14354. variables for maximum readability and productivity?" At this point in my own C
  14355. career, I usually can produce the code that I need rapidly; but I spend a
  14356. great deal of time reorganizing it. My own struggle with C is less with the
  14357. syntax than with the underlying concepts of organization that are built into
  14358. the language. I still struggle with this. I have made my own efforts in the
  14359. direction that Lang is giving, with success and frustrations similar to his. I
  14360. no longer believe that the organizational goal expressed by Andreas Lang is
  14361. suited to the nature of C.
  14362. The nature of C dictates that you minimize the number of external variables. I
  14363. have determined this by trial and error. Given that code reusability is a key
  14364. reason for working in C, one strives to organize in such a way that code
  14365. re-use is simplified. I try to move new code rapidly into a temporary library.
  14366. First of all, I must organize the source in a certain way so that it is suited
  14367. to being in a library. Then, creating the library produces an ASCII file list
  14368. of public symbols in the library (the .LST file produced by TurboC is an
  14369. example). This ASCII file is an important reference tool. It should be a
  14370. concise reference to the functions and external variables in the library-ized
  14371. source. It shouldn't be littered with names that could have been eliminated by
  14372. reorganization.
  14373. The nature of C, I believe, is not suited to collecting all external variables
  14374. in a single header file. Rather, the nature of C dictates that you minimize
  14375. the number of external variables that you use. Often a variable need be shared
  14376. by only a few functions. I take this as a signal that those functions belong
  14377. together in a common source file. If this cannot be done, the variable can
  14378. probably be passed as a parameter, rather than an external. When this cannot
  14379. be done, I want the declaration of an external variable together with the code
  14380. that requires the external. The reference serves as a warning notice to myself
  14381. to be careful.
  14382. Sometimes an entire set of variables must be shared by a number of functions.
  14383. This is my clue to create a structure to house that set of variables. Not only
  14384. does this reduce the number of variables to one, it also solidifies concepts
  14385. forming in my code. The name which seems reasonable to apply to a new
  14386. structure often identifies the concept. Thus I find the organizational demands
  14387. of C help me to formulate my ideas.
  14388. The apparent redundancy of variable and function declarations used to bother
  14389. me. Now they hardly seem redundant at all. It used to bother me that function
  14390. declarations were a necessary prerequisite to function calls. Now I view the
  14391. function declarations as a valuable portion of my code. They summarize
  14392. important information in a convenient place for handy lookup.
  14393. It used to bother me that variables must be declared before they can be used.
  14394. Now I think of the variable declarations as an organizational tool. Now, the
  14395. undeclared variables of GWBASIC make me uncomfortable. BASIC provides no
  14396. built-in method of assuring that I'll be able to go back to a certain spot in
  14397. my code and double check the name of a variable, or to see what names have
  14398. been used.
  14399. The thing that bothers me now about C is that I find little discussion that
  14400. addresses the nature of C. One has to hunt everywhere, read everything, and
  14401. glean what one can. Many analyses offer solutions at the syntax level, rather
  14402. than emphasizing the underlying concepts of organization which are built into
  14403. the language.
  14404. In your reply to Lang, you write, "There is no write-once solution... You need
  14405. to repeat the prototype." This cold, impersonal rule of thumb states the
  14406. facts. But the wisdom in your personal coding habits does reflect the nature
  14407. of C: "I don't try to initialize in a header file, but I do include a copy of
  14408. the header file in the source... The compiler should check for agreement
  14409. [between the extern reference and the definition]."
  14410. "The compiler should check for agreement." This solves a problem that I have
  14411. run across, and never managed to solve. Including a copy of the header in the
  14412. source was something that, until now, seemed to me unnecessary and redundant.
  14413. Now I see it as necessary and unredundant. Thanks.
  14414. To me, your remarks on your personal coding habits address the nature of C.
  14415. Perhaps you view personal coding habits as a subjective issue. I would
  14416. disagree with that. Obviously, some aspects are personal. Generally, I think
  14417. the things that I try are personal. But when I've been forced by the language
  14418. to modify my coding habits, I know I've bumped up against the nature of C. And
  14419. when I finally make the right guess, and things start to work, I know that I
  14420. have an objective solution. I'd like to see more emphasis placed on this thing
  14421. that I call the nature of C.
  14422. Aside and apart from all of the above, let me say that I really enjoy your
  14423. column. The Q&A format is always instructive. Also, you're very brave to
  14424. publicly offer solutions to spur-of-the-moment problems. And you're very
  14425. honest to print replies from readers who have a month or more to come up with
  14426. solutions that may be even better than yours! But seriously, I keep all my
  14427. back issues of CUJ on my desk, and I always refer to your column.
  14428. Art Shipman
  14429. New York, NY
  14430. Thanks for your ideas. It makes explicit for me what I have been espousing
  14431. implicitly. I agree that there is a nature to C, as you so well explain. I
  14432. don't like to make dogmatic rules, but rather to make suggestions as to its
  14433. nature.
  14434. For example, if one finds themselves using external variables to communicate
  14435. between source files, one should check the organization of the code. If you
  14436. find yourself ever doing a cut and paste of a set of code, you should examine
  14437. why and whether a general routine can be created. If you go beyond a single
  14438. member operator (e.g. employee.hire_date.month), you should examine the
  14439. modularity of your code.
  14440. What you've described in your letter is part of the object-oriented
  14441. design/modular programming concept that is also part of the essence of C. I
  14442. have been an advocate of structures, even before structure assignment was made
  14443. part of the language. Using structures and developing functions to operate on
  14444. them is the greatest step one can take to object-oriented programming. As I
  14445. described in my talk at the C Forum in October, if you can create these
  14446. structures and functions, you can have many of the benefits of object-oriented
  14447. design, such as code reusability, without learning a new language. (KP)
  14448.  
  14449. Listing 1
  14450. ret = yylex();
  14451. switch(ret)
  14452. {
  14453. case SET:
  14454. /* Next input should be the numeric value */
  14455. ret = yylex();
  14456. if (ret != INTEGER_NUMBER)
  14457. {
  14458. printf("\n ERROR -- number not specified
  14459.  
  14460. after SET");
  14461. exit(0);
  14462. }
  14463. else
  14464. sscanf(yytext,"%d",&value);
  14465. break;
  14466. ...
  14467.  
  14468.  
  14469. Listing 2
  14470. #include <stdio.h>
  14471. #include <stdlib.h>
  14472. #include <string.h>
  14473.  
  14474. int comp(unsigned char **, unsigned char **);
  14475. unsigned char *list[] = { "cat", "car", "cab",
  14476. "cap", "can" };
  14477.  
  14478. main()
  14479. {
  14480. int x;
  14481.  
  14482. qsort(list, 5, sizeof(unsigned char *), comp);
  14483. for (x = 0; x < 5; x++)
  14484. printf("%s\n", list[x]);
  14485. return 0;
  14486. }
  14487.  
  14488. int comp(unsigned char **a, unsigned char **b)
  14489. {
  14490. return strcmp(*a, *b);
  14491. }
  14492.  
  14493.  
  14494. Listing 3
  14495. VAR( int, foo ); /* simple variable */
  14496. INIT( WORD, bar, 5 ); /* init variable */
  14497. INIT( BYTE, aaa[5], { 1,2,3,4,5});
  14498. /* init array */
  14499. TBL( BYTE, tbl[], table.h); /* init table */
  14500.  
  14501.  
  14502. Listing 4
  14503. #ifdef MAIN_FILE
  14504. #define VAR( ttt, vvv) ttt vvv
  14505. #define INIT( ttt, vvv, iii ) ttt vvv = iii
  14506. #define TBL( ttt, vvv, iii ) ttt vvv = { #include iii }
  14507. #else
  14508. #define VAR( ttt, vvv ) extern ttt vvv
  14509. #define INIT( ttt, vvv, iii) extern ttt vvv
  14510. #define TBL( ttt, vvv, iii) extern ttt vvv
  14511. #endif
  14512.  
  14513.  
  14514. Listing 5
  14515. MAIN_FILE defined
  14516.  
  14517. int foo;
  14518. WORD bar = 5;
  14519.  
  14520. BYTE aaa[5] = {1,2,3,4,5};
  14521. BYTE tbl[] = { #include table.h };
  14522.  
  14523. MAIN_FILE not defined
  14524.  
  14525. extern int foo;
  14526. extern WORD bar;
  14527. extern BYTE aaa[5];
  14528. extern BYTE tbl[];
  14529.  
  14530.  
  14531. Listing 6
  14532. #ifdef MAKE_STRING_TABLE
  14533. #define TABLE_VALUE(value, string) string
  14534. #else
  14535. #define TABLE_VALUE(value, string) value
  14536. #endif
  14537.  
  14538.  
  14539. Listing 7
  14540. #define MAKE_STRING_TABLE
  14541. char *day_strings[] =
  14542. {
  14543. #include "table.h"
  14544. };
  14545.  
  14546. #undef MAKE_STRING_TABLE
  14547. enum e_day =
  14548. {
  14549. #include "table.h"
  14550. };
  14551.  
  14552.  
  14553. Listing 8
  14554. /*** IN THE FILE GLOBALS.H ***/
  14555.  
  14556. #ifdef ALLOCATE_SPACE /* allocate space for globals */
  14557. #define GLOBAL
  14558. #define INIT(x) x
  14559. #else /* just declare externals */
  14560. #define GLOBAL extern
  14561. #defint INIT(x)
  14562. #endif
  14563.  
  14564. /*
  14565. NOTE: The INIT(x) macro won't work with aggregates
  14566. because it interprets a comma as indicating a new
  14567. macro parameter, not as part of the current
  14568. parameter. Aggregates are initialized with
  14569. #ifdef ALLOCATE_SPACE
  14570. */
  14571.  
  14572. GLOBAL
  14573. int variable1 INIT(=1),
  14574. variable2;
  14575.  
  14576. GLOBAL
  14577. int variable1 INIT(=1),
  14578. variable2;
  14579.  
  14580.  
  14581. GLOBAL
  14582. struct range
  14583. {
  14584. int xmin, xmax, ymin, ymax;
  14585. } data_range
  14586. #ifdef ALLOCATE_SPACE
  14587. = { 0, 0, 0, 0, 0 }
  14588. #endif
  14589. ;
  14590.  
  14591. /** IN THE MAIN .C FILE ***/
  14592.  
  14593. #define ALLOCATE_SPACE
  14594. #include "globals.h"
  14595.  
  14596. /* So, our inclusion becomes: */
  14597.  
  14598. int variable1 = 1,
  14599. variable2;
  14600.  
  14601. struct range
  14602. {
  14603. int xmin, xmax,; ymin, ymax;
  14604. } data_range = { 0, 0, 0, 0 };
  14605.  
  14606. /* IN ALL OTHER .C FILES */
  14607.  
  14608. #include "globals.h"
  14609.  
  14610. /* So our inclusion becomes: */
  14611.  
  14612. extern
  14613. int variable1,
  14614. variable2;
  14615.  
  14616. extern
  14617. struct range
  14618. {
  14619. int xmin, xmax, ymin, ymax;
  14620. } data_range;
  14621.  
  14622.  
  14623. Listing 9
  14624. struct range
  14625. {
  14626. int xmin; /* Comment on xmin */
  14627. int xmax; /* Etc. */
  14628. int ymin;
  14629. int ymax;
  14630. };
  14631.  
  14632. /* These are the references */
  14633.  
  14634. /* Comment on data_range */
  14635. extern struct range data_range;
  14636. /* Comment on variable1 */
  14637. extern int variable1;
  14638. /* Comment on variable2 */
  14639.  
  14640. extern int variable2;
  14641.  
  14642. /* These are the definitions */
  14643.  
  14644. #ifdef ALLOCATE_SPACE
  14645. struct range data_range = { 0, 0, 0, 0 };
  14646. int variable1 = 1;
  14647. int variable2 = 0;
  14648. #endif
  14649.  
  14650.  
  14651.  
  14652.  
  14653.  
  14654.  
  14655.  
  14656.  
  14657.  
  14658.  
  14659.  
  14660.  
  14661.  
  14662.  
  14663.  
  14664.  
  14665.  
  14666.  
  14667.  
  14668.  
  14669.  
  14670.  
  14671.  
  14672.  
  14673.  
  14674.  
  14675.  
  14676.  
  14677.  
  14678.  
  14679.  
  14680.  
  14681.  
  14682.  
  14683.  
  14684.  
  14685.  
  14686.  
  14687.  
  14688.  
  14689.  
  14690.  
  14691.  
  14692.  
  14693.  
  14694.  
  14695.  
  14696.  
  14697.  
  14698.  
  14699.  
  14700.  
  14701.  
  14702.  
  14703. Object-Oriented Design with Applications
  14704.  
  14705.  
  14706. Alex Lane
  14707.  
  14708.  
  14709. Alex Lane is the Senior Technical Writer in the Languages Business Unit at
  14710. Borland International. This article was excerpted from Turbo C++ by Example
  14711. (M&T Books, 1990) and has been reprinted with the permission of M&T Books
  14712. (415/366-3600).
  14713.  
  14714.  
  14715. The object model is taking the programming world by storm, to the extent that
  14716. the technology for implementing object-oriented systems is outstripping the
  14717. ability of many programmers to use it effectively. Using a new technology
  14718. effectively involves adopting new design techniques, not simply using new
  14719. tools. Grady Booch's Object-Oriented Design with Applications is a practical
  14720. guide to constructing object-oriented systems.
  14721. The book's emphasis is on the pragmatic, for although Booch does discuss the
  14722. fundamental concepts of the object model and introduces the reader to a
  14723. notation system for object-oriented design, most of the book focuses on
  14724. nuts-and-bolts discussions of object-oriented system design. Concepts, the
  14725. method, and applications are, in fact, the three major sections of the book.
  14726. The audience for the book encompasses both computer professional -- including
  14727. software engineer, analyst, and manager -- and student.
  14728.  
  14729.  
  14730. Concepts
  14731.  
  14732.  
  14733. The discussion of concepts starts with an examination of complexity. Booch
  14734. tackles the issue of what makes software complex and links complexity directly
  14735. to the "software crisis." He lists five attributes of complex systems and
  14736. discusses the role of both algorithmic and object-oriented decomposition to
  14737. help cope with this complexity.
  14738. Booch then embarks on a definition of the object model that discusses its
  14739. evolution, its elements, and its applications. This model is a "language-free"
  14740. model, in that it is not predicated on any one language, but rather abstracts
  14741. the model from existing concepts. Booch then discusses what is and is not an
  14742. object and a class, discusses the relationships among objects, among classes,
  14743. and between objects and classes. The first section ends with an important
  14744. chapter on classification.
  14745.  
  14746.  
  14747. The Method
  14748.  
  14749.  
  14750. The section on the method begins with a justification for and presentation of
  14751. the object-oriented design notation that Booch uses in the rest of the book.
  14752. This includes notation for class and object diagrams (for documenting the
  14753. logical design of a system), and module and process diagrams (for documenting
  14754. the physical design). Booch also provides state transition and timing diagrams
  14755. to show the state space of a class instance and dynamic object interactions,
  14756. respectively. On my first read of the book, I skipped these sections, which
  14757. was a mistake, since they are used extensively in the applications
  14758. discussions. The notation is, however, reproduced on the book's inside covers
  14759. for easy reference.
  14760. The book continues its presentation of the method by describing the design
  14761. process ("round-trip gestalt design"), and discussing the pragmatics of design
  14762. given the incremental, iterative nature of object-oriented design.
  14763.  
  14764.  
  14765. Applications
  14766.  
  14767.  
  14768. The first two sections are a preamble of sorts to the third section covering
  14769. applications. While I found this section to be a real treasure -- Booch takes
  14770. five applications from the analysis stage through design, evolution, and
  14771. modification -- readers who specialize in only one object-oriented language
  14772. will likely leave 80 percent of this section unread because the author
  14773. implements each of the five applications in a different language. A home
  14774. heating system is done in Smalltalk. A geometric optics construction kit is
  14775. implemented in Object Pascal. C++ is used to construct a problem reporting
  14776. system. Common LISP is applied to the problem of cryptanalysis. Finally, Ada
  14777. is used to build a traffic management system.
  14778. In today's computing world, developers have increasingly become cliquish,
  14779. sticking to their own specialty and shunning others. However, as Robert
  14780. Heinlein once wrote, "Specialization is for insects," and the reader who
  14781. ignores the "other" parts of this section on applications is shortchanging
  14782. him- or herself. You don't have to be a guru at any of these languages to get
  14783. a great deal out of this section.
  14784.  
  14785.  
  14786. In General
  14787.  
  14788.  
  14789. Many computer science books -- particularly those undertaking to explain
  14790. design -- have the unfortunate characteristic of taking themselves too
  14791. seriously. This is certainly not the case in Booch's book. Illustrative
  14792. cartoons scattered throughout the book will bring a smile to your lips as they
  14793. pound home their message. Booch also has a refreshing way of expressing ideas.
  14794. Nobody, he notes, would expect to successfully (or cheaply) add a sub-basement
  14795. to a 100-story building, yet equivalent changes are demanded in software all
  14796. the time. I'll marshall that argument the next time I'm confronted with such a
  14797. change.
  14798. The book makes good use of typography, with distinguishable fonts for text and
  14799. code, and recognizable section headers. There is a band of whitespace along
  14800. the sides of the page, and the pages stand up well to scribbling and accenting
  14801. with yellow marker. Figures, cartoons, and sidebars break the monotony of
  14802. text-filled pages. Sidebars are noted in the Table of Contents.
  14803. The end material in the book is helpful, and, in general, well organized. A
  14804. capsule summary of object concepts and the characterstics of object-oriented
  14805. languages is presented in the appendix. These are, of necessity, pretty sparse
  14806. summaries, and Booch provides a list of references for each language. The
  14807. appendix is followed by the book's footnotes (I would have preferred them at
  14808. the end of each chapter), and a good glossary of terms.
  14809. The bibliography is arranged in a classified manner. References to
  14810. object-oriented design are grouped separately from those on object-oriented
  14811. programming, software engineering, etc. This gives the bibliography a limited
  14812. value. If you know your subject and want a list of books, you're fine. If you
  14813. know only your author, you may have to look in several different
  14814. classifications before finding what you seek.
  14815. The field of object-oriented design is young, and there is always the risk
  14816. that the first few books published on the subject will be rushed and
  14817. incomplete. Not so here. Grady Booch's book thoroughly covers its material in
  14818. an engaging manner, and is destined to be a classic in the field. It deserves
  14819. a place on the shelf of every serious software developer.
  14820. Object-Oriented Design with Applications
  14821. Grady Booch
  14822. Benjamin/Cummings
  14823. Publishing Co.
  14824. ISBN 0-8053-0091-0
  14825. 580 pp.
  14826. (1991)
  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.  
  14864.  
  14865.  
  14866.  
  14867.  
  14868.  
  14869.  
  14870.  
  14871.  
  14872.  
  14873.  
  14874.  
  14875.  
  14876.  
  14877.  
  14878.  
  14879.  
  14880.  
  14881.  
  14882.  
  14883.  
  14884.  
  14885.  
  14886.  
  14887.  
  14888.  
  14889.  
  14890.  
  14891.  
  14892.  
  14893.  
  14894.  
  14895.  
  14896.  
  14897.  
  14898.  
  14899. Algorithmics: The Spirit of Computing
  14900.  
  14901.  
  14902. Tom Rombouts
  14903.  
  14904.  
  14905. Tom Rombouts works in product development for Ashton-Tate in Torrance,
  14906. California. He is also the leader of the dBASE SIG of the Los Angeles Computer
  14907. Society. USENET: tomr@ashtate.A-T.com.
  14908.  
  14909.  
  14910. Algorithmics is an introduction and overview of the history, methods, theory,
  14911. and future of algorithm study and analysis. Excellently written,
  14912. well-researched, and with numerous illustrations, the book contains
  14913. fascinating material in every chapter. However, David Harel's approach may be
  14914. too theoretical for most day-to-day programming work, and likely too academic
  14915. and mathematical at times for many managers or end-users.
  14916. To some, the fundamental issue of computer science might be creating faster,
  14917. smaller, and cheaper hardware. To others, it might be designing more efficient
  14918. and reliable methods of software development. But to noted theoretician David
  14919. Harel, it is Algorithmics (the study of algorithms -- planned processes used
  14920. to solve specific problems) that most captures the true "Spirit of Computing."
  14921. Based on a series of radio lectures by the author, the book starts by
  14922. comparing a recipe from a cookbook to a computer program, and eventually works
  14923. its way up to presenting issues on the leading edge of algorithm theory
  14924. involving such things as algebraic quadratic equations, prime number theory,
  14925. and reduction equivalency. Believe it or not, in some ways factoring a
  14926. 150-digit number is not so different from preparing a chocolate mousse!
  14927. The book consists of four somewhat self-contained parts. Part one,
  14928. Preliminaries, includes a quick historical review, a summary of the basic
  14929. concepts of algorithms and data, and a brief survey of common computer
  14930. languages and the types of problems they are best suited for.
  14931. The second part, Methods and Analysis, covers algorithmic methods, trying to
  14932. prove the correctness of algorithms, and efficiency considerations regarding
  14933. algorithms.
  14934. The third part, Limitations and Robustness, covers topics such as the two
  14935. classes of problems that computers can solve, problems that can not be solved
  14936. by computers, and finite state machine theory. This section contains the bulk
  14937. of the book's math and theory, which, depending on your point of view, is
  14938. either the most or least important material.
  14939. Part four, Relaxing the Rules, discusses issues such as new problems that must
  14940. be addressed when introducing parallel processing, algorithms that use
  14941. randomization techniques to generate "probably correct" solutions, and
  14942. artificial intelligence and possible limits of future computing applications.
  14943. Finally, there is a 48-page section of bibliographic notes and a comprehensive
  14944. 20-page index.
  14945. Although the author intends the book to be read sequentially, two chapters in
  14946. particular can be read by themselves. Programming Languages gives a quick yet
  14947. insightful overview of seven major languages (including Pascal and not C?),
  14948. while Algorithmics and Intelligence gives a clear outline of issues relating
  14949. to artificial intelligence in areas such as voice and pattern recognition and
  14950. game theory.
  14951. I find one of the book's real strengths to be its light, humorous style. Each
  14952. chapter begins and ends with old testament bible quotations that apply (out of
  14953. context, of course) to the topics covered in that chapter. Or, in the
  14954. bibliography for the section on recursion, the book references itself!
  14955. The book teaches that there are four main categories of algorithmic problems,
  14956. only one of which can be realistically solved by computers. Given a certain
  14957. minimal set of abilities, and unlimited space and time, all programming
  14958. languages and computers are equivalent in the types of problems they can and
  14959. cannot solve.
  14960. Overall, I think this is an excellent book. However, it probably will not help
  14961. you debug your C program, nor help you decide when to upgrade to the latest
  14962. version of your favorite compiler.
  14963. On the other hand, it is my (perhaps minority) opinion that knowing more than
  14964. the absolute minimum needed to get your paycheck can make many day-to-day
  14965. tasks seem much simpler in comparison. After all, if you can grasp the
  14966. concepts of the limits of algorithmics, debugging a text parser should be a
  14967. breeze!
  14968. Reading the book might even enhance your humor skills at your next party. The
  14969. next time someone says, "Did you hear the one about the traveling salesman?"
  14970. you will be able to immediately reply: "Yes! The traveling salesman's search
  14971. for a minimal, perhaps Hamiltonian, visitation path is perhaps the best-known
  14972. example of a Nondeterministic Polynomial-time Complete algorithmic problem!"
  14973. Algorithmics: The Spirit of Computing
  14974. David Harel
  14975. Addision-Wesley
  14976. 1987
  14977. $25.00
  14978. 425 pages
  14979. ISBN 0-201-19240-3.
  14980.  
  14981.  
  14982.  
  14983.  
  14984.  
  14985.  
  14986.  
  14987.  
  14988.  
  14989.  
  14990.  
  14991.  
  14992.  
  14993.  
  14994.  
  14995.  
  14996.  
  14997.  
  14998.  
  14999.  
  15000.  
  15001.  
  15002.  
  15003.  
  15004.  
  15005.  
  15006.  
  15007.  
  15008.  
  15009.  
  15010.  
  15011. Editor's Forum
  15012. It is a truism that we live in an era of electronic communications.
  15013. Nonetheless, I find myself continually astonished at how pervasive the
  15014. electronic grapevine has become.
  15015. Perhaps it is because I am basically conservative. Electronic mail has long
  15016. come free with UNIX. Even so, I resisted the use of e-mail within Whitesmiths
  15017. all the years we were tied into the UNIX network. Mail within the company was
  15018. useful, I felt. But I saw too many hours wasted by people generating, and
  15019. wading through, gossip on the various forums.
  15020. When I went on my own, I quickly noticed the vacuum that comes with being
  15021. disconnected from the technical community. I gave in, bought the UUMAIL
  15022. package from Vortex Technology for my PC, and signed up with UUNET to get on
  15023. the network. My usage has grown exponentially. I once stayed comfortably
  15024. within the 60 minutes per month that came with my rock bottom base rate. Now I
  15025. blow that quota by shipping a single book manuscript, or a batch of articles
  15026. for CUJ. It is a rare week that I don't get mail from four continents.
  15027. I tell you this for several reasons. I am beginning to get letters to the
  15028. editor via e-mail. That is a milestone of sorts, at least for me, and a true
  15029. sign of the times. It is only fair that I give in a further step and begin
  15030. advertising my e-mail address on a wider basis. It will stay unchanged even
  15031. for the year I am spending in Australia, thanks to automatic mail forwarding.
  15032. Were it not for e-mail and FAX, of course, I couldn't possibly edit a magazine
  15033. this year that is published in faraway Kansas.
  15034. I welcome this growing electronic interconnectivity, albeit reluctantly at
  15035. times. But I still can't bring myself to read comp.std.c, the electronic forum
  15036. on standardizing C.
  15037. P.J. Plauger
  15038. pjp@plauger.uunet.com
  15039. P.S. I invite our readers to stop by the R&D booths at SD '91 in Santa Clara
  15040. (February 12-15). Both CUJ and TECH Spec will be exhibiting, so stop by and
  15041. say hello to our staff. We'd like to hear what you have to say.
  15042.  
  15043.  
  15044.  
  15045.  
  15046.  
  15047.  
  15048.  
  15049.  
  15050.  
  15051.  
  15052.  
  15053.  
  15054.  
  15055.  
  15056.  
  15057.  
  15058.  
  15059.  
  15060.  
  15061.  
  15062.  
  15063.  
  15064.  
  15065.  
  15066.  
  15067.  
  15068.  
  15069.  
  15070.  
  15071.  
  15072.  
  15073.  
  15074.  
  15075.  
  15076.  
  15077.  
  15078.  
  15079.  
  15080.  
  15081.  
  15082.  
  15083.  
  15084.  
  15085.  
  15086.  
  15087.  
  15088.  
  15089.  
  15090.  
  15091.  
  15092.  
  15093.  
  15094. New Products
  15095.  
  15096.  
  15097. Industry-Related News & Announcements
  15098.  
  15099.  
  15100.  
  15101.  
  15102. C++ Solution For Embedded Systems
  15103.  
  15104.  
  15105. Microtec Research has introduced C++ development tools for embedded systems
  15106. engineering. The tools include an optimizing C++/ANSI C cross compiler, a
  15107. C++-compatible source-level debugger, and C++ symbol name inspection tools.
  15108. The new product, CCC68K, implements the 2.1 definition for the C++ language
  15109. with numerous extensions to support embedded applications development.
  15110. Complementing the compiler is the XRAY68K debugger for debugging optimized C++
  15111. code. Microtec Research also supplies a variety of inspection and conversion
  15112. tools. First targeted for the Motorola 68000 family of microprocessors, CCC68K
  15113. supports a superset of the v2.1 C++ language definition.
  15114. The CCC68K package also includes the ASM68K relocatable macro assembler and
  15115. single and multiple inheritance runtime class libraries for I/O streams and
  15116. complex arithmetic. The debugger provides a window-oriented user interface
  15117. that separates user code and debug information into appropriate groupings.
  15118. Prices for the complete toolkit begin at $6,600. For more information, contact
  15119. Microtec Research, 2350 Mission College Blvd., Santa Clara, CA 95054, (408)
  15120. 980-1300; FAX (408) 982-8266.
  15121.  
  15122.  
  15123. Mark Williams Ships Coherent 3.1
  15124.  
  15125.  
  15126. Mark Williams Company is now shipping Coherent v3.1, an upgrade of its
  15127. UNIX-compatible operating system. It offers a C compiler, lex, yacc, make,
  15128. UUCP, text processing, program development, administrative and maintenance
  15129. commands.
  15130. Coherent v3.1 includes information on accessing the MWC UUCP bulletin board
  15131. system, a configurable AT hard disk driver for unsupported disks, an Adaptec
  15132. AHA-154x series SCSI device driver, COM3 and COM4 serial line support, and a
  15133. RAM-disk device driver.
  15134. Coherent v3.1 also offers a generic multi-port serial card device driver, a
  15135. miscellaneous function library with source code, 16-bit version of compress,
  15136. uncompress and zcat, enhanced mail, man, scat and msgs utilities, new and
  15137. updated on-line manual pages, and numerous bug fixes.
  15138. Coherent v3.1 sells for $99.95. For more information, contact Mark Williams
  15139. Company, 60 Revere Drive, Northbrook, IL 60062, (708) 291-6700; FAX (708)
  15140. 291-6750.
  15141.  
  15142.  
  15143. Intek C++ For Windows
  15144.  
  15145.  
  15146. Intek Integration Technologies is now shipping v2.0b of its C++ product. Intek
  15147. C++ is a translator that generates C code as output. The DOS version runs in
  15148. protected mode at compile time, allowing access to all memory in the machine.
  15149. Intek C++ supports small data models (small and medium memory) as recommended
  15150. by Microsoft. Intek C++ v2.0b supports the extended keywords near, far, huge,
  15151. cdedl, pascal, fortran, as well as the new Microsoft v6.0 keywords, _based,
  15152. _self, _segment, and _segname.
  15153. Intek C++ supports a variety of C compilers, including Microsoft C v5.1 and
  15154. v6.0; Microsoft C+ with Windows, v5.1 and v6.0; Turbo C v2.0; Watcom C v8.0;
  15155. Watcom C 386 v8.0; and Metaware High C 386. Intek C++ can work with other
  15156. standard and robust C compilers that support 31 character names. Some of the
  15157. users cross-compile from Intek C++ to C compilers for chips like the TI 34010.
  15158. Intek C++ sells for $495 for single copies; site licenses are available. It
  15159. runs under MS-DOS on the 80386 with 2Mb of RAM, and also under UNIX System
  15160. V/386. The UNIX version can cross-compile to any DOS C compiler.
  15161. For more information, contact Intek Integration Technologies, 1400 112th Ave.
  15162. SE, Suite
  15163.  
  15164.  
  15165. Saber Software Offers C++ Environment
  15166.  
  15167.  
  15168. Saber Software has introduced Saber-C++, a workstation-based UNIX C++ program.
  15169. It is a dual C++ and C language development environment.
  15170. Software engineers can use C++ immediately because Saber-C++ offers an
  15171. interactive workspace and a full C++ interpreter for prototyping,
  15172. experimentation and runtime error checking. Users are able to view and
  15173. understand program elements ranging from simple data structures to complex C++
  15174. class hierarchies.
  15175. Saber-C++ offerings include automatic static and runtime error detection,
  15176. source-level debugger, graphical browsers, interactive workspace, incremental
  15177. linker, integration with UNIX tools, and extensive documentation.
  15178. Saber-C++ sells for $3,995 and includes technical support and maintenance for
  15179. one year. For more information, contact Saber Software, 185 Alewife Brook
  15180. Parkway, Cambridge, MA 02138, (617) 876-7636; FAX (617) 547-9011.
  15181.  
  15182.  
  15183. C++ for UNIX 80386/486
  15184.  
  15185.  
  15186. Computer Innovations is now offering C++ v2.1 for UNIX System-V based
  15187. 80386/486 systems, including those from Interactive, SCO, and ESIX.
  15188. C++ v2.1 contains both Super-C and object-oriented features of C++. Super-C
  15189. extensions include default function arguments, in-line methods and functions,
  15190. type-safe linkage, and call by reference or value. Object-oriented features of
  15191. C++ include class/method organization, overloaded operators, inheritance,
  15192. polymorphism, constuctors and destructors, and virtual functions.
  15193. The package includes standard C++ class libraries for streams and complex
  15194. arithmetic. It sells for $495. For more information, contact Computer
  15195. Innovations, 980 Shrewsbury Ave., Tinton Falls, NJ 07724, (201) 542-5920.
  15196.  
  15197.  
  15198. C Code Builder Kit
  15199.  
  15200.  
  15201. Now available from Intel Corporation is the 386/486 C Code Builder Kit. This
  15202. software development toolkit for 32-bit applications from a single vendor,
  15203. provides developers with the single source tools necessary for 32-bit software
  15204. development on Intel 386 and i486 microprocessor-based personal computers.
  15205.  
  15206. The toolkit contains the Intel 32-bit compiler, 32-bit protected mode
  15207. source-level debugger, linker, libraries, utilities, make facility and DOS
  15208. extender.
  15209. The toolkit sells for $695. For more information, contact Intel Corporation,
  15210. P.O. Box 58130, 3065 Bowers Ave., Santa Clara, CA 95052-8130, (800) 548-4725.
  15211.  
  15212.  
  15213. C++ Compiler for CPU Board Family
  15214.  
  15215.  
  15216. Heurikon Corporation has released the Green Hills C++ compiler for its
  15217. 68030-based Multibus 1, Multibus II, and VMEbus CPU boards. The compiler will
  15218. initially run under the UNIX v3.0 operating system and eventually will be
  15219. ported to real-time operating systems.
  15220. The Green Hills compiler produces optimized 68030 object code directly from
  15221. the C++ source code. The compiler (object code) costs $2,500 and is available
  15222. for the HK68/M130, HK68/V30XE, and HK68/M230 CPU boards running UNIX v3.0. For
  15223. more information, contact Heurikon, 8000 Excelsior Dr., Madison, WI 53717,
  15224. (608) 831-0900; FAX (608) 831-4249.
  15225.  
  15226.  
  15227. IDB Object Database
  15228.  
  15229.  
  15230. The IDB object database from Persistent Data Systems is programmable in
  15231. standard C and runs both on UNIX workstations and on the PC compatibles under
  15232. DOS and Microsoft Windows 3.0.
  15233. IDB is based on IDL, the interface description language. IDL is a technology
  15234. developed to manage the complex data structures of compilers and other
  15235. software development tools.
  15236. IDB applications may be configured with or without an optional display manager
  15237. and browser. Using the browser's built-in display, edit, and search
  15238. operations, a developer can prototype a new application by describing its
  15239. data. Applications that use IDB for display and data management are portable
  15240. across the range of supported platforms.
  15241. The price of a single license ranges from $2,500 to $6,000. For more
  15242. information, contact Persistent Data Systems, 75 West Chapel Ridge Road,
  15243. Pittsburgh, PA 15238, (412) 963-1843.
  15244.  
  15245.  
  15246. ToolBook for OS/2
  15247.  
  15248.  
  15249. Now available from Asymetrix Corporation is the ToolBook 1.0 for the OS/2
  15250. operating system. ToolBook is a software construction set that allows users to
  15251. build custom graphical applications without traditional programming languages.
  15252. ToolBook is a set of application development tools, including a color drawing
  15253. package and an object-oriented programming language called OpenScript. The
  15254. tools feature hyper-navigation capabilities, animation, text formatting, and
  15255. flat-file database creation.
  15256. The retail version of ToolBook for OS/2 comes with several applications: one
  15257. that reads and writes dBase files, a hypermedia tutorial, a business
  15258. calculator, an animation primer, a quick tour of ToolBook features, and a
  15259. collection of scripts, clip art, and page designs. The OS/2 ToolBook sells for
  15260. $395.
  15261. Also available from Asymetrix is the ToolBook Developer Program, which
  15262. provides both development and marketing assistance, and a new ToolBook
  15263. Developer Partnership, an option that gives developers unlimited access to a
  15264. designated Asymetrix support engineer for more in-depth technical support. The
  15265. ToolBook Developer Program is free with the purchase of the $450 Asymetrix
  15266. Author's Resource Kit, a package of tools that helps developers build and
  15267. distribute applications.
  15268. For more information, call (800) 624-8999.
  15269.  
  15270.  
  15271. XVision 3.1 Expands Network Support
  15272.  
  15273.  
  15274. Graphic Software Systems has released XVision v3.1. Its new features include
  15275. increased X client capabilities, a new Program Starter that creates icons for
  15276. individual X clients, and additional network support.
  15277. XVision is a Microsoft Windows application that turns PCs into X Servers.
  15278. Users can cut and paste between the two environments without a DOS hot-key
  15279. mechanism. XVision is compatible with MS-Windows 3.0.
  15280. XVision v3.1 allows the user to run up to 16 X clients simultaneously. The
  15281. Program Starter now accepts command-line parameters that allow users to create
  15282. icons for specific X clients. By selecting these icons, individual X clients
  15283. are automatically started.
  15284. New network support includes Sun PC-NFS, Ungermann-Bass Net/One TCP/IP, and
  15285. Wollongong WIN/TCP for DOS. XVision can be networked to a variety of X hosts
  15286. simultaneously, including UNIX and VMS.
  15287. XVision v3.1 is priced at $449. Current XVision users can purchase the upgrade
  15288. for $150. For more information, contact Graphic Software Systems, 9590 SW
  15289. Gemini Dr., Beaverton, OR 97005, (503) 641-2200; FAX (503) 643-8642.
  15290.  
  15291.  
  15292. Spell Checking Engine For Clipper and C Applications
  15293.  
  15294.  
  15295. Geller Software's Spell Checking Engine is a library for Clipper and C
  15296. developers that adds spell checking to applications.
  15297. This function library consists of object modules that link into Clipper or C
  15298. applications, an English language dictionary with more than 100,000 words
  15299. stored in a compressed format, and a maintenance program for adding or
  15300. removing words from the main dictionary or for building specialized
  15301. dictionaries.
  15302. The Spell Checking Engine also works with the Paradox Engine, Force, CodeBase
  15303. 4, and other programs compatible with Microsoft C. Future releases will also
  15304. be compatible with Borland's Turbo C and the Watcom C compiler.
  15305. The Spell Checking Engine allows developers to add spell checking and
  15306. correction of any user input within a custom application. The system can also
  15307. spell check a wide variety of file formats, including xbase data and memo
  15308. files, spreadsheets, and graphics files.
  15309. For more information, call Glenn Hart at (914) 357-2055.
  15310.  
  15311.  
  15312. 32-Bit Protected-Mode Extended Graphics Library
  15313.  
  15314.  
  15315. A 32-bit 386 protected-mode extended graphics library, libhpgl.lib v5.0, is
  15316. now available from Gary R. Olhoeft. This library supports graphics direct to
  15317. hardware for PC-compatible standard modes EGA/VGA/MCGA/8514, VESA/SVGA,
  15318. Hercules Graphics Station Card GB1024, Truevision ATVista-4M and Wysiwyg
  15319. hardcopy to HP-GL and PostScript devices.
  15320. The library supports mixed vector plotting and raster imaging, graphics
  15321. viewports, user unit scaling, rotatable and scalable labels, and more up to
  15322. 1,024 x 768 with 8-, 16-, or 32-bit graphics cards. The library comes with
  15323. full source code in Micro Way NDP C-386 and Phar Lap 386 ASM, extensive
  15324. example code and manual, 30-day money back guarantee, free upgrades for one
  15325. year, and has no royalties for distribution of compiled composite code.
  15326. Libhpgl.lib sells for $300. For more information, contact Gary Olhoeft, P.O.
  15327. Box 10870 Edgemont, Golden, CO 80401-0620, (303) 279-6345.
  15328.  
  15329.  
  15330. c-tree Plus Improves Resource Management
  15331.  
  15332.  
  15333.  
  15334. FairCom has released c-tree Plus, a new file management and data server
  15335. product that enhances and extends the functionality of the c-tree file
  15336. handler.
  15337. Developers can use c-tree Plus to produce a variety of application types:
  15338. single or multi-user; single or multiple machine environments; applications
  15339. with small or large amounts of data; optimized response time for a single
  15340. query or a large request to select, sort, and return a set of data.
  15341. c-tree Plus features include expanded support for variable length and BCD data
  15342. types and keys; file level alternate collating sequence support; dynamic space
  15343. reclamation; high-speed hashed data and index caching; and improved native I/O
  15344. system utilization.
  15345. c-tree Plus sells for $595. Current c-tree users can upgrade to c-tree Plus
  15346. for $200. For more information, contact FairCom, 4006 West Broadway, Columbia,
  15347. MO 65203, (800) 234-8180; FAX (314) 445-9698.
  15348.  
  15349.  
  15350. Microware Offers Multimedia Interface
  15351.  
  15352.  
  15353. Microware Systems is now offering Rave (real-time audio/video environment)
  15354. real-time multimedia development environment and user interface for PC
  15355. compatibles.
  15356. Using OS-9000/Rave, designers can configure realistic user interfaces and
  15357. control panels by combining high-quality audio, computer-generated graphic
  15358. images, captured live video and customizable menus in the same user interface.
  15359. Rave combines audio, video, and computer-generated graphics, and it can
  15360. develop complex interface without writing code. Rave facilitates design of
  15361. realistic real-time man/machine interfaces and runs on top of OS-9000
  15362. real-time operating system.
  15363. OS-9000/Rave sells for $750. For more information, contact Microware Systems,
  15364. 1900 NW 114th St., Des Moines, IA 50325-7077, (515) 224-1929, FAX (515)
  15365. 224-1352.
  15366.  
  15367.  
  15368. EMS Revises C Utilities Library
  15369.  
  15370.  
  15371. EMS Professional Shareware Libraries has been shipping a revised version of
  15372. the Library of PD/Shareware C Utilities, which include more than 400 public
  15373. domain and shareware C utility files.
  15374. Each file in the library is described and indexed in a database that
  15375. accompanies the library. The library contains a variety of types of files,
  15376. including AI, array, benchmark, bit manipulation, C++, communication,
  15377. compression, database, date/time, debugger, disk management, documentor, DOS,
  15378. editor, function library, games, graphics, mathematics, memory management,
  15379. mouse, printer, network, security, tutorial, window, and menu.
  15380. The library sells for $79.50. For more information, contact EMS Professional
  15381. Shareware Libraries, 4505 Buckhurst Ct., Olney, MD 20832, (301) 924-3594; FAX
  15382. (301) 963-2708.
  15383.  
  15384.  
  15385. DOCZ Assists Software Development
  15386.  
  15387.  
  15388. DOCZ from Software Toolz allows the sharing and reuse of software modules by
  15389. automating the documentation of subroutine libraries and programs. The
  15390. documentation can be contained in the same file as the source code.
  15391. DOCZ also builds on-line help libraries. Program editors can be interfaced to
  15392. DOCZ to extract function call prototypes from the help libraries while
  15393. editing.
  15394. DOCZ helps developers transport help libraries and documentation from one
  15395. platform to another. Its generated documentation contains all the information
  15396. needed to create an object-oriented development environment. DOCZ supports all
  15397. major languages.
  15398. DOCZ sells for $195. For more information, contact Software Toolz, 8030 Pooles
  15399. Mill Drive, Ball Ground, GA 30107-9610, (404) 889-8264.
  15400.  
  15401.  
  15402. JMI Expands C Executive Capabilities
  15403.  
  15404.  
  15405. JMI Software Consultants has introduced new versions of C Executive, a
  15406. real-time, multitasking, ROMable kernel. The new versions of C Executive
  15407. support Advanced Micro Device's Am29050, Intel's i960, and the Inmos (SGS
  15408. Thomson) transputer.
  15409. All versions of C Executive offer the optional DOS compatible file system,
  15410. CE-DOSFILE. With this file system, any CISC or RISC microprocessor can be used
  15411. to maintain MS-DOS file systems on-line. CE-DOS-FILE is compatible with MS-DOS
  15412. file system media.
  15413. For more information, contact JMI Software Consultants, 904 Sheble Lane, P.O.
  15414. Box 481, Spring House, PA 19477, (215) 628-0846.
  15415.  
  15416.  
  15417. Magee Adds Data Entry Features to Screen Manager
  15418.  
  15419.  
  15420. Magee Enterprises has released the Screen Manager Professional v2.0, an
  15421. advanced interface design toolbox for C programmers. SMP v2.0 data entry
  15422. features include field-oriented data entry, user attachable validation
  15423. functions, integration with context-sensitive help, and an interactive field
  15424. building utility.
  15425. Other features of SMP v2.0 include unlimited number of windows, event-driven
  15426. mouse support, context-sensitive help, linkable libraries, and a tutorial with
  15427. more than 100 examples of code. SMP v2.0 also offers a comprehensive menu
  15428. system, recording and playback of keystrokes, and shadowing and overlapping of
  15429. windows.
  15430. For more information, contact Magee Enterprises, 2909 Langford Rd. Suite A600,
  15431. Norcross, GA 30071-1506, (404) 446-6611; FAX (404) 368-0719.
  15432.  
  15433.  
  15434. Air Data Offers Products For 80x86 Embedded Systems Development
  15435.  
  15436.  
  15437. Air Data is now distributing two new products for embedded software
  15438. development.
  15439. Interactors is a concurrent object-oriented environment for C++. This
  15440. real-time executive is based on multi-party interactions. The real-time
  15441. facilities are added to off-the-shelf C compilers in the form of a source
  15442. library. Interactors is compatible with both Zortech C++ v2.0 compiler and the
  15443. Borland Turbo C++ compiler for MS-DOS.
  15444. Zlocate is a C++ locator for 80x86 embedded software development. The locator
  15445. converts MS-DOS/Zortech's loadable code into absolute code format, suitable
  15446. for programming ROMs.
  15447. These two products were developed by Objective:Systems, P.O. Box 265, Ville
  15448. Mont-Royal, Quebec, Canada H3P 3C5. For more information, contact the
  15449. distributor, Air Data, 4440 Old Orchard St., Montreal, Quebec H4A 3B4, Canada
  15450. (514) 484-0390.
  15451.  
  15452.  
  15453.  
  15454. Software through Pictures Available For Sun Workstations
  15455.  
  15456.  
  15457. Interactive Development Environments has made available Software through
  15458. Pictures Release 4.2 under the Open-Windows v2.0 application environment from
  15459. Sun Microsystems. The software is also available on the SPARCstation 2
  15460. workstations.
  15461. Software through Pictures under OpenWindows allows users to set up an
  15462. X11-based software development center, using the full range of Sun platforms
  15463. from the SPARCserver 490 to a SPARCstation SLC, combined with any X11 display
  15464. server from other manufacturers.
  15465. Single license prices for Software through Pictures range form $5,000 to
  15466. $21,000, depending on the configuration. For more information, contact
  15467. Interactive Development Environments, 595 Market St., 10th Floor, San
  15468. Francisco, CA 94105, (415) 543-0900; FAX (415) 543-3716.
  15469.  
  15470.  
  15471. XDB Offers SQL Precompiler For Windows 3.0
  15472.  
  15473.  
  15474. XDB has introduced its SAA-compatible SQL precompiler for C. XDB will bundle
  15475. the C precompiler with XDB-C and XDB-Windows software developer toolkits. With
  15476. these toolkits, C programmers can build sophisticated applications for XDB's
  15477. SQL database servers.
  15478. The C precompiler can be configured to be DB2, SAA, ANSI, Oracle, or
  15479. XDB-compatible and is capable of running under DOS, OS/2, and Windows 3.0. The
  15480. C precompiler provides support for both embedded and dynamic SQL. XDB also
  15481. allows applications to mix XDB's library of functions found in XDB-C plus
  15482. XDB's SQL engine for Windows 3.0.
  15483. XDB-Windows is priced at $750. XDB-C is priced at $595. For more information,
  15484. contact XDB Systems at (301) 779-6030.
  15485.  
  15486.  
  15487. Database Software Consultants Updates Network Library
  15488.  
  15489.  
  15490. Database Software Consultants has released dCL-Net Library v4.0 and changed
  15491. the name to dMill network kit for Clipper. In addition to this library, the
  15492. dMill network kit for dBase III Plus and the dMill network kit for FoxPro/LAN
  15493. will also be available.
  15494. These network kits offer an upgrade path, including an education component, a
  15495. techniques component, and a tools component. Product manuals for the libraries
  15496. are comprehensive and heavily indexed.
  15497. For more information, contact Database Software Consultants, P.O. Box 8380,
  15498. Austin, TX 78713-8380, (512) 477-3423.
  15499.  
  15500.  
  15501. ZIM v3.11 Complies With 88open Standards
  15502.  
  15503.  
  15504. ZIM, Sterling Software's Entity-Relationship-based 4GL/RDBMS has been
  15505. officially certified as compliant with 88open standards.
  15506. ZIM is an application development environment delivering complex, multi-user
  15507. applications. ZIM is available on a range of hardware platforms and operating
  15508. systems. The ZIM family of products includes Runtime, Query-Runtime, and
  15509. SQL-compatible versions.
  15510. For more information, contact Sterling Software, 36 Antares Dr., Suite 500,
  15511. Ottawa, Ontario K2E 7W5; (613) 727-1397; FAX (613) 727-6940.
  15512.  
  15513.  
  15514. Microsoft Offers Upgrade to Lattice C Users
  15515.  
  15516.  
  15517. Microsoft is offering Lattice C compiler users a $125 upgrade to the Microsoft
  15518. C Professional Development System v6.0. No new features will be added to
  15519. Lattice C v6.05.
  15520. Version 6.0 upgrades are available directly from Microsoft. Proof of Lattice C
  15521. ownership is required. For more information, contact Microsoft, One Microsoft
  15522. Way, Redmond, WA 98052-6399, (206) 882-8080; FAX (206) 883-8101, or contact
  15523. Microsoft customer service at (800) 541-1261.
  15524.  
  15525.  
  15526. Motorola Supports High Security of UNIX Systems
  15527.  
  15528.  
  15529. The Motorola Computer Group will support the high security and multiprocessing
  15530. features of the UNIX System V r4 operating system to be released by AT&T.
  15531. Motorola will incorporate these capabilities into its System V/68 and System
  15532. V/88 UNIX operating systems for computers based on Motorola's own MC68000 and
  15533. M88000 microprocessor families.
  15534. For more information, contact Motorola at (602) 438-3576.
  15535.  
  15536.  
  15537. ParcPlace Systems Ships Objectworks
  15538.  
  15539.  
  15540. ParcPlace Systems is now shipping the newest releases of its object-oriented
  15541. programming systems, Objectworks\C++ and Objectworks\Smalltalk.
  15542. The company also established ParcPlace Partners, a value-added business
  15543. program for third-party Smalltalk application developers and systems
  15544. integrators. The ParcPlace Partners program offers a variety of marketing and
  15545. technical services that support the development, porting, and selling of
  15546. Smalltalk applications.
  15547. For more information, contact ParcPlace Systems, 1550 Plymouth St., Mountain
  15548. View, CA 94043, (415) 691--6700; FAX (415) 691-6715.
  15549.  
  15550.  
  15551. Informix Supports DEC Multiprocessor System
  15552.  
  15553.  
  15554. Informix Software is planning to make its leading line of information
  15555. management software products available on Digital Equipment Corporation's
  15556. recently introduced application DEC 433MP multiprocessor platform. The new
  15557. platform will run the SCO UNIX System V version of the UNIX operating system.
  15558. For more information, contact Informix Software, 4100 Bohannon Dr., Menlo
  15559. Park, CA 94025, (415) 926-6300.
  15560.  
  15561.  
  15562.  
  15563.  
  15564.  
  15565.  
  15566.  
  15567.  
  15568.  
  15569.  
  15570.  
  15571.  
  15572.  
  15573.  
  15574.  
  15575.  
  15576.  
  15577.  
  15578.  
  15579.  
  15580.  
  15581.  
  15582.  
  15583.  
  15584.  
  15585.  
  15586.  
  15587.  
  15588.  
  15589.  
  15590.  
  15591.  
  15592.  
  15593.  
  15594.  
  15595.  
  15596.  
  15597.  
  15598.  
  15599.  
  15600.  
  15601.  
  15602.  
  15603.  
  15604.  
  15605.  
  15606.  
  15607.  
  15608.  
  15609.  
  15610.  
  15611.  
  15612.  
  15613.  
  15614.  
  15615.  
  15616.  
  15617.  
  15618.  
  15619.  
  15620.  
  15621.  
  15622.  
  15623.  
  15624. We Have Mail
  15625. Dear Mr. Plauger,
  15626. Since purchasing Turbo C++ in the middle of this year, I have had a love-hate
  15627. relationship with it. The qsort function is one of those. I first found this
  15628. when playing around with Leor Zolman's Mini Data Base article.
  15629. My solution in this case was different than that of Mr. Irani. (See mdbedit. c
  15630. in Listing 1.) I passed the parameters exactly as Turbo C++ required, then
  15631. "changed" them with casts inside the function. It would appear to me that the
  15632. problem here has to do with the way Borland handles void. Turbo C seems to
  15633. avoid this with a very loose prototype in <stdlib.h>.
  15634. Other problems with Turbo C++ have been its handling of arrays and a somewhat
  15635. buggy IDE.
  15636. The IDE on occasion fails to restore the mouse cursor properly, leaving a
  15637. black square on the screen where it was last clicked. (If you use an old, or
  15638. nonMicrosoft driver, the mouse cursor may disappear completely, while the
  15639. mouse remains active.) When debugging, the IDE sometimes becomes lost,
  15640. requiring exiting, and restarting. Part of it seem to think the program is
  15641. still executing, while part of it rightly knows it has successfully finished.
  15642. I do not like the editor defaults on the IDE. Thankfully most of them are
  15643. changeable. However, some must be changed from TCINST while others must be
  15644. changed from within the IDE.
  15645. Overall I like the Turbo C++ product, but don't think it is as perfect as much
  15646. of the press out there has implied.
  15647. I am not a professional programmer so I do not claim to be any expert. However
  15648. I hope some of this may give some insight to others.
  15649. I am a self-taught electronics technician of 20 plus years, now working on
  15650. becoming a self-taught programmer. I program in C, Assembly, QuickBASIC, and
  15651. Pascal (in that order).
  15652. Sincerely,
  15653. W. Paul Mills
  15654. 4638 N.W. 35th St.
  15655. Topeka, KS 66618-3609
  15656. Thanks for the information. I haven't researched this, so I'm mostly just
  15657. rattling, but I wonder if the qsort problem isn't just one manifestation of
  15658. the significant difference between C++'s and C's attitudes toward types and
  15659. type checking. C++ strives to be a strictly typed language -- C doesn't even
  15660. pretend. The end result must be that many common C practices won't be
  15661. acceptable C++ code.
  15662. -- rlw
  15663. Dear Mr. Ward:
  15664. I recall seeing in a back issue of The C Users Journal that the yacc/bison and
  15665. lex/flex disks were the most often requested from your disk library. Since
  15666. this seems to be what many of your readers want, I was wondering if you are
  15667. planning any more articles on lex and yacc? It seems to me that you could have
  15668. articles showing ANSI-C and C++ grammars, for instance. Or how about something
  15669. on HOC7, which was mentioned on page 77 of the July 1989 Journal.
  15670. By the way, where can I find HOC7? I couldn t find it on Compuserve, nor on
  15671. any of the local BBS s around here. Do you have it in your disk library or on
  15672. any of your mayazine code disks that I could order? Thanks in advance for any
  15673. assistance you can offer.
  15674. Sincerely,
  15675. William Pierpoint
  15676. Pierpoint Software
  15677. P.O. Box 2198
  15678. Camarillo, CA 93011
  15679. Of course I'm willing to publish some more articles about lex and yacc. I
  15680. suspect a full blown ANSI or C++ grammar would be a little beyond the scope of
  15681. a single magazine article, but we might find a way to publish it anyway.
  15682. I suspect you'll need to contact Victor Volkman directly to get a copy of
  15683. HOC7. In case you aren't familiar with it, I strongly recommend Kernighan and
  15684. Pike's The Unix Programming Environment. The book develops six increasingly
  15685. sophisticated implementations of HOC (a High Order Calculator) as lex and yacc
  15686. case studies.
  15687. -- rlw
  15688. Dear CUJ:
  15689. The December issue of CUJ arrived today. I opened it to the table of contents,
  15690. and turned eagerly to the article by Art Shipman, "Debugging with two
  15691. monitors." After scanning the article two or three times, I asked, "Where is
  15692. the hardware info on how to connect the second monitor?" Listening closely, I
  15693. heard no answer.
  15694. Reading the author's bio, I noticed he is a consultant, and his address was
  15695. given. Does this mean I am to contact him and enlist his professional services
  15696. to find out how to connect the second monitor?
  15697. I also scanned the sidebar "Utilities for a second monitor" on page 31, and
  15698. again found no mention of hardware add-on requirements; a new board? a
  15699. connector into a printer port? does my PC already have a second monitor port
  15700. which I just plug into? if so, why? ...
  15701. Can you help me out? (Which way did I come in, you ask?)
  15702. Yours in frustration,
  15703. Homer B. Tilton
  15704. 8401 Desert Steppes Drive
  15705. Tucson, Arizona 85710
  15706. As Art Shipman says in the opening paragraph of that story, you must pick up a
  15707. second video card to run a second monitor. You must, however, be careful to
  15708. select a combination of cards that can be configured to reside at separate
  15709. memory and port addresses and that can be placed on independent interrupts.
  15710. Since the second monitor is always a "non-standard" system addition, this
  15711. configuration must be customized to your particluar system. The documentation
  15712. with more sophisticated and special purpose video systems will often explain
  15713. how to configure these cards.
  15714. I hope this helps.
  15715. -- rlw
  15716. Don Libes,
  15717. Thanks for the article about software timers in the November 1990 CUJ. I
  15718. enjoyed it. A couple of observations:
  15719. In Listing 3 and Listing 5, you disable interrupts while accessing the
  15720. timers[] array. But each function has an early return that, if taken, will
  15721. leave interrupts disabled. For example, in listing 3, the line
  15722. if (!t-inuse) return;
  15723. will cause an early return without reenabling interrupts.
  15724. Also, the enable/disable of interrupts is a necessary precaution when
  15725. accessing the timer data structures. But it is sufficient only if you are
  15726. running on a single processor system that is not multiprocessing, or that
  15727. cannot preempt execution when you are executing inside a timer function, or in
  15728. any system where disable_interrupts( ) also prevents process preemption. It is
  15729. not sufficient for:
  15730. any system where process preemption (timesharing or multitasking) continues
  15731. even though disable_interrupts( ) is called, and it is
  15732. a multiprocessor system, if the timer structures are in shared memory
  15733. a single processor multi-process or multi-threaded, if the execution can be
  15734. preempted while in a timer function.
  15735. In such environments, you need an additional semaphore to restrict execution
  15736. of these critical areas to no more than one process at a time. For example, if
  15737. locks(s)/unlocks(s) are functions that lock/unlock a critical section to one
  15738. process at a time, based on the condition of the semaphore s,and if lock( )
  15739. waits indefinitely until the semaphore becomes available (a simplification),
  15740. then the code could look like this:
  15741. void
  15742. timer_undeclare(t)
  15743. struct timer *t;
  15744. {
  15745. disable_interrupts();
  15746. lock(s);
  15747. ....
  15748. unlock(s);
  15749.  
  15750. enable_interrupts();
  15751. }
  15752. Looking forward to more articles from you!
  15753. Randy D. Miller
  15754. Don replies:
  15755. Oops, you are correct about the early return without re-enabling interrupts.
  15756. As far as running in multiprocessing environments, I thought this was obvious.
  15757. However, I don't mind if it is stated more obviously as you do in your letter.
  15758. (-DL)
  15759. Dear Editor,
  15760. The "Quick Take" of Menuet in the December 1990 issue was a disaster: the
  15761. review is more like a Quick Mistake! Even though my company sells a product
  15762. that competes with Menuet, I have to come to the defense of the Menuet
  15763. developers. The review is inaccurate in some of its comments about Menuet, and
  15764. way off base in its reading of the GUI market place. Contrary to what the
  15765. review states, there is plenty of need for GUIs other than MS-Windows, HP's
  15766. New Wave and DRI's GEM.
  15767. I do agree with one thing about the article: Menuet and MetaWindow combined
  15768. will add 200K to an application. However, that's still a lot less than the 4
  15769. megabytes of memory required for decent performance with any MS-Windows
  15770. application!
  15771. The review-said: "Font support is noticeably lacking...". Wrong! Any package
  15772. based on the MetaWindow toolkit, lets you have many different fonts, sizes,
  15773. etc.
  15774. The review implies a three button mouse is "the cat's meow" (sorry). Our user
  15775. research indicates a one button mouse is easier to use and that's what end
  15776. users want! In fact, there are many end users out there who are "mouse
  15777. phobic." Having to use three buttons would give them a nervous breakdown.
  15778. Isn't the whole idea of a GUI to make the application easier to use for the
  15779. end user?
  15780. The review said: "Since Menuet requires MetaWindow as a prerequisite, the
  15781. market will be limited to those already interested in MetaWindows. Wrong! Many
  15782. of our customers (about 50%) are new to graphics, and are looking for
  15783. intelligent alternatives to MS-Windows. There are still many C programmers out
  15784. there who like to write small, compact programs, and get them done before the
  15785. turn of the century.
  15786. Most developers I talk to say MS-Windows is a pain in the tail to program in!
  15787. They looked at Windows and don't want to pay the learning curve (of perhaps
  15788. months!), or deal with such a memory hog. They need to develop applications
  15789. that do a specific task, don't take 4MB of memory, and are easy to use. In
  15790. many cases, there are better GUI development tools than Windows 3.0!
  15791. The reviewer's comment about Menuet not having intertask communication is
  15792. myopic at best. Obviously, he is enamored with leading edge technologies
  15793. (sometimes called bleeding edge!). However, most of the "trailing edge"
  15794. (probably 90% of the DOS market) doesn't need intertask communications. This
  15795. is not a "Bells and Whistles" war: developers are looking for tools that make
  15796. them productive. Is that really MS-Windows in every case? I think the reviewer
  15797. lost site of what developers and end users really need.
  15798. This is another example of reviewers not really understanding the market. They
  15799. evaluate products based on the number of bells and whistles, not on whether it
  15800. meets a need! If cars were reviewed that way, then the reviewer would
  15801. recommend that everyone buy a Rolls Royce! Good development tools are not
  15802. based on the number of bells and whistles, but on whether they make the
  15803. developer more productive!
  15804. The statement, "there seems to be little need for another me-too GUI for DOS,"
  15805. I find particularly naive, close-minded, and just plain stupid! The reviewer
  15806. has set up a false standard and implied all GUI development products should
  15807. meet this standard. That's crazy! The reviewer also implied that MS-Windows is
  15808. the ultimate development tool and that there is no reason to look at anything
  15809. else. That's frightening! This review has done an incredible disservice to
  15810. your readers. There is a great demand for alternative GUI development
  15811. products, but you would never know it by reading reviews like this one.
  15812. Sincerely,
  15813. Tod Brannan, President
  15814. Oxford Consulting Group
  15815. 1010 Oxford Street
  15816. Berkeley, CA 94707
  15817. You are right -- and you will see no more "Quick Takes" in this magazine. I
  15818. made the mistake of allowing what had been commissioned as a very cursory
  15819. "internal guidance only" survey to be repackaged for the magazine. While
  15820. cursory examinations (complete with the kinds of mistakes that accompany a
  15821. hurried scan of a complex project) are useful to some of our non-technical
  15822. staff, they are totally inappropriate for this magazine and violate all my own
  15823. rules about reviews.
  15824. I appologize to my readers and to all the vendors who were mentioned in the
  15825. December Quick Take.
  15826. And Tod, you must be one hell of a nice guy to get so upset about how a
  15827. competitor was treated! I'm glad I have people like you reading my magazine.
  15828. -- rlw
  15829.  
  15830. Listing 1
  15831. #include <stdio.h>
  15832. #include <stdlib.h>
  15833. #include <string.h>
  15834. #include <ctype.h>
  15835. #include "mdb.h"
  15836.  
  15837. static void fix_db(void);
  15838.  
  15839. #define LIST_RECS 1 /* Edit menu action codes */
  15840. #define NEXT 2
  15841. #define PREVIOUS 3
  15842. #define MODIFY 4
  15843. #define NEW 5
  15844. #define DELETE 6
  15845. #define UNDELETE 7
  15846. #define QUIT 8
  15847. #define SELECT 9
  15848. #define FIX 10
  15849.  
  15850. static struct menu_item edit_menu[] = {
  15851. {LIST_RECS, "List Records"},
  15852. {NEXT, "Go to Next Record"},
  15853. {PREVIOUS, "Go to Previous Record"},
  15854. {MODIFY, "Modify Current Record"},
  15855. {NEW, "Add New Record"},
  15856. {DELETE, "Delete Current Record"},
  15857. {UNDELETE, "Un-Delete Current Record"},
  15858. {SELECT, "Select Record (by Record Number)"},
  15859. {FIX, "Fix Up Database"},
  15860. {QUIT, "Return to Main Menu"},
  15861. {'\0'}
  15862. };
  15863.  
  15864.  
  15865. /**************************************************
  15866. * Function: edit_db
  15867. * Purpose: Perform operations on current
  15868. Database
  15869. * Parameter: Database Name
  15870. * Return Value: None
  15871. */
  15872.  
  15873. void edit_db(char *db_name) {
  15874. int cur_rec = 0; /* current record number */
  15875. struct record *rp;
  15876. char buf[150];
  15877. int i;
  15878.  
  15879. while (1) {
  15880. rp = RECS[cur_rec];
  15881. printf("\nDatabase: %s\n", db_name);
  15882. if (n_recs)
  15883. {
  15884. printf("Current Record is #%d", cur_rec);
  15885. if (!rp->active)
  15886. printf(" (Deleted)");
  15887. printf(":\n");
  15888.  
  15889. printf("Name: %s %s\n", rp->first, rp->last);
  15890. printf("ID#: %ld\n", rp->id);
  15891. printf("Age: %d\n", rp->age);
  15892. printf("Gender: %c\n", rp->gender);
  15893. printf("Salary: $%.2f\n", rp->salary);
  15894. }
  15895.  
  15896. switch(do_menu(edit_menu, "Edit Menu")) {
  15897.  
  15898. case LIST_RECS:
  15899. for(i = 0; i < n_recs; i++)
  15900. printf("%4d: %s%s\n", i, RECS[i]->last,
  15901. RECS[i]->active ? "" : "(Deleted)");
  15902. printf("Press RETURN to continue:");
  15903. gets(buf);
  15904. break;
  15905. case NEXT: /* find next active record: */
  15906. for (i = cur_rec + 1; i < n_recs; i++)
  15907. if (RECS[i]->active) /* skip inactives */
  15908. break;
  15909. if (i == n_recs) { /* over the top? */
  15910. printf("\aAt end of file.\n");
  15911. break;
  15912. }
  15913. cur_rec = i;
  15914. break;
  15915. case PREVIOUS: /* find previous active record: */
  15916. for (i = cur_rec - 1; i >= 0; i--)
  15917. if (RECS[i]->active) /* skip inactives */
  15918. break;
  15919. if (i < 0) { /* "under the bottom"? */
  15920. printf("\aAt beginning of file.\n");
  15921. break;
  15922. }
  15923.  
  15924. cur_rec = i;
  15925. break;
  15926. case NEW:
  15927. if (n_recs+1 > max_recs) {
  15928. printf("Maximum # of records ");
  15929. printf("(%d) reached.\n", max_recs);
  15930. break;
  15931. }
  15932. if ((rp = alloc_rec()) == NULL) {
  15933. printf("Out of memory. Try Fixing ");
  15934. printf("Database first...\n");
  15935. break;
  15936. }
  15937. /* make new record current: */
  15938. cur_rec = n_recs++;
  15939. RECS[cur_rec] = rp;
  15940. rp->active = TRUE;
  15941. strcpy(rp->last,""); /* initialize the record */
  15942. strcpy(rp->first,"");
  15943. rp->id = 0;
  15944. rp->age = 0;
  15945. rp->gender = ' ';
  15946. rp->salary = 0.0F; /* fall through to MODIFY */
  15947. case MODIFY:
  15948. printf("Last Name [%s]: ", rp->last);
  15949. if (strlen(gets(buf)) > 0)
  15950. strcpy(rp->last, buf);
  15951. printf("First Name [%s]: ", rp->first);
  15952. if (strlen(gets(buf)) > 0)
  15953. strcpy(rp->first, buf);
  15954. printf("ID# [%ld]: ", rp->id);
  15955. if (strlen(gets(buf)) > 0)
  15956. rp->id = atol(buf);
  15957. printf("Age [%d] ", rp->age);
  15958. if (strlen(gets(buf)) > 0)
  15959. rp->age = atoi(buf);
  15960. printf("Gender [%c]: ", rp->gender);
  15961. if (strlen(gets(buf)) > 0)
  15962. rp->gender = (char)toupper(*buf);
  15963. printf("Salary [%.2f]: ", rp->salary);
  15964. if (strlen(gets(buf)) > 0)
  15965. rp->salary = (float) atof(buf);
  15966. break;
  15967. case DELETE:
  15968. if (!rp->active) {
  15969. printf("Record is already deleted.\n");
  15970. break;
  15971. }
  15972. printf("Press 'y' to delete record,\n");
  15973. printf("anything else to abort: ");
  15974. gets(buf);
  15975. if (tolower(*buf) == 'y')
  15976. rp->active = FALSE;
  15977. break;
  15978. case UNDELETE:
  15979. if (rp->active) {
  15980. printf("Record is not deleted.\n");
  15981. break;
  15982. }
  15983.  
  15984. rp->active = TRUE;
  15985. printf("Record restored.\n");
  15986. break;
  15987. case SELECT:
  15988. printf("Enter new record number: ");
  15989. i = atoi(gets(buf));
  15990. if (i < 0 i > n_recs) {
  15991. printf("Record # out of range.\n");
  15992. break;
  15993. }
  15994. cur_rec = i;
  15995. break;
  15996. case FIX:
  15997. fix_db(); /* clean up database */
  15998. break;
  15999. case QUIT:
  16000. return;
  16001. }
  16002. }
  16003. }
  16004.  
  16005. /**************************************************
  16006. *
  16007. * Function: fix_db
  16008. * Purpose: Purge deleted records, sort db
  16009. * Parameters: None
  16010. * Return Value: None
  16011. */
  16012.  
  16013. static int compar(const void *a, const void *b);
  16014.  
  16015. static void fix_db(void) { /* File Fix module */
  16016. int i, new_n_recs;
  16017.  
  16018. for (i = 0, new_n_recs = 0; i < n_recs; i++) {
  16019. RECS[new_n_recs] = RECS[i];
  16020. if (RECS[i]->active)
  16021. new_n_recs++;
  16022. else
  16023. free(RECS[i]);
  16024. }
  16025. n_recs = new_n_recs;
  16026. qsort(RECS, new_n_recs, sizeof(struct record *), compar);
  16027. }
  16028.  
  16029. /**************************************************
  16030. * Function: compar
  16031. * Purpose: Comparison function for qsort(),
  16032. * sorting simply on last name
  16033. * Parameters: Two pointers to record pointers
  16034. * Return Value: As per strcmp()
  16035. */
  16036.  
  16037. static int compar(const void *a, const void *b) {
  16038. struct record **p1 = (struct record **)a;
  16039. struct record **p2 = (struct record **)b;
  16040. return strcmp((*p1)->last, (*p2)->last);
  16041. }
  16042.  
  16043.  
  16044.  
  16045.  
  16046.  
  16047.  
  16048.  
  16049.  
  16050.  
  16051.  
  16052.  
  16053.  
  16054.  
  16055.  
  16056.  
  16057.  
  16058.  
  16059.  
  16060.  
  16061.  
  16062.  
  16063.  
  16064.  
  16065.  
  16066.  
  16067.  
  16068.  
  16069.  
  16070.  
  16071.  
  16072.  
  16073.  
  16074.  
  16075.  
  16076.  
  16077.  
  16078.  
  16079.  
  16080.  
  16081.  
  16082.  
  16083.  
  16084.  
  16085.  
  16086.  
  16087.  
  16088.  
  16089.  
  16090.  
  16091.  
  16092.  
  16093.  
  16094.  
  16095.  
  16096.  
  16097.  
  16098.  
  16099.  
  16100.  
  16101.  
  16102.  
  16103.  
  16104.  
  16105.  
  16106.  
  16107. Embedded Real-Time Multitasking Kernel
  16108.  
  16109.  
  16110. Joel Halbert
  16111.  
  16112.  
  16113. Joel Halbert has a B.S. in electrical engineering from the University of Texas
  16114. in Austin, Texas. He has 12 years experience in the design and implementation
  16115. of computer hardware and software. He may be contacted at 1407 Meadowmear,
  16116. Austin, TX 78753.
  16117.  
  16118.  
  16119. In implementing software for embedded systems, several items can ease the job.
  16120. These include a kernel to implement a virtual machine, a high-level language
  16121. (preferably C), and a good in-circuit emulator. I describe here a simple
  16122. kernel for the Motorola 6801 microprocessor that supports tasks written in C.
  16123. I also describe how to interface between the C routines and assembly language.
  16124. I use the assembler, compiler, and in-circuit emulator (ICE) supplied by
  16125. American Automation.
  16126. The kernel is very simple. It implements only three calls. All kernel code is
  16127. written in assembly. Calls to the kernel conform to the C subroutine call
  16128. interface, however, so that tasks written in C can call the kernel directly.
  16129. C is a particularly good language to write embedded system code. It has the
  16130. bit manipulation needed to talk directly to hardware registers. Its high-level
  16131. features enable programmers to structure code for easier debugging,
  16132. maintenance, and readability. In addition, C generates less code than other
  16133. high-level languages.
  16134.  
  16135.  
  16136. 6801 Architecture
  16137.  
  16138.  
  16139. The 6801 microcontroller, designed in the late 1970s, is a relatively old
  16140. architecture. It is cheap, easy to use, and manufactured by several
  16141. semiconductor houses. The chip features:
  16142. a serial communications interface
  16143. a 16-bit three-function programmable timer
  16144. choice of single-chip or expanded 64K-byte address space
  16145. 2,048 bytes of ROM
  16146. 128 bytes of RAM
  16147. 29 parallel I/O lines of which two can be defined as handshake lines 
  16148. internal clock generator with divide-by-four output
  16149. TTL compatible inputs and outputs
  16150. Figure 1 shows the registers for the 6801. Two eight-bit accumulators (called
  16151. A and B) can be concatenated into one 16-bit register referred to as D. The
  16152. index register (called X), the stack (called S), and the program counter
  16153. (called PC) all are 16 bits wide. The condition code register (called CC) is
  16154. eight bits wide. Figure 2 shows the condition code register in greater detail.
  16155. The 6801 operates in eight different modes. The operating mode is determined
  16156. just after chip reset by levels on three of the I/O pins. Three major
  16157. divisions of these modes are:
  16158. Single Chip -- All four ports are configured as parallel input/output ports.
  16159. The MPU functions as a self-contained microcomputer with no external address
  16160. or data bus.
  16161. Expanded Non-Multiplexed -- The MPU can directly address modest amounts of
  16162. external memory (up to 256 bytes).
  16163. Expanded Multiplexed -- The MPU can address the entire 64K byte address space.
  16164. In addition, 13 I/O lines are available.
  16165. The kernel I describe here uses Expanded Multiplexed mode. That supports
  16166. external RAM and ROM that can reside in sockets for expandability and ease of
  16167. maintenance.
  16168.  
  16169.  
  16170. Kernel Architecture
  16171.  
  16172.  
  16173. I consider all code except for the tasks part of the system. The rest is task
  16174. code. To initiate multitasking, the application program calls a setup routine,
  16175. giving the starting function pointer for each task. This routine allocates
  16176. separate stack areas (distinct from from system stack) for each of a fixed
  16177. number of tasks. It then sets up a current stack pointer and execution pointer
  16178. for each task. After the initialization of four tasks, for example, the system
  16179. can concurrently execute the tasks with code similar to
  16180. while (true)
  16181. {
  16182. continue_task(0);
  16183. continue_task(1);
  16184. continue_task(2);
  16185. continue_task(3);
  16186. }
  16187. The continue_task function (Listing 1) transfers control to the numbered task
  16188. and returns when the task calls suspend. The continue_task and suspend
  16189. functions also save and restore any register variables on the task stack. You
  16190. can expand the while (true) test to check for various stopping conditions,
  16191. including global status flags set by the tasks. This kind of multitasking is
  16192. non-preemptive. It requires that the tasks call suspend frequently. Since each
  16193. task has its own stack, the call to suspend may occur at any level of
  16194. subroutine nesting.
  16195. The kernel may appear to contain a lot of functionality to pack into two pages
  16196. of assembly code, but it is actually much simpler than a general real-time
  16197. multitasking kernel. For example, this task scheduler provides no means for an
  16198. external event to activate a task. Every task is expected to have control for
  16199. a short time in every loop; the task itself must check for activation
  16200. conditions. Also, the scheduler does not provide signaling between tasks. The
  16201. tasks do, however, use message queues and global tables to implement this type
  16202. of signaling.
  16203. Since all tasks are linked with the kernel as reentrant functions in one
  16204. program, special care must be taken when referencing static variables (both
  16205. global and non-global). In general, a tasks's use of static variables should
  16206. be limited to deliberate inter-task communication. Variables allocated off the
  16207. stack are automatically private to the task.
  16208. It may be helpful to see a diagram of the stack structure at various times of
  16209. the execution of the kernel. At startup, memory is uninitialized. No stacks
  16210. for task are yet defined. When the startup code calls the initialization
  16211. routines, the task's stacks are defined, as shown in Figure 3.
  16212. Two arrays, task_tab and fram_tab, hold the stack pointers and frame pointers
  16213. for each task. The kernel saves the system stack pointer in sys_stk and the
  16214. system frame pointer in sys_sf. When a task is in control, it is using its own
  16215. stack and frame pointers. When the task calls suspend, the kernel saves these
  16216. pointers in the task table and retrieves those for the system. The task
  16217. transfers control to the system by setting task mode to 0 and executing the
  16218. next task in the master while loop in main.
  16219.  
  16220.  
  16221. Data Flow
  16222.  
  16223.  
  16224. The tasks communicate with each other via circular queues and system tables.
  16225. At system startup, all queues are empty and the system tables contain default
  16226. values. Serious operation starts when the computer receives operating
  16227. parameters via the serial lines. This causes an interrupt routine to insert
  16228. characters into a circular buffer called in_queue.
  16229. When activated, the message_in (Listing 2) task sees that characters are in
  16230. in_queue. The task determines what kind of message has been sent. After
  16231. decoding the message, the task loads a return message into another circular
  16232. buffer, called out_que.
  16233.  
  16234. When the task message_out (Listing 3) is activated, it sees that characters
  16235. are in out_queue. The task removes the message from out_que and reformats it
  16236. as a message suitable for serial transmission -- with CRC bytes generated and
  16237. transparency characters inserted. The formatted message is then put into the
  16238. xmit_que for sending over the serial line. The task msg_out then calls
  16239. send_msg, which sends out the first character, enables the send character
  16240. interrupt, and waits until all characters of the message are sent before
  16241. disabling the send character interrupt.
  16242. The task key_brd in Listing 4 checks for key strokes on the key pad. It works
  16243. with the counter interrupt routine. If a key is being pressed, the interrupt
  16244. routine rcv_key decodes the key and puts it into the global variable
  16245. keyboard_port. When activated, key_brd checks for a character in
  16246. keyboard_port. If key_brd finds a character, it inserts the character into
  16247. key_que.
  16248. The task display in Listing 5 reads the queue key_que to determine whether or
  16249. not the the key should affect the display. In addition, display loads a buffer
  16250. containing key strokes for serial transmission.
  16251.  
  16252.  
  16253. Task Structure
  16254.  
  16255.  
  16256. All tasks must be organized the same way for this scheme to work. The basic
  16257. structure is an endless loop:
  16258. while (true)
  16259. {
  16260. /* all statements
  16261. * that make up the
  16262. * task
  16263. */
  16264. }
  16265. All tasks communicate via shared memory (global memory) using circular queues
  16266. and semaphores. Also, the tasks read system tables for setting operational
  16267. parameters.
  16268.  
  16269.  
  16270. Interfacing Assembly With C
  16271.  
  16272.  
  16273. In this project, I interfaced assembly with C using two different approaches
  16274. -- writing functions completely in one language and embedding in-line assembly
  16275. statements within C code.
  16276. When writing a function in assembly, the most important aspect is the function
  16277. calling convention for the particular C compiler you are using. With the
  16278. American Automation C compiler that I used for this project, the parameters in
  16279. a call are handled differently depending on the number of parameters passed.
  16280. If you are passing only one parameter to the subroutine, then the parameter is
  16281. passed in the D register. If you are passing more than one parameter, then the
  16282. leftmost parameter is passed in the D register, and the rest of the parameters
  16283. are passed through the stack. Examine the following call:
  16284. init_task(1,&message_in);
  16285. The leftmost paramater, 1, is passed in the D register, and &message_in, the
  16286. address of the subroutine message_in, is passed on the stack.
  16287. The following example shows the second approach, embedding assembly within a C
  16288. function. The example illustrates how to reference the same variable by C code
  16289. and assembly. A typical variable declaration in C is
  16290. extern unsigned char VARIABLE;
  16291. Compilers usually transform the variable name when translating the C
  16292. statements to assembly. A common transformation is to add an underscore or
  16293. some other character either before or after the name. The American Automation
  16294. compiler appends a question mark ? to a variable name. When referencing the
  16295. variable within assembly, I use the following statement:
  16296. xref VARIABLE?
  16297. Each compiler has its own way to embed assembly language in C. Turbo C uses a
  16298. keyword, asm, to indicate to the complier that the following statement is
  16299. in-line assembly. The American Automation compiler uses the keyword #asm to
  16300. indicate the beginning of a series of assembly language statements and the
  16301. keyword #endasm to indicate the end of a series of assembly language
  16302. statements. American Automation recommends that all #asm statements be
  16303. immediately preceeded with a semicolon. See Listing 5.
  16304. In assembly language, an address gets assigned to each variable that you
  16305. define. A final hardware address is determined for a variable name using a
  16306. combination of SECTION and DS (define storage) declarations. The American
  16307. Automation C compiler supplies an assembly language startup file that is
  16308. always linked first in front of all other files. This file is where you would
  16309. assign memory addresses to hardware registers residing on your board. The
  16310. following demonstrates a SECTION statement in an assembly language source
  16311. file.
  16312. SECTION IO,?,DATA ; data (I/O) start address
  16313. You are defining a section of memory called IO.? means the address will be
  16314. defined at link time, and DATA means it is a data section. IO has previously
  16315. been defined as 3000. You are telling the assembler to create a section of
  16316. memory starting at address 3000 that will be uninitialized data.
  16317. Now you can begin to use DS statements to assign variables to memory
  16318. locations. For example:
  16319. XDEF I_O?, VARIABLE?,OTHER?
  16320. I_O?: DS 1
  16321. VARIABLE?:
  16322. OTHER?: DS 1
  16323. I have defined three variables -- I_O, VARIABLE, and OTHER. Notice that the
  16324. XDEF directive enables other files that are linked with this file to know
  16325. about these variables. Note also that the DS statements come after the SECTION
  16326. statement. Any other DS statements that follow will define storage in the IO
  16327. section until another SECTION statement is encountered.
  16328.  
  16329.  
  16330. Interrupts
  16331.  
  16332.  
  16333. Usually, embedded systems software deals with interrupts. These interrupts
  16334. come from a variety of sources. The code I show here deals with interrupts
  16335. from the programmable timer and the serial communications interface.
  16336. The programmable timer is set up to be a free running counter that will
  16337. generate an interrupt whenever the contents of the counter equal 0. This timer
  16338. generates delays and also reads the keypad for any keys that are pressed. The
  16339. serial communications interface generates an interrupt whenever a character is
  16340. received by the serial port receive buffer and whenever the transmit buffer is
  16341. empty.
  16342. You incorporate interrupts by the following method. The startup file assigns
  16343. the addess of the interrupt routine to vector locations by the method shown in
  16344. Listing 6. The first statement is a SECTION directive that creates a section
  16345. called VECTORS at location . It is followed by define words containing
  16346. addresses of the routines that will be invoked when the vector is executed. In
  16347. addition, the startup file contains an assembly language routine that provides
  16348. the glue needed to call a C subroutine, which actually does the work in the
  16349. interrupt handler. There is an assembly language routine for each vector that
  16350. may be needed. The routine for the timer is shown in Listing 7.
  16351. With this setup, you can write your interrupt routine much like any other C
  16352. function.
  16353. Figure 1
  16354. Figure 2
  16355. Figure 3
  16356.  
  16357. Listing 1 (multask.s)
  16358. title multask.s
  16359. name mtask
  16360. *********************************************************************
  16361. *
  16362. * NAME :
  16363.  
  16364. *
  16365. * DESCRIPTION : multasking routines for the motorola 68xx series
  16366. * of microcomputers
  16367. *
  16368. * init_task(task#, task_address) /* called from system mode */
  16369. * continue_task(task#) /* called from system mode */
  16370. * suspend() /* called from task mode */
  16371. *
  16372. * The task_mode flag to keep track of which mode is in effect
  16373. * allows the use of subroutines hat are called by both system
  16374. * and task code. If those subroutines call suspend(). then in
  16375. * the case of a system mode caller syspend() must return
  16376. * control immediately.
  16377. *
  16378. *********************************************************************/
  16379.  
  16380. * COMMON DEFINITIONS */
  16381. stksize equ 255
  16382. tasks equ 4
  16383.  
  16384. xref SF ;Stack Frame
  16385.  
  16386. SECTION DATA,?,DATA
  16387.  
  16388. sys_stk dw 0
  16389. sys_sf dw 0
  16390. task_num dw 0
  16391. task_mode db 0
  16392. tstk dw 0 ; temporary stack storage
  16393. task_tab ds tasks*2 ; tasks words
  16394. fram_tab ds tasks*2 ; frame words
  16395. SECTION STACK,?,DATA
  16396. xdef _task_stks, task_stks
  16397. _task_stks
  16398. task_stks ds stksize*(tasks+1)
  16399. pad2 dw 0 ; end boundry for data
  16400. xdef _sp?
  16401. _sp?: DS 2
  16402. pad3 dw 0 ; end boundry for stacks
  16403.  
  16404. SECTION PROGRAM_CODE,?,CODE
  16405.  
  16406. xdef init_task?
  16407. xdef continue_task?
  16408. xdef suspend?
  16409.  
  16410. *********************************************************************
  16411. *
  16412. * NAME : init_task(task_number, task_address)
  16413. *
  16414. * DESCRIPTION: initializes the task specified by the task address
  16415. * saves the stack pointer into sys_stack
  16416. * saves the task number into task_num
  16417. * calculates the beginning of the task's stack
  16418. * loads the stack pointer with the results of the preceeding
  16419. * calculation
  16420. * sets task_mode to one to indicate that we are in task mode
  16421. * push address of callsus to new stack in case the task returns
  16422. * this prepares the return address of a hung task that
  16423.  
  16424. * repeatedly gives up control by calling suspend
  16425. * this routine drops through to suspend.
  16426. * so in effect this routine sets up a task with a stack then
  16427. * prepares the stack for the next call as if it were running
  16428. * then suspends itself.
  16429. *
  16430. init_task?:
  16431. sts sys_stk ; Save the system stack
  16432. std task_num ; save parameter task number for later
  16433. ldd SF ; get stack frame pointer and save into
  16434. std sys_sf ; system variable
  16435. ldd task_num
  16436. ldaa #stksize ; make calculation of task_num times
  16437. mul ; stack size then add to beginning
  16438. addd #(task_stks+stksize) ; of stack pool
  16439. std tstk ; put d into temporary stack storage
  16440. lds tstk ; get new stack from temp. stack storage
  16441. subd #3 ; adjust frame pointer for next routine
  16442. std SF ; load new stack frame pointer
  16443. inc task_mode ; put us into task mode
  16444. ldd #callsus ; get address of suspend and
  16445. pshb ; load it into the new stack
  16446. psha ;
  16447. pshx ; save stack into new stack
  16448.  
  16449. * fall through to next routine (suspend)
  16450.  
  16451. *********************************************************************
  16452. *
  16453. * NAME : suspend()
  16454. *
  16455. * DESCRIPTION: suspend the current task by saving the current stack
  16456. * stack pointer into the task table and then getting the
  16457. * system stack and transfer control to the system, setting
  16458. * task mode to 0 and start executing the next task in the
  16459. * master while loop in main.
  16460. *
  16461. suspend?: ; see if we are in task mode if s?o
  16462. tst task_mode ; go to suspend task
  16463. bne sustsk ; else return
  16464. rts
  16465. sustsk:
  16466. ldd task_num ; get the task number and multiply by two
  16467. lsld ; for indexing purposes into the task table
  16468. ldx #task_tab
  16469. abx
  16470. sts x ; store the stack into the proper table entry
  16471. ldx #fram_tab ; get address of frame table and store SF
  16472. abx ; into indexed address of frame table array
  16473. ldd SF
  16474. std x
  16475. lds sys_stk ; restore the system stack
  16476. ldd sys_sf ; restore stack frame pointer
  16477. std SF
  16478. clr task_mode ; go into system mode
  16479. rts ; and return
  16480.  
  16481. *********************************************************************
  16482. *
  16483.  
  16484. * NAME : continue(task_number)
  16485. *
  16486. * DESCRIPTION: continues the task specified by the task number
  16487. * get the task number in the task table
  16488. * loads the stack pointer at the table index
  16489. * set task mode to one indication we are task mode
  16490. * and returns
  16491. *
  16492. *
  16493.  
  16494. continue_task?:
  16495. sts sys_stk ; store the stack
  16496. std task_num ; save into variable task_num
  16497. ldd SF
  16498. std sys_sf
  16499. ldd task_num
  16500. lsld ; multiply task number by 2
  16501. ldx #task_tab ; get address of tasktable into index
  16502. abx ; add (task hum * 2) + table address
  16503. lds x ; load new stack into sp
  16504. ldx #fram_tab ; get the task's frame pointer and update
  16505. abx ; the frame pointer
  16506. ldd x
  16507. std SF
  16508. inc task_mode ; set task mode to task
  16509. rts ; return ( to new task)
  16510.  
  16511. *********************************************************************
  16512. *
  16513. * NAME : callsus
  16514. *
  16515. * DESCRIPTION: this routine is in case the task returns all the way
  16516. * back to the main routine.
  16517. *
  16518.  
  16519. callsus:
  16520. bsr suspend?
  16521. bra callsus
  16522.  
  16523. end
  16524.  
  16525.  
  16526. Listing 2 (msg_out.c)
  16527. /******************************************************
  16528. * NAME: message_in
  16529. * DESCRIPTION: Text ........
  16530. ******************************************************/
  16531.  
  16532. #include "que.h"
  16533.  
  16534. extern struct g_queue in_que;
  16535.  
  16536. void message_in()
  16537. {
  16538. unsigned char new_msg,temp;
  16539. int i, state;
  16540.  
  16541. while (true)
  16542. {
  16543.  
  16544. new_msg = true;
  16545. i = 1;
  16546. state = 0;
  16547. /* now look at each character if the character is
  16548. a DLE then the next character is ingored */
  16549. while (new_msg)
  16550. {
  16551. temp = remove_one(&in_que);
  16552. /*
  16553. * implement a state machine to
  16554. * format the incoming message
  16555. * to a form suitable for your application
  16556. */
  16557. }
  16558. /*
  16559. * calculate the crc
  16560. */
  16561. /* determine the address */
  16562. /*
  16563. * determine the type of message
  16564. * and send a reply
  16565. */
  16566. }
  16567. }
  16568.  
  16569.  
  16570. Listing 3 (key_brd.c)
  16571. /******************************************************
  16572. * NAME : key_msg
  16573. * DESCRIPTION: get a key, decode, put in key queue
  16574. ******************************************************/
  16575. #include "que.h"
  16576.  
  16577. extern struct g_queue key_que;
  16578.  
  16579. void key_msg()
  16580. {
  16581.  
  16582. unsigned char position, key;
  16583.  
  16584. position = (unsigned char)0x00;
  16585. while ( 1 )
  16586. {
  16587. while (getkey(&position) < 0)
  16588. suspend();
  16589. insert_one(position,&key_que);
  16590. }
  16591. }
  16592.  
  16593. /******************************************************
  16594. * NAME: getkey
  16595. * DESCRIPTION:get key position from interrupt routine
  16596. ******************************************************/
  16597.  
  16598. getkey(position)
  16599. unsigned char *position;
  16600. {
  16601. int x;
  16602. /* has there been a read of keypad
  16603.  
  16604. * is there a disable msg
  16605. * if there has been a read - then
  16606. * decode and verify the key then return
  16607. * it in x
  16608. */
  16609.  
  16610. return x;
  16611. }
  16612.  
  16613.  
  16614. Listing 4 (display.c)
  16615. /***************************************************
  16616. * NAME display
  16617. * DESCRIPTION: skeleton routine
  16618. * to read key stroke
  16619. ***************************************************/
  16620.  
  16621. void t_display()
  16622. {
  16623. unsigned char key;
  16624. int state = 0;
  16625.  
  16626. while(1)
  16627. {
  16628. switch (state)
  16629. {
  16630. case 0:
  16631. /*
  16632. * here you would implement a
  16633. * state machine to handle cases of
  16634. * key strokes and their effect on
  16635. * the display
  16636. */
  16637. default:
  16638. break;
  16639. } /* end of switch logic for states */
  16640.  
  16641. while (key_que.empty && !reset && !disable_on)
  16642. suspend();
  16643. key = remove_one(&key_que);
  16644.  
  16645. }
  16646. }
  16647.  
  16648.  
  16649. Listing 5
  16650. exam(stuff)
  16651. char *stuff;
  16652. {
  16653. char i;
  16654.  
  16655. for (i=6; i>=0; i--)
  16656. {
  16657. I_O = stuff[i];
  16658. #asm /* #asm to indicate start of assembly */
  16659. xref VARIABLE? /* note the external reference */
  16660. STAA VARIABLE? /* to VARIABLE */
  16661. #endasm /* #endasm to indicate end of assembly */
  16662. }
  16663.  
  16664. ; /* required semi-colon to preceed #asm */
  16665. #asm
  16666. xref OTHER?
  16667. LDAA OTHER?
  16668. #endasm
  16669. }
  16670.  
  16671.  
  16672. Listing 6
  16673. SECTION VECTORS,$FFFO,CODE
  16674. _VECTORS:
  16675. DW timer ;$FFFO - SCI
  16676. DW serial ;$FFF2 - TOF (Timer Overflow)
  16677. DW 0 ;$FFF4 - OCF (Output Compare)
  16678. DW 0 ;$FFF6 - ICF (Input Capture)
  16679. DW 0 ;$FFF8 - IRQ1 (or IS3)
  16680. DW 0 ;$FFFA - SWI (Software Interrupt)
  16681. DW 0 ;$FFFC - NMI (Non-maskable Interrupt)
  16682. DW main ;$FFFE - RESET (Hardware Reset)
  16683.  
  16684.  
  16685. Listing 7
  16686. timer:
  16687. XREF counter_irq?
  16688. LDX PR
  16689. PSHX
  16690. LDX SR
  16691. PSHX
  16692. JSR counter_irq? ; place the name of C interrupt handler here
  16693. PULX
  16694. STX SR
  16695. PULX
  16696. STX PR
  16697. RTI
  16698.  
  16699.  
  16700. Listing 8 (interupt.c)
  16701. /************************************************************
  16702. * NAME : Interupt.c
  16703. * DESCRIPTION : Interrupt routines for the preset controller
  16704. ************************************************************/
  16705.  
  16706. /************************************************************
  16707. *
  16708. * NAM : rcv_msg
  16709. * DESCRIPTION: called by the interrupt routine which is
  16710. * invoked by uart interrupt bit.
  16711. * COMPATIBLE LANGUAGES: <Assembler only>
  16712. ************************************************************/
  16713. #include "serial.h"
  16714. #include "que.h"
  16715.  
  16716. extern TRCSR;
  16717. extern TDR;
  16718. extern xstatus;
  16719. extern struct g_queue xmit_que;
  16720. extern struct g_queue in_que;
  16721. extern RDR;
  16722. rcv_msg()
  16723.  
  16724. {
  16725. unsigned char xstatus, temp;
  16726. unsigned int remove, insert;
  16727.  
  16728. xstatus = TRCSR;
  16729. if (xstatus & RDRF) /* did we receive something? */
  16730. {
  16731. temp = RDR;
  16732. remove = in_que.remove;
  16733. insert = in_que. insert;
  16734. if ((insert+1!=remove) && (insert-remove!=kqlength-1))
  16735. { /* queue is full */
  16736. in_que.que[insert++] = temp;
  16737. if (insert == kqlength)
  16738. insert = 0;
  16739. in_que.insert = insert;
  16740. in_que.empty = false;
  16741. }
  16742. }
  16743. if (xstatus & TDRE)
  16744. { /* do we have something to send? */
  16745. if(xmit_que.empty == false)
  16746. {
  16747. remove = xmit_que.remove;
  16748. TDR = xmit_que.que[remove++];
  16749. if (remove == kqlength)
  16750. remove = 0;
  16751. xmit_que.remove = remove;
  16752. if (remove == xmit_que.insert)
  16753. xmit_que.empty = true;
  16754. }
  16755. else
  16756. {
  16757. /* turn off the interupt for transmit buffer empty and
  16758. turn off the serial transmit buffer */
  16759. TRCSR &= ~TIE;
  16760. }
  16761. }
  16762. }
  16763.  
  16764. /************************************************************
  16765. *
  16766. * NAME : counter_irq
  16767. *
  16768. * DESCRIPTION: called by the interrupt routine which is
  16769. * invoked by global timer interrupt - increments
  16770. * the global value of time and reads the keyboard.
  16771. *************************************************************/
  16772.  
  16773. extern enable_timer;
  16774. extern globaltime;
  16775. extern flashtime;
  16776. extern dspy;
  16777. extern disable_on;
  16778. extern keyboard_port;
  16779. extern P1DR;
  16780. extern pad_read;
  16781.  
  16782. counter_irq()
  16783.  
  16784. {
  16785. unsigned char i,line;
  16786. static unsigned char keyport;
  16787. unsigned int temp;
  16788.  
  16789. /* clear the TOF bit in the timer control & status reg.
  16790. * AND then clear the interupt mask in the condition code reg.
  16791. */
  16792. ;
  16793. #asm
  16794. XREF TCSR?
  16795. LDD TCSR?
  16796.  
  16797. CLI
  16798. #endasm
  16799.  
  16800. /* increment the global timer variable */
  16801. if (enable_timer) globaltime += 1;
  16802.  
  16803. /* increment the flash time */
  16804. flashtime += 1;
  16805.  
  16806. /* refresh the display */
  16807. show(dspy);
  16808.  
  16809. /* if (!disable_on) then read the keypad */
  16810. if (!disable_on)
  16811. {
  16812. line = 0x8;
  16813. for (i=0; i<4; i++)
  16814. {
  16815. P1DR = line;
  16816. line >>= 1;
  16817. keyport = P1DR;
  16818. if (keyport & 0xf0)
  16819. break;
  16820. }
  16821. if (i==0)
  16822. keyboard_port = (keyport & 0xf0) 1;
  16823. else if (i==1)
  16824. keyboard_port = (keyport & 0xf0) 2;
  16825. else if (i==2)
  16826. keyboard_port = (keyport & 0xf0) 4;
  16827. else if (i==3)
  16828. keyboard_port = (keyport & 0xf0) 8;
  16829. pad_read = true;
  16830. }
  16831. }
  16832.  
  16833.  
  16834. Listing 9 (msg_out.c)
  16835. /*****************************************************
  16836. * NAME : message_out
  16837. *
  16838. * DESCRIPTION:
  16839. * waits for a message in the message out queue,
  16840. * when one appears it assembles it then sends it.
  16841. *****************************************************/
  16842.  
  16843.  
  16844. #include "que.h"
  16845.  
  16846. extern struct g_queue out_que;
  16847. unsigned char buf[32];
  16848.  
  16849. void message_out()
  16850. {
  16851. unsigned char no_msg_out,length;
  16852. int i;
  16853.  
  16854. while (true)
  16855. {
  16856. length = remove_one(&out_que);
  16857. buf[0] = length;
  16858. for(i=1; i<=length; i++)
  16859. buf[i] = remove_one(&out_que);
  16860. format_msg();
  16861. send_msg();
  16862. }
  16863. }
  16864.  
  16865. /******************************************************
  16866. * NAME: format_msg
  16867. *
  16868. * DESCRIPTION: takes a message in the buffer and
  16869. * formats it for the serial port.
  16870. *******************************************************/
  16871.  
  16872. format_msg()
  16873. {
  16874. /* prepare a buffer for crc generation */
  16875.  
  16876. /* now calculate the CRC */
  16877.  
  16878. /* insert the crc bytes into the buffer */
  16879.  
  16880. /* now add the DLE characters */
  16881.  
  16882. /* add the stop flag */
  16883.  
  16884. /* now check the CRC characters to see if we should add DLE's */
  16885.  
  16886. /* finally - put in the length of total buffer */
  16887.  
  16888. }
  16889.  
  16890. **********************************************************************
  16891. *
  16892. * NAME : send_msg
  16893. *
  16894. * */
  16895. send_msg()
  16896. {
  16897. /* send the first character to the serial port to kick off the
  16898. serial transmission. turn on the interupts and let the interrupts
  16899. finish the rest of the transmission */
  16900.  
  16901. /* now turn on the serial transmit buffer */
  16902.  
  16903.  
  16904. /* send the first character to the serial transmit buffer */
  16905.  
  16906. /* enable the interrupt for serial transmit data register empty */
  16907.  
  16908. /* wait for the last character to be completely transmitted */
  16909. }
  16910.  
  16911.  
  16912. Listing 10 (que_hand.c)
  16913. /**************************************************
  16914. * NAME : insert_one
  16915. *
  16916. * DESCRIPTION: insert a item into the item queue.
  16917. * This queue works as a circular buffer with two
  16918. * pointers, insert and delete. As a character is
  16919. * inserted the insert pointer is incremented
  16920. * modulo que length. In other words after each
  16921. * increment the pointer is checked to see if it
  16922. * equals the length of the que. If so, then it
  16923. * is reset to zero. When the pointers are equal
  16924. * then the que is full. The insert pointer is always
  16925. * incremented first then the item is inserted.
  16926. * The empty flag is always set false if a item
  16927. * is inserted.
  16928. ******************************************************/
  16929.  
  16930. #include "que.h"
  16931.  
  16932. insert_one(item, k_que)
  16933. unsigned char item;
  16934. struct g_queue *k_que;
  16935. {
  16936.  
  16937. unsigned int insert,remove;
  16938.  
  16939. while (((k_que->insert)+1==k_que->remove) Â½Â½
  16940. ((k_que->insert)-(k_que->remove)==kqlength-1))
  16941. suspend(); /* queue is full */
  16942.  
  16943. set_irq();
  16944. insert = k_que->insert;
  16945. remove = k_que->remove;
  16946. (*k_que).que[insert] = item;
  16947. if (++insert == kqlength)
  16948. insert = 0;
  16949. k_que->insert = insert;
  16950. k_que->empty = false;
  16951. clear_irq();
  16952. }
  16953.  
  16954. /***********************************************
  16955. * NAME : remove_one
  16956. *
  16957. * DESCRIPTION: remove a item from the item queue.
  16958. * This queue works as a circular buffer with two
  16959. * pointers insert, and delete. as a character is
  16960. * removed the remove pointer is incremented modulo
  16961. * que length. In other words after each increment
  16962. * the pointer is checked to see if it equals the
  16963.  
  16964. * length of the que. If so, then it is reset
  16965. * to zero. When the pointers are equal then the
  16966. * que is empty. And the que empty flag is set
  16967. * true. The remove pointer is always pointing to
  16968. * the next character to be removed. When the
  16969. * remove pointer is equal to the insert pointer
  16970. * - 1 then the queue is full
  16971. *
  16972. ***************************************************/
  16973.  
  16974. remove_one(k_que)
  16975. struct g_queue *k_que;
  16976. {
  16977.  
  16978. unsigned int insert,remove;
  16979. unsigned char item;
  16980.  
  16981. while (k_que->empty == true)
  16982. suspend();
  16983. set_irq();
  16984. insert = k_que->insert;
  16985. remove = k_que->remove;
  16986. item = k_que->que[remove];
  16987. if (++remove == kqlength)
  16988. remove = 0;
  16989. if ( remove == insert )
  16990. k_que->empty = true;
  16991.  
  16992. k_que->remove = remove;
  16993. clear_irq();
  16994. return item;
  16995. }
  16996.  
  16997.  
  16998. Listing 11 (pcon.h)
  16999. /*************************************
  17000. * Name : pcon.h
  17001. * Description: data structures for
  17002. * communication within the kernel
  17003. *************************************/
  17004.  
  17005. struct sale_status {
  17006. unsigned char sale_flag;
  17007. unsigned char digits[6];
  17008. unsigned char enter_flag;
  17009. };
  17010.  
  17011. struct rev_status {
  17012. unsigned char major;
  17013. unsigned char minor;
  17014. unsigned char date[6];
  17015. };
  17016.  
  17017. struct config_table {
  17018. unsigned char jordan_round;
  17019. unsigned char num_dec_pts_c;
  17020. unsigned char num_dec_pts_v;
  17021. unsigned char cash_vol_def;
  17022. unsigned char c_or_v;
  17023.  
  17024. unsigned char fill_dash;
  17025. unsigned char lst_sig_dig_c;
  17026. unsigned char lst_sig_dig_v;
  17027. unsigned char preset_req;
  17028. };
  17029.  
  17030. /* data structures for serial comm mesages */
  17031.  
  17032. struct sale_msg { /* almost the same as */
  17033. unsigned char salef; /* sale_status except */
  17034. unsigned char digits[3]; /* the digits are */
  17035. unsigned char enterf; /* represented by BCD */
  17036. }; /* instead of byte */
  17037.  
  17038. struct rev_msg {
  17039. unsigned char major;
  17040. unsigned char minor;
  17041. unsigned char date[3];
  17042. };
  17043.  
  17044.  
  17045. Listing 12 (serial.h)
  17046. /*****************************************
  17047. * Name: serial.h
  17048. * Description: all info and data
  17049. * structures for serial comm
  17050. *****************************************/
  17051.  
  17052. #define WU 0x01 /* wake up bit */
  17053. #define TE 0x02 /* transmit enable */
  17054. #define TIE 0x04 /* transmit interrupt enable */
  17055. #define RE 0x08 /* receive enable */
  17056. #define RIE 0x10 /* receive interrupt enable */
  17057. #define TDRE 0x20 /* transmit data register empty */
  17058. #define ORFE 0x40 /* overrun framing error */
  17059. #define RDRF 0x80 /* receive data register full */
  17060.  
  17061. #define SSO 0x01
  17062. #define SS1 0x02
  17063. #define CCO 0x04
  17064. #define CC1 0x08
  17065.  
  17066. #define test_mode CC1 Â½ SSO /* RMCR = 00001001 */
  17067. #define tr_test TE Â½ RE /* TRCSR = 00001010 */
  17068. #define normal_mmode RIE Â½ RE Â½ TE
  17069. /* TRCSR = 00011010 */
  17070.  
  17071.  
  17072. Listing 13 (que.h)
  17073. /************************************
  17074. * Name: que.h
  17075. * Description: typedef for queues for
  17076. * preset controller
  17077. ************************************/
  17078.  
  17079. #define kqlength 32
  17080.  
  17081. struct g_queue {
  17082. unsigned char empty;
  17083.  
  17084. unsigned int insert;
  17085. unsigned int remove;
  17086. unsigned char que[kqlength];
  17087. };
  17088.  
  17089. typedef unsigned char message_buf[32];
  17090.  
  17091.  
  17092. Listing 14 (main.c)
  17093. /*******************************************
  17094. * NAME : Main.c
  17095. *
  17096. * DESCRIPTION : Main controlling routine
  17097. * for the preset controller.
  17098. ******************************************/
  17099.  
  17100. #include "pcon. h"
  17101. #include "que.h"
  17102. #include "serial.h"
  17103.  
  17104. extern void key_msg();
  17105. extern void message_out();
  17106. extern void massage_in();
  17107. extern void display();
  17108. extern void diag();
  17109.  
  17110. /* at 08 Timer Control and Status Register: */
  17111. extern unsigned char TCSR;
  17112. /* at 09:0A Counter: */
  17113. extern unsigned char COUNTER;
  17114. /* at 0B:0C Output Compare Register: */
  17115. extern unsigned char OCR;
  17116. /* at 0D:0E Input Capture Register: */
  17117. extern unsigned char ICR;
  17118. /* at 0F Port 3 Control and Status Register: */
  17119. extern unsigned char P3CSR;
  17120. /* rate and mode control register: */
  17121. extern unsigned char RMCR;
  17122. /* transmit/receive control register: */
  17123. extern unsigned char TRCSR;
  17124. /* receive data register: */
  17125. extern unsigned char RDR;
  17126. /* transmit data register: */
  17127. extern unsigned char TDR;
  17128.  
  17129. extern unsigned char P1DDR;
  17130. extern unsigned char P1DR;
  17131. extern unsigned char P2DDR;
  17132. extern unsigned char P2DR;
  17133.  
  17134. /* for debugging */
  17135. extern unsigned char over, received;
  17136.  
  17137. struct sale_status key_buf;
  17138. struct rev_status rev_stat;
  17139. struct config_table config;
  17140. struct g_queue key_que;
  17141. struct g_queue in_que;
  17142. struct g_queue out_que;
  17143.  
  17144. struct g_queue xmit_que;
  17145.  
  17146. int globaltime;
  17147. in flashtime;
  17148. unsigned char disable_on;
  17149. unsigned char test_stat;
  17150. unsigned char nozzle_up;
  17151. extern unsigned char enable_timer;
  17152.  
  17153. /* date of release */
  17154. const char date[] = "022290";
  17155. /* major revision number */
  17156. define revis0 0
  17157. /* minor revision number */
  17158. define revis1 6
  17159.  
  17160. /**************************************************
  17161. * NAME : main
  17162. *
  17163. * DESCRIPTION: call all the initialization of
  17164. * hardware and task setup then
  17165. * loops forever and calls each task
  17166. * in a round-robin fashion. Each
  17167. * task will run until it suspends
  17168. * itself then return to main for
  17169. * the next task in the list.
  17170. ***************************************************/
  17171.  
  17172. void main()
  17173. {
  17174.  
  17175. initsystem();
  17176. initstructures();
  17177. init_task(0,&display);
  17178. init_task(1,&message_in);
  17179. init_task(2,&message_out);
  17180. init_task(3,&key_msg);
  17181. clear_irq();
  17182. while(true)
  17183. {
  17184. continue_task(0);
  17185. continue_task(1);
  17186. continue_task(2);
  17187. continue_task(3);
  17188. }
  17189. }
  17190.  
  17191. /****************************************************
  17192. * NAME : initstructures
  17193. *
  17194. * DESCRIPTION: init the data structures for the
  17195. * firmware sets up all pointers and
  17196. * what ever else need doing
  17197. ****************************************************/
  17198.  
  17199. initstructures ()
  17200. {
  17201. int i;
  17202.  
  17203.  
  17204. /* init the key queue */
  17205. key_que. insert= 0;
  17206. key_que.remove = 0;
  17207. key_que.empty = true;
  17208.  
  17209. /* init the in message que */
  17210. in_que.insert = 0;
  17211. in_que.remove = 0;
  17212. in_que.empty = true;
  17213.  
  17214. /* init the out message que */
  17215. out_que.insert = 0;
  17216. out_que.remove = 0;
  17217. out_que.empty = true;
  17218.  
  17219. /* init the Transmit que */
  17220. xmit_que.insert = 0;
  17221. xmit_que.remove = 0;
  17222. xmit_que.empty = true;
  17223.  
  17224. /* init the all other data as needed */
  17225. }
  17226.  
  17227. /*****************************************************
  17228. * NAME: initsystem
  17229. *
  17230. * DESCRIPTION:
  17231. * Init the counter timer chip for continous
  17232. * operation and to interupt at overflow.
  17233. * Init the serial port for receive and send and
  17234. * to generate an interrupt whenever the transmit
  17235. * buffer is empty and the receive buffer is full.
  17236. ****************************************************/
  17237.  
  17238. #define ETOI 0x04
  17239.  
  17240. initsystem()
  17241. {
  17242.  
  17243. /* set up the rate and mode control
  17244.  
  17245. for internal clock and 9600 */
  17246. RMCR = CC0 Â½ SS0;
  17247.  
  17248. /* set up the serial port to transmit and receive */
  17249. TRCSR = normal_mode;
  17250.  
  17251. /* set up the free running counter
  17252. to interupt at overflow */
  17253. TCSR = ETOI;
  17254.  
  17255. /* set up port 1 bits 0-3 as write only
  17256. and bits 4-7 as read only */
  17257. P1DDR = 0x0f;
  17258.  
  17259. /* set up port 2 bit 0 as a write bit
  17260. and turn off the serial transmit buffer */
  17261. P2DDR = 0x01;
  17262. P2DR = 0xFF;
  17263.  
  17264. }
  17265.  
  17266. /***************************************************
  17267. * NAME : set_irq, clear_irq
  17268. *
  17269. * DESCRIPTION: two routines that set the
  17270. * interupt mask and clear the
  17271. * interrupt mask in the condition
  17272. * code register of the 6801
  17273. **************************************************/
  17274.  
  17275. /* routine to set the interupt mask */
  17276.  
  17277. set_irq()
  17278. {
  17279.  
  17280. ;
  17281. #asm
  17282. sei
  17283. #endasm
  17284. }
  17285.  
  17286. /* routine to clear the interupt mask */
  17287.  
  17288. clear_irq()
  17289. {
  17290. ;
  17291. #asm
  17292. cli
  17293. #endasm
  17294. }
  17295.  
  17296.  
  17297.  
  17298.  
  17299.  
  17300.  
  17301.  
  17302.  
  17303.  
  17304.  
  17305.  
  17306.  
  17307.  
  17308.  
  17309.  
  17310.  
  17311.  
  17312.  
  17313.  
  17314.  
  17315.  
  17316.  
  17317.  
  17318.  
  17319.  
  17320.  
  17321.  
  17322.  
  17323.  
  17324.  
  17325.  
  17326.  
  17327. Real-Time Data Acquisition
  17328.  
  17329.  
  17330. David Fugelso and Mike Michnovicz
  17331.  
  17332.  
  17333. David Fugelso has a B.S. in computer science from the University of New Mexico
  17334. and has seven years of experience in software engineering. Mike Michnovicz has
  17335. an M.S. in electrical engineering from the University of New Mexico and has
  17336. seven years of experience in systems integration and embedded systems. The
  17337. authors may be reached at Titan/Spectron P.O. Box 9254, Albuquerque NM,
  17338. 87119-9254 or via e-mail on Compuserve at 72460,741.
  17339.  
  17340.  
  17341. Titan/Spectron has developed a real-time system called DIAL, for DIfferential
  17342. Absorption Lidar. It is used for the remote detection of hydrocarbon gasses
  17343. such as methane, ethane, and propane. The DIAL technique measures the
  17344. atmospheric absorption of a laser beam to sense the presence of gasses. It
  17345. does so by measuring and comparing the return signals of laser pulses at two
  17346. different wavelengths to locate specific gasses. This system makes six lidar
  17347. measurements at a continuous rate of 10Hz. The program collects lidar
  17348. measurements, writes them to disk, performs the DIAL calculations, and
  17349. displays the results. The first two of these functions have real-time
  17350. requirements, but the calculation and display function does not since data is
  17351. saved and can be processed later.
  17352. DIAL is based on a real-time UNIX system that uses System V inter-process
  17353. communication facilities for synchronizing concurrent tasks and sharing data.
  17354. The algorithm has one producer and two consumer tasks. It uses a bounded
  17355. buffer technique implemented with System V message queues and shared memory.
  17356. The system acquires data from six digitizer channels, each sampling at 20
  17357. million samples per second over a period of 20 microseconds. The digitizer has
  17358. a resolution of 12 bits. Two bytes store and transfer each sample, so the host
  17359. computer must read, write, and process 4,800 bytes (400 x 2 x 6) per pulse.
  17360. The 4,800 bytes of data collected from the six lidar measurements is referred
  17361. to as a frame. At 10Hz, the data acquisition system must have a minimum
  17362. continuous throughput of 48 Kbytes/sec.
  17363. The host machine for this system is a 386-based computer with an AT bus
  17364. running LynxOS, a real-time UNIX operating system, from Lynx Real-Time
  17365. Systems. (See the sidebar on real-time operating systems.) The system includes
  17366. a 370Mb hard drive connected to the AT via a SCSI interface. It transfers from
  17367. the digitizer to the host via an IEEE-488 bus (also known as a GPIB). The
  17368. system's IEEE- 488 controller uses direct memory access (DMA) to place data
  17369. into host memory.
  17370.  
  17371.  
  17372. Algorithm Description
  17373.  
  17374.  
  17375. Data acquisition work is split among three tasks: one producer task and two
  17376. consumer tasks. A fourth task, the task synchronization module (TSM),
  17377. synchronizes the activities of the working tasks. The producer task reads data
  17378. over the IEEE-488 from the digitizer. The first consumer task writes data to
  17379. disk; the second performs the DIAL calculation and graphically displays the
  17380. data. The read task is the most critical; if it misses a trigger, digitizer
  17381. data will be lost. The write task is less critical than the read task since
  17382. frame buffers can be written at any time; however, the write task cannot get
  17383. systematically behind. The graphics task, on the other hand, may lag behind
  17384. real-time and is allowed to miss data.
  17385. After receiving an external trigger event, the TSM task initiates the data
  17386. acquisition process by locating an unused buffer and sending the buffer to the
  17387. producer task. The producer task reads the data and sends the buffer to the
  17388. first consumer task, which writes the data to disk and sends the buffer to the
  17389. second consumer task. When that task is finished, it releases the buffer.
  17390. Tasks pass buffers via four System V message queues: the available queue, the
  17391. read queue, the write queue, and the graph queue. The actual buffers are not
  17392. copied among the tasks. Rather, the tasks pass messages containing indexes to
  17393. buffers located in shared memory. The TSM task creates these messages, each
  17394. containing an index or "key" for a buffer, and places them in the proper
  17395. queue. An index can be thought of as a key because each message is unique and
  17396. corresponds to exactly one buffer. This message queue mechanism provides
  17397. mutual exclusion among accesses to the shared memory and also provides task
  17398. synchronization.
  17399.  
  17400.  
  17401. Implementation
  17402.  
  17403.  
  17404. The short program in Listing 1 starts the four data acquisition tasks. In the
  17405. DIAL Lidar program, this task contains the user interface. The listing
  17406. illustrates how the UNIX fork() call starts separate tasks. fork() makes an
  17407. identical copy of the calling program with a new process ID. fork() returns a
  17408. zero to the new task that is the child process, and returns the process ID of
  17409. the child to the parent process.
  17410. Listing 2 is the header file included by the four data acquisition tasks. The
  17411. header file defines a semaphore, message queue, shared memory indexes (keys),
  17412. the message structure, frame buffer size, number of buffers, the priorities of
  17413. the tasks, and other miscellaneous values. The keys for semaphores, message
  17414. queues, and shared memory allow the separate tasks to access the same system
  17415. resources.
  17416. The message structure contains two elements. The operating system requires and
  17417. uses a message type field to differentiate between types of messages. Because
  17418. all of the messages in this program are similar, type is not used. The second
  17419. element is the frame buffer index or key to a contiguous segment of shared
  17420. memory.
  17421. Tasks run at different priorities. The TSM task has the highest priority since
  17422. it must execute when an external event arrives. The read task has the second
  17423. highest priority since the data must be acquired in a specific time frame. The
  17424. write task has the next highest priority since the write to disk is critical
  17425. but finite delays are acceptable. The lowest priority is assigned to the graph
  17426. task. It essentially uses whatever CPU time is left over from the other tasks.
  17427. Listing 3 is the TSM task, lidar_acq.c. As noted earlier, the TSM task
  17428. synchronizes the activities of the other tasks. After setting its own priority
  17429. and performing some initialization, the task creates unique messages for each
  17430. frame buffer and places each message in the available queue.
  17431. The TSM task then waits for the other tasks to finish loading and
  17432. initializing. This is accomplished by three waits for the "alive" semaphore.
  17433. These waits are necessary because the TSM task starts the external data
  17434. acquisition process. If the other tasks are not ready, the acquisition process
  17435. will break down. LynxOS provides an alternative form of the System V
  17436. implementation of the semaphore, which the program uses here. sem_get() works
  17437. similar to the System V semget() but uses a string key instead of an integer
  17438. key. sem_signal() and sem_wait() replace the complicated System V function
  17439. semop().
  17440. Once the other processes have checked in, the TSM task writes to the trigger
  17441. device the number of 400 micro-second cycles between triggers (count) and the
  17442. number of triggers to generate (int_count). The trigger device is a custom
  17443. device with an external clock developed to handle the intricate triggering of
  17444. the lasers. (See the sidebar on developing device drivers for real-time UNIX.)
  17445. The task then waits for the external trigger by reading the trigger "file."
  17446. After receiving a trigger signal, the TSM task looks for a free buffer on the
  17447. available queue through the msgrcv() system call. By making the call with the
  17448. flag IPC_NOWAIT, the program instructs msgrcv() to return immediately whether
  17449. a message is in the queue or not. If a buffer is available, the program places
  17450. it into the read queue. Otherwise, the TSM task must free a buffer from
  17451. another task. Since the calculate and graph process is not a critical part of
  17452. the system, the program places all buffers in the graph message queue also in
  17453. the available queue (using msgrcv() with the IPC_NOWAIT flag in a loop until
  17454. the graph queue is empty). If still no buffers are available, the program has
  17455. encountered a fatal error. (It has missed data.)
  17456. After receiving all external events, the TSM task terminates the data
  17457. acquisition session by placing a quit message on the read queue. The task then
  17458. waits for messages for all of the frame buffers to be placed on the available
  17459. queue, followed by the quit message. If then deletes all the semaphores,
  17460. message queues, and shared memory resources. This allows the work tasks to
  17461. complete any remaining processing.
  17462. Listing 4 presents the producer task, lidar_read.c. This task synchronizes
  17463. activities through the system call msgrcv() acting on the read message queue.
  17464. It sends the parameter NOFLAGS to the routine, causing the task to pause until
  17465. a message is placed in the read queue. Once the system has acquired data and
  17466. placed it in shared memory, the task places a message in the write message
  17467. queue.
  17468. Not shown here is the code that initializes, reads, and closes the IEEE-488
  17469. device. It was omitted partly to save space in this presentation. Digitizer
  17470. device commands also vary among manufacturers. The code is somewhat messy
  17471. because manufacturers support several types of digitizers.
  17472. Listing 5 shows the first consumer task, lidar_write.c. The task's structure
  17473. is similar to that of the read task. The task waits for a message to be placed
  17474. in the write queue. After receiving the message, the task writes a frame
  17475. buffer and places the message in the graph message queue.
  17476. LynxOS provides a system service that allows the use of contiguous disk files.
  17477. Using contiguous disk files speeds up I/O because the disk head does not have
  17478. to move as much. Moreover, the system knows to bypass the disk cache. The
  17479. program creates such a file with the mkcontig() system call, which requires a
  17480. maximum file lenghth parameter. The program then opens the file and access it
  17481. using standard system calls. A contiguous file imposes one additional
  17482. constraint - data must be written in 512-byte chunks. Although each frame
  17483. contains only 4,800 bytes, the program thus writes 5,120 bytes (ten 512-byte
  17484. blocks) to disk. In the actual application, this extra 320 bytes is used to
  17485. store ancillary information about each frame, such as pulse energies and
  17486. absorption coefficients.
  17487. Listing 6 shows the second consumer, lidar_graph.c. Again, the code is
  17488. structured the same as the two preceding tasks. When the task is finished
  17489. processing a buffer, it places the buffer on the available message queue.
  17490. The second consumer performs DIAL calculations. It can display up to six
  17491. graphs using X Window library calls. For brevity, the listing omits the
  17492. calculation and graphics code. In its place is a CPU delay function, which
  17493. gives an example of using UNIX signals.
  17494.  
  17495.  
  17496. Performance
  17497.  
  17498.  
  17499. Running at 10Hz, the system has 100 msec to acquire the data for each pulse.
  17500. Of this, the producer task takes between 20 and 40 msec (depending on the
  17501. digitizer) of real time to transfer 4,800 bytes of data over the IEEE-488 bus.
  17502. This time includes the time it takes the digitizer to acquire a waveform and
  17503. the overhead to initiate the transfer. Since the transfer uses DMA, CPU usage
  17504. is much smaller (less than 10 msec).
  17505. The first consumer task completes the write to disk in 10 msec real time and
  17506. uses less than 0.7 msec of CPU time. (Without the use of contiguous disk
  17507. files, the write time triples.)
  17508. The second consumer task can take between 0 and 200 mseconds depending on
  17509. which graphics option is selected. Some processing options cannot be performed
  17510. in real time. The display will lag behind data acquisition, causing the
  17511. program to periodically dump the graphics message queue, resulting in time
  17512. gaps in the display data. The number of buffers allocated affects both the
  17513. length of the gaps and the time between gaps. Using ten buffers and assuming a
  17514. calculation and display time of 100 mseconds/buffer, you can see that the
  17515. graph queue will be dumped every 5.8 seconds:
  17516. Display Time /
  17517. (Total CPU time/cycle - 0.1 s)
  17518. * Nbuffers * 0.1 s
  17519. When the program dumps the graph queue, the gap length will be 900 msec,
  17520. because the program removes nine frame buffers (Nbuffers - 1) from the graph
  17521. queue.
  17522. The other factors that have an impact system performance are the control
  17523. statements, context switches, and message queue overhead. Of these, only the
  17524. message queue overhead has significant impact. The time to send and receive a
  17525. message is approximately 0.3 msec, so with four transfers per frame, 1.2 msec
  17526. are used for synchronization.
  17527.  
  17528.  
  17529. Conclusion
  17530.  
  17531.  
  17532.  
  17533. I have presented a flexible and easy-to-implement solution for the
  17534. producer/two consumer data acquisition problem. The LynxOS operating system
  17535. provides the real-time environment and the facilities to easily develop
  17536. real-time programs.
  17537. References
  17538. "Real-Time Data Acquisition," Mike Bunnel and Mitch Bunnel, Dr. Dobbs Journal,
  17539. June 1989 pp. 36-44.
  17540. "Unix Interprocess Communication," William J. Freda, The C Users Journal,
  17541. November 1990, Vol. 8 No. 11, pp. 49-61.
  17542. Concurrent Programming, Fundamental Techniques for Real-Time and Parallel
  17543. Software Design, Tom Axford, Wiley & Sons, West Sussex, England.
  17544. What Makes A Real-Time Operating System?
  17545. Two basic areas separate real-time operating systems from non-real time
  17546. operating systems -- task scheduling and fast response to external events. A
  17547. real-time program requires an operating system that guarantees a maximum
  17548. response time to external events. The key to fast servicing of interrupts is
  17549. the pre-emption of currently executing tasks, including those tasks that are
  17550. active within system calls. UNIX allows the system kernel to be interrruped,
  17551. but it cannot be pre-empted while performing a system call. MS-DOS BIOS calls
  17552. can be interrupted, but the system code is not reentrant. (Two concurrent
  17553. tasks cannot make the same system call). Even in real-time systems, response
  17554. time is hard to measure. Instructions vary in execution time and portions of
  17555. the operating system may lock out interrupts during critical sections.
  17556. The other basic difference is task scheduling. UNIX uses a time-sharing task
  17557. scheduler that changes priorities of tasks so that system resources are shared
  17558. fairly among users. A real-time task scheduler does not change task
  17559. priorities. Task priorities are set by the user and remain set, so an infinite
  17560. loop running at higher priority than a shell or login task cannot be
  17561. interrupted and will never end.
  17562. A real-time system also requires a set of system facilities for efficient
  17563. inter-process communication and task control. The operating system must
  17564. provide facilities for creating and deleting tasks, setting priorities,
  17565. synchronizing, and sharing information.
  17566. The DIAL Lidar project uses the LynxOS operating system from Lynx Real-Time
  17567. Systems. The LynxOS system is based on UNIX but was completely rewritten to
  17568. support real-time programming. The time-sharing task scheduler was replaced
  17569. with a real-time scheduler, and the kernel was rewritten to allow pre-emption
  17570. and to be reentrant. System V communication facilities provide inter-process
  17571. communication, including semaphores, shared memory, message queues, signals,
  17572. and pipes.
  17573. Writing Device Drivers In LynxOS
  17574. Device drivers carry enough mystique that some programmers shy away from the
  17575. subject. But with LynxOS, both beginners and experts can write device drivers.
  17576. LynxOS makes installation especially easy -- programmers can install drivers
  17577. into the system without rebuilding the system kernel, which in turn allows
  17578. online debugging.
  17579. One of the DIAL Lidar project goals was to minimize development time by
  17580. choosing a system that included all of the necessary device drivers. LynxOS
  17581. was chosen partially because it satisfied that requirement. However, as the
  17582. project progressed, the need to control a custom peripheral device presented
  17583. an opportunity to build a custom device driver. We needed to design and build
  17584. a digital circuit that could produce the timing signals necessary to operate a
  17585. Neodymium YAG laser externally and to trigger a digitizing oscilloscope. The
  17586. circuit needed to be programmable with respect both to delays between the
  17587. various laser control pulses and to the repetition rate of the laser.
  17588. We chose to base the circuit on the familiar Intel 8253 timer chip, using only
  17589. one of the 8253's three times. Its output, the "master" timing pulse, is
  17590. routed to the custom digital circuitry that generates the various timing
  17591. pulses for the laser and the digitizer. It is also connected to one of the bus
  17592. interrupt lines. The master timing pulse triggers a chain of events external
  17593. to the system computer. It also initiates the processing needed to capture and
  17594. process the lidar return data.
  17595. The structure of this simple device driver resembles that of a standard UNIX
  17596. driver. It has a top half consisting of the standard open, close, read, write,
  17597. and ioctl calls. By using conventional UNIX I/O system calls, you can
  17598. interface with the timing circuit hardware. The driver also has a bottom half
  17599. that quickly responds to an interrupt from the timer. The sole function of the
  17600. handler is the generation of a signal used to synchronize the lidar program's
  17601. multiple tasks. In this driver, the coupling between the driver's top and
  17602. bottom halves is very loose. No data needs to be input or output within the
  17603. interrupt handler, nor does the top half of the driver process any input or
  17604. output. It merely reads and writes the 8253's status and control registers.
  17605. Testing the card and driver software was also easy. We connected an
  17606. oscilloscope to the timer output, to verify that we could control the
  17607. operation of the 8253 using the appropriate driver calls. We checked the
  17608. interrupt handler by starting up a task that writes to the screen after
  17609. receiving a signal, and then signaling the task from the handler.
  17610.  
  17611. Listing 1 (start.c)
  17612. Listing 1 (start.c)
  17613. /* ---
  17614. Simple routine to start all the processes
  17615. for the lidar program. The program starts
  17616. each routine with a fork call, and then
  17617. terminates.
  17618. --- */
  17619.  
  17620. #include <stdio.h>
  17621.  
  17622. /* --- process names --- */
  17623. static char *tasks[] = { "lidar_acq",
  17624. "lidar_graph", "lidar_write",
  17625. "lidar_read", NULL};
  17626. main ()
  17627. {
  17628. int i = 0;
  17629. while (tasks[i] != NULL) {
  17630. if (fork()) /* child, start task */
  17631. execve (tasks[i], NULL, NULL);
  17632. i++;
  17633. }
  17634. }
  17635.  
  17636.  
  17637. Listing 2 (lidar.h)
  17638. Listing 2 (lidar.h)
  17639. /* ---
  17640. Header for lidar tasks. This header includes
  17641. the IDs for the message queues and shared memory,
  17642. shared structures, and shared definitions.
  17643. --- */
  17644.  
  17645. #include <file.h>
  17646. #include <time.h>
  17647. #include <types.h>
  17648. #include <stat.h>
  17649. #include <resource.h>
  17650. #include <sys/ipc.h>
  17651.  
  17652. #include <sys/msg.h>
  17653. #include <sys/sem.h>
  17654. #include <sys/shm.h>
  17655. #include <sys/oscalls.h>
  17656. #include <tclock.h>
  17657.  
  17658. /* --- queue keys --- */
  17659. #define AVAILABLE_Q 1
  17660. #define GRAPHICS_Q 2
  17661. #define READ_Q 3
  17662. #define WRITE_Q 4
  17663. /* --- shared memory key --- */
  17664. #define FB_KEY 10
  17665.  
  17666. /* --- semaphore keys --- */
  17667. #define ALIVE_KEY "alive"
  17668.  
  17669. /* --- message structure containing frame buffer
  17670. index (key) for shared memory access --- */
  17671. typedef struct message_rec {
  17672. long type; /* required */
  17673. int key; /* index into shared memory */
  17674. } message;
  17675.  
  17676. /* -- defines for messages -- */
  17677. #define MSG_SIZE 4
  17678.  
  17679. /* --- frame buffer number and size --- */
  17680. #define N_FRAMES 10
  17681. #define FRAME_SIZE 2560
  17682. #define WRITE_SIZE 2560
  17683. #define READ_SIZE 2070
  17684.  
  17685. /* --- number of runs to make for sample program --- */
  17686. #define N_RUNS 100
  17687.  
  17688. /* --- priorities for each process --- */
  17689. #define LIDAR_ACQ 19
  17690. #define LIDAR_READ 18
  17691. #define LIDAR_WRITE 17
  17692. #define LIDAR_GRAPH 16
  17693.  
  17694. /* --- useful defines --- */
  17695. #define TRUE 1
  17696. #define FALSE 0
  17697. #define QUIT -1
  17698. #define NOFLAGS 0
  17699. #define ANYTYPE 0
  17700.  
  17701.  
  17702. Listing 3 (lidar_acq.c)
  17703. /* ---
  17704. lidar_acq task. This is the task synchronization task.
  17705. This program initialiazes shared memory and the message
  17706. queues for the real-time portion of the lidar program.
  17707. --- */
  17708.  
  17709. #include "lidar.h"
  17710.  
  17711.  
  17712. main()
  17713. {
  17714. int i, j, trigger_fd, alive_sem, shm_seg_id;
  17715. int avail_qid, graph_qid, read_qid, write_qid;
  17716. message mptr;
  17717. trigger_clock cycles;
  17718.  
  17719. /* --- set routine priority --- */
  17720. setpriority (PRIO_PROCESS, 0, LIDAR_ACQ);
  17721.  
  17722. /* --- open task alive semaphore --- */
  17723. alive_sem = sem_get(ALIVE_KEY, 0);
  17724.  
  17725. /* --- open message queues --- */
  17726. avail_qid = msgget (AVAILABLE_Q, 0666 IPC_CREAT);
  17727. graph_qid = msgget (GRAPHICS_Q, 0666 IPC CREAT);
  17728. read_qid = msgget (READ_Q, 0666 IPC_CREAT);
  17729. write_qid = msgget (WRITE_Q, 0666 IPC_CREAT);
  17730.  
  17731. /* --- create shared memory segment ids --- */
  17732. shm_seg_id = shmget (FB_KEY, FRAME_SIZE * N_FRAMES,
  17733. 0666 IPC_CREAT);
  17734.  
  17735. /* --- create messages for shared memory
  17736. indexing and place them in the available
  17737. message queue --- */
  17738. for (i = 0; i < N_FRAMES; i++) {
  17739. mptr.key = i;
  17740. if (msgsnd (avail_qid, &mptr, MSG_SIZE, 0) < 0) {
  17741. printf ("Queue problem.\n");
  17742. exit (1);
  17743. }
  17744. }
  17745.  
  17746. /* --- wait for the read, write, and graph
  17747. tasks to check in --- */
  17748. sem_wait (alive_sem);
  17749. sem_wait (alive_sem);
  17750. sem_wait (alive_sem);
  17751.  
  17752. /* --- open trigger timing device and set to
  17753. 10 Hz for N_RUNS pulses --- */
  17754. trigger_fd = open("/dev/tclock", O_RDWR);
  17755. cycles.count = 250; /* 250 cycles = .1 sec */
  17756. cycles.int_count = N_RUNS; /* number of pulses */
  17757. i = write (trigger_fd, &cycles, 4);
  17758.  
  17759. /* --- real time data acquisition loop --- */
  17760. for (i = 0; i < N_RUNS; i++) {
  17761. /* --- wait for trigger --- */
  17762. if (read (trigger_fd, &cycles, 2)) {
  17763. printf ("Missed a pulse. Fatal Error\n");
  17764. break;
  17765. }
  17766.  
  17767. /* --- get a free frame buffer --- */
  17768. j = msgrcv (avail_qid, &mptr, MSG_SIZE, 0, IPC_NOWAIT);
  17769.  
  17770. /* --- if there aren't any available buffers --- */
  17771.  
  17772. if (j < 0) {
  17773. /* --- remove all of the frame buffers
  17774. from the graph message queue and
  17775. place them in the available queue --- */
  17776. while (msgrcv(graph_qid, &mptr, MSG_SIZE,
  17777. ANYTYPE, IPC_NOWAIT) >= 0)
  17778. msgsnd (avail_qid, &mptr, MSG_SIZE, 0);
  17779. /* --- if there still are no buffers,
  17780. fatal error has occured --- */
  17781. if (msgrcv (avail_qid, &mptr, MSG_SIZE,
  17782. ANYTYPE, IPC_NOWAIT) == -1) {
  17783. printf ("Fatal queue problem. Terminating\n");
  17784. break;
  17785. }
  17786. }
  17787.  
  17788. /* --- start the acquisition process --- */
  17789. msgsnd (read_qid, &mptr, MSG_SIZE, IPC_NOWAIT);
  17790. }
  17791.  
  17792. /* --- terminate data acquisition --- */
  17793. close(trigger_fd);
  17794.  
  17795. /* --- signal other processes to quit --- */
  17796. mptr.key = QUIT;
  17797. msgsnd (read_qid, &mptr, MSG_SIZE, NOFLAGS);
  17798.  
  17799. /* --- wait for all messages to be placed
  17800. on available Q and retire --- */
  17801. for (i = 0; i < N_FRAMES; i++)
  17802. msgrcv(avail_qid, &mptr, MSG_SIZE, ANYTYPE, NOFLAGS);
  17803.  
  17804. /* --- and the terminate message --- */
  17805. msgrcv (avail_qid, &mptr, MSG_SIZE, ANYTYPE, NOFLAGS);
  17806.  
  17807. /* --- delete queues --- */
  17808. msgctl (avail_qid, IPC_RMID, 0);
  17809. msgctl (read_qid, IPC_RMID, 0);
  17810. msgctl (write_qid, IPC_RMID, 0);
  17811. msgctl (graph_qid, IPC_RMID, 0);
  17812.  
  17813. /* --- release semaphore --- */
  17814. sem_delete (ALIVE_KEY);
  17815.  
  17816. /* --- delete shared memory segments --- */
  17817. shmctl (shm_seg_id, IPC_RMID, 0);
  17818. }
  17819.  
  17820.  
  17821. Listing 4 (lidar_read.c)
  17822. /* ---
  17823. lidar_read task. This task initializes shared
  17824. memory, waits for a message, reads a frame
  17825. buffer from the digitizer, and sends a message
  17826. to the write task when the frame buffer is full.
  17827. --- */
  17828.  
  17829. #include "lidar.h"
  17830.  
  17831.  
  17832. main()
  17833. {
  17834. int ieee_488;
  17835. int read_qid, write_qid;
  17836. int alive_sem;
  17837. int terminate = FALSE;
  17838. char *fb_shm;
  17839. message mptr;
  17840.  
  17841. /* --- set process prioprity --- */
  17842. setpriority (PRIO_PROCESS, 0, LIDAR_READ);
  17843.  
  17844. /* --- open message queues --- */
  17845. read_qid = msgget (READ_Q, 0666 IPC_CREAT);
  17846. write_qid = msgget (WRITE_Q, 0666 IPC_CREAT);
  17847.  
  17848. /* --- get semaphore --- */
  17849. alive_sem = sem_get (ALIVE_KEY, 0);
  17850.  
  17851. /* --- create shared memory frame buffers --- */
  17852. fb_shm = shmat(shmget (FB_KEY, FRAME_SIZE*N_FRAMES,
  17853. 0666IPC_CREAT), 0, 0);
  17854.  
  17855. /* --- open ieee file and set up digitizer --- */
  17856. ieee_488 = init_digitizer ();
  17857.  
  17858. /* --- tell the world we're open for business --- */
  17859. sem_signal (alive_sem);
  17860.  
  17861. /* --- while lidar data acquisition program running --- */
  17862. while (!terminate) {
  17863. /* --- wait for a message to start read --- */
  17864. msgrcv (read_qid, &mptr, MSG_SIZE, ANYTYPE, NOFLAGS);
  17865.  
  17866. /* --- check for quit --- */
  17867. if (mptr.key == QUIT)
  17868. terminate = 1;
  17869. else
  17870. read_digitizer (ieee_488, fb_shm +
  17871. mptr.key * FRAME_SIZE);
  17872.  
  17873. /* --- send the message on to the write task --- */
  17874. msgsnd (write_qid, &mptr, MSG_SIZE, IPC_NOWAIT);
  17875. }
  17876.  
  17877. /* --- close digitizer file --- */
  17878. close_digitizer (ieee_488);
  17879.  
  17880. /* --- release memory --- */
  17881. shmdt (fb_shm);
  17882. }
  17883.  
  17884.  
  17885. Listing 5 (lidar_write.c)
  17886. /* ---
  17887. lidar_write task. This task waits for a
  17888. message from the read routing. When a message
  17889. arrives, a frame buffer is written to disk.
  17890. After the write, the message is enqueued
  17891.  
  17892. onto the graph queue.
  17893. --- */
  17894.  
  17895. #include "lidar.h"
  17896.  
  17897. main()
  17898. {
  17899. int raw_fd, alive_sem;
  17900. int terminate = 0;
  17901. int graph_qid, write_qid;
  17902. char *fb_shm;
  17903. message mptr;
  17904.  
  17905. /* --- set process priority --- */
  17906. setpriority (PRIO_PROCESS, 0, LIDAR_WRITE);
  17907.  
  17908. /* --- open message queues --- */
  17909. graph_qid = msgget (GRAPHICS_Q, 0666 IPC_CREAT);
  17910. write_qid = msgget (WRITE_Q, 0666 IPC_CREAT);
  17911.  
  17912. /* --- get alive semaphore --- */
  17913. alive_sem = sem_get (ALIVE_KEY, 0);
  17914.  
  17915. /* -- create shared memory buffer -- */
  17916. fb_shm = shmat (shmget (FB_KEY, FRAME_SIZE * N_FRAMES,
  17917. 0666 IPC_CREAT), 0, 0);
  17918.  
  17919. /* --- create a contiguous file --- */
  17920. mkcontig ("test.out", S_IWRITE, (long)N_RUNS *
  17921. (long)FRAME_SIZE);
  17922.  
  17923. /* --- open raw, write file --- */
  17924. if ((raw_fd = open("test.out", O_WRONLYO_CREAT)) == -1) {
  17925. printf("Unable to open raw file: lidar_write.\n");
  17926. exit (1);
  17927. }
  17928.  
  17929. /* --- tell the world we're open for business --- */
  17930. sem_signal (alive_sem);
  17931.  
  17932. /* --- main write loop --- */
  17933. while (!terminate) {
  17934. /* --- wait for a buffer ---- */
  17935. msgrcv (write_qid, &mptr, MSG_SIZE, ANYTYPE, NOFLAGS);
  17936.  
  17937. /* --- check for terminate --- */
  17938. if (mptr.key == QUIT)
  17939. terminate = 1;
  17940. else
  17941. write (raw_fd, fb_shm + mptr.key * FRAME_SIZE,
  17942. WRITE_SIZE);
  17943.  
  17944. /* --- pass frame buffer to graphics/processing
  17945. task --- */
  17946. msgsnd (graph_qid, &mptr, MSG_SIZE, IPC_NOWAIT);
  17947. }
  17948.  
  17949. /* -- release memory -- */
  17950. shmdt (fb_shm);
  17951.  
  17952.  
  17953. /* --- close device --- */
  17954. close (raw_fd);
  17955. }
  17956.  
  17957. Listing 6 (lidar_graph.c)
  17958. /* ---
  17959. lidar_graph task. This program processes
  17960. the raw frame buffer. Frame buffers are received
  17961. through the graph q, when processing is finished,
  17962. the frame buffer is placed on the available queue.
  17963.  
  17964. For the sample program, processing and graphing
  17965. are replaced by a call to delay which uses
  17966. DELAY_TIME cpu time.
  17967. --- */
  17968.  
  17969. #include <stdio.h>
  17970. #include <signal.h>
  17971. #include "lidar.h"
  17972.  
  17973. #define DELAY_TIME 0.3
  17974.  
  17975. main ()
  17976. {
  17977. int terminate = 0;
  17978. int graph_qid, avail_qid;
  17979. char *fb_shm;
  17980. int alive_sem;
  17981. message mptr;
  17982.  
  17983. /* --- set process priority --- */
  17984. setpriority (PRIO_PROCESS, 0, LIDAR_GRAPH);
  17985.  
  17986. /* --- open message queues --- */
  17987. graph_qid = msgget (GRAPHICS_Q, 0666 IPC_CREAT);
  17988. avail_qid = msgget (AVAILABLE_Q, 0666 IPC_CREAT);
  17989.  
  17990. /* --- create alive semaphore --- */
  17991. alive_sem = sem_get (ALIVE_KEY, 0);
  17992.  
  17993. /* -- create shared memory buffers -- */
  17994. fb_shm = shmat (shmget (FB_KEY, FRAME_SIZE * N_FRAMES,
  17995. 0666 IPC_CREAT), 0, 0);
  17996.  
  17997. /* --- tell the world we're open for business --- */
  17998. sem_signal (alive_sem);
  17999.  
  18000. /* --- while lidar program running --- */
  18001. while (!terminate) {
  18002. /* --- wait for the next buffer --- */
  18003. msgrcv (graph_qid, &mptr, MSG_SIZE, ANYTYPE, NOFLAGS);
  18004. /* --- check for termination --- */
  18005. if (mptr.key == QUIT)
  18006. terminate =1;
  18007. else
  18008. delay (DELAY_TIME);
  18009.  
  18010. /* --- place buffer on the availaible queue --- */
  18011.  
  18012. msgsnd (avail_qid, &mptr, MSG_SIZE, IPC_NOWAIT);
  18013. }
  18014.  
  18015. /* -- release memory -- */
  18016. shmdt (fb_shm);
  18017. }
  18018.  
  18019. int cpu_hog_func ();
  18020. int take_cpu_time = 1;
  18021. #define SETTIMER(t,p) \
  18022. t.it_value.tv_sec = (int)delta; \
  18023. t.it_value.tv_usec = (p - (int)p) * 1000000; \
  18024. setitimer (ITIMER_PROF, &t, 0);
  18025.  
  18026. /* --- delay and grab cpu time --- */
  18027. delay (delta)
  18028. double delta;
  18029. {
  18030. struct itimerval t;
  18031.  
  18032. /* --- signal handler for profiler interrupt --- */
  18033. signal (SIGPROF, cpu_hog_func);
  18034.  
  18035. /* --- turn on timer --- */
  18036. SETTIMER(t,delta);
  18037.  
  18038. /* --- loop until stop condition --- */
  18039. while (take_cpu_time)
  18040. /* do nothing */;
  18041.  
  18042. /* --- reset loop condition --- */
  18043. take_cpu_time++;
  18044.  
  18045. /* --- turn off timer --- */
  18046. SETTIMER(t,0);
  18047. }
  18048.  
  18049. cpu_hog_func ()
  18050. {
  18051. take_cpu_time--;
  18052. }
  18053.  
  18054.  
  18055.  
  18056.  
  18057.  
  18058.  
  18059.  
  18060.  
  18061.  
  18062.  
  18063.  
  18064.  
  18065.  
  18066.  
  18067.  
  18068.  
  18069.  
  18070.  
  18071.  
  18072.  
  18073.  
  18074.  
  18075. Portable C For The 8051 Microcontroller
  18076.  
  18077.  
  18078. Frank van den Berg
  18079.  
  18080.  
  18081. This article is not available in electronic form.
  18082.  
  18083.  
  18084.  
  18085.  
  18086.  
  18087.  
  18088.  
  18089.  
  18090.  
  18091.  
  18092.  
  18093.  
  18094.  
  18095.  
  18096.  
  18097.  
  18098.  
  18099.  
  18100.  
  18101.  
  18102.  
  18103.  
  18104.  
  18105.  
  18106.  
  18107.  
  18108.  
  18109.  
  18110.  
  18111.  
  18112.  
  18113.  
  18114.  
  18115.  
  18116.  
  18117.  
  18118.  
  18119.  
  18120.  
  18121.  
  18122.  
  18123.  
  18124.  
  18125.  
  18126.  
  18127.  
  18128.  
  18129.  
  18130.  
  18131.  
  18132.  
  18133.  
  18134.  
  18135.  
  18136. Building Embedded Systems With Turbo C
  18137.  
  18138.  
  18139. Pat Villani
  18140.  
  18141.  
  18142. Pat Villani received his B.S.E.E. from Polytechnic Institute of Brooklyn and
  18143. his M.S.E.E. from Polytechnic Institute of New York. Pat has developed
  18144. applications ranging from avionics and guidance to computer peripherals. He is
  18145. president of Network Software Systems, Ltd., which specializes in C
  18146. applications and embedded control systems. Readers may reach him at (908)
  18147. 206-0320.
  18148.  
  18149.  
  18150. Embedded systems have traditionally represented a specialized area of computer
  18151. science and electrical engineering, an application where both disciplines
  18152. merge. System design often requires tradeoffs between programming convenience
  18153. and system cost. Quite often, code produced for embedded systems is created by
  18154. both software and hardware engineers.
  18155. The first microprocessors used in embedded systems implemented a lot of code
  18156. in assembly langauage because of the type of software development packages
  18157. available for these processors. These packages were often expensive and
  18158. required the manufacturer's development system or a large machine to build
  18159. code. Only large corporations could afford these programming platforms. As
  18160. microprocessor capabilities increased, manufacturers introduced programming
  18161. langauges geared toward embedded systems. These new languages were limited to
  18162. the same platforms, so the trend of limited embedded system development
  18163. continued.
  18164. Concurrently, great strides were made in desktop computers. These computers,
  18165. based on the same microprocessors that were used in embedded systems, were
  18166. capable of running applications and small software development packages that
  18167. targeted these machines. Further developments in desktop computers enabled the
  18168. developers of these packages to include more advanced features. The result:
  18169. powerful langauges such as the Borland Turbo languages and Microsoft languages
  18170. that include debuggers and integrated development environments that allow
  18171. rapid development of applications.
  18172. Today, low-cost clone motherboards, hybrids, and single chip computers based
  18173. on XT and AT architectures exist. When combined with low-cost peripherals,
  18174. these motherboards and devices present a unique opportunity to system
  18175. developers. Low-cost and small-volume controllers, data loggers, and
  18176. specialized processors are feasible. Adapting langauges such as Turbo C to
  18177. produce code for these environments makes these designs even more affordable.
  18178.  
  18179.  
  18180. Basic Principles
  18181.  
  18182.  
  18183. Compilers such as Borland Turbo C generate efficient code that is independent
  18184. of any operating system. What ties the compiler to an operating system or any
  18185. environment is the system startup code and the libraries that are linked to
  18186. produce the executable object file. Replacing these files with new files
  18187. specific to embedded systems allows the code to work in an embedded system.
  18188. The first file you must replace is the startup file. Turbo-C uses one of the
  18189. files COx. OBJ (where x is T, S, L, etc., depending on the memory model used)
  18190. found in \TC\LIB. This file transforms the unique parameters of MS-DOS passed
  18191. at startup into the conventional C environment argc, argv, envp, and errno.
  18192. The file also sets up the stacks and aligns the segments of memory.
  18193. The next file that you must replace is the corresponding flavor of
  18194. \TC\LIB\Cx.LIB. This file contains all the operating system calls, such as
  18195. read and write. It also contains library functions such as strcpy and tolower.
  18196. Our approach is to replace this file with a new library emulating these calls.
  18197.  
  18198.  
  18199. Design Advantages
  18200.  
  18201.  
  18202. Rapid development is a key factor in today's market. This approach helps the
  18203. system developer achieve this goal by reducing development time. The project
  18204. is developed using the Turbo C integrated environment or Turbo Debugger. When
  18205. the developer is satisfied with the code's functionality, the code is
  18206. recompiled and linked using make files and the command line environment. A
  18207. commercial or public domain locater is then used to create an EPROM for the
  18208. target system.
  18209. The project is quickly debugged in a convenient environment in which code
  18210. changes are rapidly made and revision control is maintained. Using the Turbo
  18211. Debugger, nearly all C code and assembly code can be debugged, registers
  18212. examined, breakpoints set, etc., without the need for a costly in-circuit
  18213. emulator. If a suitable ROM monitor is used during the final debug cycle, an
  18214. in-circuit emulator may not be needed at all.
  18215. Since the code is transported from an MS-DOS environment to a target system,
  18216. the functional code can be easily transported to a different 80x86-based
  18217. target system by simply changing the device drivers and library I/O files.
  18218. This built-in hardware independence aids the developer in writing reusable
  18219. code, further reducing development time in future projects.
  18220.  
  18221.  
  18222. Example Project
  18223.  
  18224.  
  18225. The development of a simple 8086 ROM monitor illustrates this design approach
  18226. (Listing 1). The monitor, called mon86, performs two functions:
  18227. examine and change memory
  18228. go to specified address
  18229. It uses a simple table-driven executive that can be extended by adding new
  18230. table entries and suitable functions to execute these new commands. An error
  18231. function terminates the table.
  18232. The monitor uses only Turbo C calls that comply with both SVID and POSIX
  18233. definitions, improving portability. DOS-unique system calls, such as INT21,
  18234. etc., are not used. Using them would complicate the code by requiring the use
  18235. of conditional preprocessor directives to selectively include the non-portable
  18236. sections of code. It also complicates library coding, since it requires that
  18237. DOS code also be emulated.
  18238. For this project, the C library is broken into two libraries: a system call
  18239. emulation and a portable C library. The example project uses simple I/O calls
  18240. such as read and write. The functions open, close, and ioctl are also
  18241. implemented for completeness. Restricting I/O function calls to this small set
  18242. reduces the work required to implement the emulation package.
  18243. The monitor C library calls are limited to simple getc and putc. These calls
  18244. are implemented as links to Turbo C calls _fputc and _fgetc, which allow for
  18245. full use of Turbo C's <stdio.h>, further enhancing portability. Also, simple
  18246. formatted output is required for memory display. To achieve this with an eye
  18247. on portability, the library provides an integer-only printf. The choice of a
  18248. limited printf has two justifications:
  18249. it is simpler to implement, because it does not require knowledge of compiler
  18250. floating point representation
  18251. limiting its use to int and long guarantees that the floating-point library is
  18252. not used, yielding smaller ROM code.
  18253. A machine library, called libm. lib, is also needed. This library is a
  18254. collection of all subroutines required to supplment C operations not supported
  18255. by the processor itself, such as long divides, multiplies, etc. These
  18256. subroutines are naturally operating system independent. It is best to extract
  18257. them from the Turbo C library as needed during the ROM build portion of the
  18258. project. A limited library is built at that time by linking the final MS-DOS
  18259. code, noting the routines used in the code map, and extracting and creating a
  18260. new library of just those routines.
  18261.  
  18262.  
  18263. Startup Assembly Code
  18264.  
  18265.  
  18266. Probably the most important piece of code for an embedded system is the system
  18267. startup code (Listing 2). This code will vary from compiler to compiler, but
  18268. the same operations generally appear in some form. The first part of the
  18269. module is usually initialization related to the assembler or compiler. For
  18270. Turbo C, this is the specification of segments. It is here that the order of
  18271. segments for the module is first specified. Turbo C requires three segments,
  18272. MS-DOS requires one.
  18273. MS-DOS requires a stack segment. In a .EXE file, this is the only segment
  18274. specified in the header that is separate from the rest. That is why the stack
  18275. segment is used by some locate utilities as a place to separate RAM code from
  18276. ROM code when generating hex files suitable for EPROM programmers.
  18277. Turbo C requires only three segments, _TEXT, _DATA, and _BSS. These closely
  18278. follow the UNIX C model. The mon86 generated here is a small version,
  18279. therefore, the CODE segment is text only, and the DATA segment is used for
  18280. data, bss (data initialized to zeros), and stack. These segments are mapped
  18281. into DGROUP.
  18282. All 80x86 processors begin executing code after reset at address
  18283. 0xffff:0x0000. The usual procedure is to place a far jump at this location to
  18284. the ROM entry address. For our example, the system entry point is defined as
  18285. the far procedure entry. This label is used as the startup address supplied to
  18286. the locater.
  18287. Next, the processor is initialized. Initializing the processor means
  18288. intializing any segment registers that the processor needs for proper
  18289. operation. Since the module presented here is for an 8086 or 8088 machine,
  18290. only the segment registers must be initialized. Note that segment register
  18291. initialization is performed with all interrupts disabled, which prevents
  18292. spurious interrupts that may occur during power-up from crashing the system.
  18293. Once the processor is initialized, it usually requires the proper setting of
  18294. the stack pointer. For this example, all you must do is initialize the sp and
  18295. bp registers. Note that initializing the bp register is required by Turbo C
  18296. code generation conventions, not by the 8086.
  18297. Special hardware initialization should be performed after the processor has
  18298. been initialized. This includes initializing the interrupt vector table,
  18299. moving the data segment to RAM, and initializing the _BSS segment to all zeros
  18300. to match C programming conventions. The example in Listing 2 includes a hook
  18301. for hardware initialization with a call to __hdw_init. (For clarity, I've
  18302. omitted moving data and initializing vectors.)
  18303. After system initialization, you must enter main so the application can run.
  18304. You enter main by building the stack frame containing the arguments envp,
  18305. argv, and argc. You use these arguments to pass system configuration
  18306. information, dip switch settings, etc. to your C code. These arguments also
  18307. serve as a convenient debugging tool, since you can test different
  18308. configuration switch positions by passing a command line argument during the
  18309. debugging phase.
  18310.  
  18311. One critical piece of code that all systems require is the function exit. This
  18312. code is very implementation sensitive. You may want to light LEDs and sound
  18313. alarms. In certain cases, you may want only to display a message and restart.
  18314. What to do when the application terminates must be decided during system
  18315. design. The startup code also contains a call to exit just after the call to
  18316. main, which allows main to return to its caller if an error occurs.
  18317. One final small but necessary bit of code is errno. errno is the location
  18318. where C usually stores an integer value to signify the error condition that
  18319. occurred during a library or system call. Although using errno this way is not
  18320. necessary in all embedded systems, it is safer to follow convention than risk
  18321. an unkown subroutine yielding a link error. Your system call emulation will
  18322. also make use of this variable, allowing for a closer emulation.
  18323.  
  18324.  
  18325. Operating System Emulation
  18326.  
  18327.  
  18328. The design example uses a single file for operating system emulation (Listing
  18329. 3). This code closely emulates the standard C equivalents. Following this
  18330. practice minimizes ROM debugging.
  18331. A small data structure, fd, tracks file status. The only parameter set by open
  18332. and close are file open or close status. This structure can be extended to
  18333. satisfy system requirements, but should be kept as simple as possible to
  18334. assure ease of debugging. The open call checks for only a single possible file
  18335. to be opened. /der/con (the control console), stdin, stdout, and stderr are
  18336. also hard-wired as read-only or write-only, as appropriate. In some systems,
  18337. open and close will link to the I/O drivers to perform driver initialization.
  18338. The most used portion of the emulation code is the functions ioctl, read, and
  18339. write. The function ioctl is most often used for setting baud rates, screen
  18340. modes, etc. It is shown here for completeness. The write function simply links
  18341. to the driver call _cout (Listing 4). It maps newlines into carriage return
  18342. and line feed pairs. The read function buffers input. It terminates each
  18343. request on receipt of a newline. Any necessary processing to put text in
  18344. canonical C form should be performed here.
  18345.  
  18346.  
  18347. Portable Library Functions
  18348.  
  18349.  
  18350. In order to simplify debugging, standard I/O calls should be emulated. This
  18351. example uses getc, puts, and printf. The functions here must closely emulate
  18352. their respective MS-DOS counterparts, preventing unexpected surprises in the
  18353. target system.
  18354. Examining Turbo C's <stdio.h> file reveals that getc is a macro that invokes
  18355. fgetc (as is typical in many systems). However, _fgetc calls DOS through
  18356. another function, _fgetc. Therefore, your code closely follows this by mapping
  18357. _fgetc and _fputc to return the correct values for success and error (Listing
  18358. 5). A simple puts is implemented in a portable fashion through calls to
  18359. putchar (Listing 6).
  18360. This application uses the abbreviated version of printf described earlier
  18361. (Listing 7). This function only calls write, which minimizes unexpected
  18362. dependencies on other library functions. Guaranteeing that floating point is
  18363. not used helps in not wasting ROM space, usually a premium when system cost is
  18364. considered. In many embedded applications, the only requirements for floating
  18365. point support result from printf's floating point display options.
  18366.  
  18367.  
  18368. Programming And Debugging
  18369.  
  18370.  
  18371. Turbo C's greatest asset is its integrated programming environment. A bug can
  18372. be quickly discovered and the code can be quickly recompiled from the same
  18373. environment. Typically, it is in this environment that the first operational
  18374. code is developed. (An interesting note: the new environment for Turbo C++
  18375. includes a register display window that works well with files assembled using
  18376. the masm debugging switch). This phase should be used for all logic testing
  18377. and customer demonstrations, since it is the most flexible of the development
  18378. phases.
  18379. Once the main logic has been debugged, the code should be linked with the
  18380. portable libraries and tested. Listing 8 shows an example make file. Both DOS
  18381. and ROM version targets should be defined, so that any changes that may be
  18382. necessary to the main logic resulting from target system testing can be
  18383. incorporated into the DOS version and retested in the integrated environment.
  18384. The make file presented here also contains two additional targets, ex0 and
  18385. ex0r. These targets are typically limited versions that test the device
  18386. drivers. Quite often, these versions provide repeating patterns and echo
  18387. routines for testing peripherals. Upon satisfactory completion of testing, the
  18388. resulting code is burned into a final EPROM and delivered.
  18389.  
  18390.  
  18391. Conclusions
  18392.  
  18393.  
  18394. This example provides a good start for an embedded system design. Although the
  18395. XT and AT architecture is used for this example, the techniques presented here
  18396. are not limited to them. These techniques have been used for new designs in
  18397. projects ranging from simple computer peripherals, such as printers and tape
  18398. transports, to specialized sonar signal analysis hardware. They provide a
  18399. broad approach that can be applied to both native and cross compilations,
  18400. substituting cross compilers and cross compilation targets in the make file.
  18401. Of course, an embedded system need not include support for printf, read, or
  18402. write. An application can instead make simple calls directly to the device
  18403. driver, which results in much smaller code. However, since many tradeoffs are
  18404. considered during the system design phase, more often than not the extra
  18405. functions are worth providing.
  18406.  
  18407. Listing 1
  18408. /*************************************
  18409. * mon86.c
  18410. * Simple 8086 rom based monitor.
  18411. **************************************/
  18412.  
  18413. #include <stdio.h>
  18414. #include <dos.h>
  18415.  
  18416. void debug();
  18417. int smatch();
  18418.  
  18419. struct table
  18420. {
  18421. char *t_name; /* the name for this entry */
  18422. int (*t_fn)(); /* the function to execute */
  18423. };
  18424.  
  18425. int mem(), go(), error();
  18426.  
  18427. struct table cmd[]=
  18428. {
  18429. "mem", mem,
  18430. "go", go,
  18431. "", error
  18432. };
  18433.  
  18434.  
  18435.  
  18436. void main(argc, argv)
  18437. int argc;
  18438. char *argv[];
  18439. {
  18440. if(argc != 2)
  18441. {
  18442. printf("%s: incorrect argument count\n",
  18443. argv [0] );
  18444. exit(1);
  18445. }
  18446. else
  18447. printf("mon86 - demo 8086 monitor\n%s
  18448. version\n", argv[1] );
  18449. debug ();
  18450. }
  18451.  
  18452. void debug()
  18453. {
  18454. char line[BUFSIZ];
  18455. char key[32];
  18456. char *lp, *p;
  18457. struct table *tp;
  18458. char *skipwh();
  18459.  
  18460. for(;;)
  18461. {
  18462. printf("/nmon86:");
  18463. gets(line);
  18464. lp = skipwh(line);
  18465. if(*lp == NULL)
  18466. continue;
  18467. p = key;
  18468. while(*lp != NULL && !iswhite(*lp))
  18469. *ptt = *lp++;
  18470. *p = NULL;
  18471. for(tp = cmd; *(tp -> t_name) != NULL; tp++)
  18472. if(smatch(key, tp -> t_name))
  18473. break;
  18474. (*(tp -> t_fn))(lp);
  18475. }
  18476. }
  18477.  
  18478. int iswhite(c)
  18479. register int c;
  18480. {
  18481. return (c != NULL && (c == ' ' c == '\t, 
  18482. c == '\n' c == '\r'));
  18483. }
  18484.  
  18485. char *skipwh(p)
  18486. register char *p;
  18487. {
  18488. while(iswhite(*p))
  18489. ++p;
  18490. return p;
  18491. }
  18492.  
  18493.  
  18494. /**********************************************
  18495. * smatch:
  18496. * match two strings. return true if equal
  18497. *********************************************/
  18498. static int smatch(s1, s2)
  18499. char *s1, *s2;
  18500. {
  18501. while(*s1 != '\0')
  18502. {
  18503. if(*s1 !=*s2)
  18504. break;
  18505. ++s1;
  18506. ++s2;
  18507. }
  18508. return *s1 == *s2;
  18509. }
  18510.  
  18511. int mem(lp)
  18512. char *lp;
  18513. {
  18514. unsigned long l = 0l;
  18515. unsigned int seg, offset;
  18516. char far *fp;
  18517. char buf[80];
  18518.  
  18519. printf("Memory examine and change\n");
  18520. lp = skipwh(lp);
  18521. while(hexdigit(*lp) >= 0)
  18522. l = (l << 4) + hexdigit(*lp++);
  18523. if(*lp == ':')
  18524. {
  18525. ++lp;
  18526. seg = l;
  18527. l = 0l;
  18528. while(hexdigit(*lp) >= 0)
  18529. l = (l << 4) + hexdigit(*lp++);
  18530. offset = l;
  18531. }
  18532. else
  18533. {
  18534. seg = 0;
  18535. offset = l;
  18536. }
  18537. fp = MK_FP(seg, offset);
  18538. for(;;)
  18539. {
  18540. printf("%04x:%04x - %02x ", FP_SEG(fp),
  18541. FP_OFF(fp), *fp & 0xff);
  18542. gets(buf);
  18543. lp = skipwh(buf);
  18544. if(smatch(".", lp))
  18545. break;
  18546. if(!hexdigit(*lp) >= 0)
  18547. {
  18548. ++fp;
  18549. continue;
  18550. }
  18551. while(hexdigit(*lp) >= 0)
  18552. l = (l << 4) + hexdigit(*lp++);
  18553.  
  18554. *fp++ = l & 0xff;
  18555. }
  18556. }
  18557. int go(lp)
  18558.  
  18559. char *lp;
  18560. {
  18561. int (far *fp)();
  18562. unsigned long l = 0l;
  18563. unsigned int seg, offset;
  18564.  
  18565. lp = skipwh(lp);
  18566. while(hexdigit(*lp) >= 0)
  18567. l = (l << 4) + hexdigit(*lp++);
  18568. if(*lp == ':')
  18569. {
  18570. ++lp;
  18571. seg= l;
  18572. l = 0l;
  18573. while(hexdigit(*lp) >= 0)
  18574. l = (l << 4) + hexdigit(*lp++);
  18575. offset = l;
  18576. }
  18577. else
  18578. {
  18579. seg= 0;
  18580. offset = l;
  18581. }
  18582. fp = NK_FP(seg, offset);
  18583. printf("Go from %04x:%04x", FP_SEG(fp),
  18584. FP_OFF(fp));
  18585. (*fp)();
  18586. }
  18587.  
  18588. int error()
  18589. {
  18590. printf("*** Unknown command\n");
  18591. }
  18592.  
  18593. hexdigit(c)
  18594. int c;
  18595. {
  18596. register char *p;
  18597. register i;
  18598. static char *set = "0123456789abcdef",
  18599. *alt_set = "0123456789ABCDEF";
  18600.  
  18601. for(i = 0, p = set; *p != NULL; i++, p++)
  18602. if(c == *p)
  18603. return i;
  18604. for(i = 0, p = alt_set; *p != NULL; i++, p++)
  18605. if(c == *p)
  18606. return i;
  18607. return -1;
  18608. }
  18609.  
  18610.  
  18611. Listing 2
  18612. page 60,132
  18613.  
  18614. title start1 - simple start-up code
  18615. ;**************************************************************;
  18616. ; ;
  18617. ; start1.asm ;
  18618. ; ;
  18619. ; start-up for stand-alone C code - Turbo C/C++ version ;
  18620. ; ;
  18621. ; created: November 17, 1990 ;
  18622. ; ;
  18623. ; Copyright (c) 1990 ;
  18624. ; Pasquale J. Villani ;
  18625. ; All Rights Reserved ;
  18626. ; ;
  18627. ;**************************************************************;
  18628.  
  18629. _TEXT segment byte public 'CODE'
  18630. assume cs:_TEXT ; small model
  18631. _TEXT ends
  18632.  
  18633. _DATA segment word public 'DATA'
  18634. DGROUP group _DATA,_BSS,_BSSEND ; small model
  18635. assume ds:DGROUP,ss:DGROUP
  18636. _DATA ends
  18637.  
  18638. _BSS segment word public 'BSS'
  18639. _BSS ends
  18640.  
  18641. _BSSEND segment byte public 'STACK'
  18642. _BSSEND ends
  18643.  
  18644. _TEXT segment byte public 'CODE'
  18645.  
  18646. extrn _main:near ; C entry point
  18647. extrn _hdw_init:near ; Hardware init (asm or C)
  18648.  
  18649. ;
  18650. ; entry:
  18651. ; Our ROM madule's entry point. This should be used as the
  18652. ; 'jmp far ptr entry' at the top of ROM.
  18653. ;
  18654. entry proc far
  18655. public entry;
  18656.  
  18657. ;
  18658. ; First order of business is the initialization of the
  18659. ; machine itself. This is basically the initialization
  18660. ; of any segment registers that the processor needs for
  18661. ; proper operation. On an 80X86 class processor, this is
  18662. ; usually initializing the segmentation registers, going
  18663. ; to the proper mode (i. e. protected, etc.) for
  18664. ; 80[234]86 processors, etc. This file is for an 8086 or
  18665. ; 8088 machine (or any other in real mode). Segment
  18666. ; registers are the only processor initialization.
  18667. ;
  18668. ; NOTE: the cs register is not initialized because the
  18669. ; processor itself initializes the first cs, followed
  18670. ; by the far jump changing it to point to segment entry.
  18671. ;
  18672. cli ; prevent interrupts while starting
  18673.  
  18674. mov ax,DGROUP ; initialize the segment registers
  18675. mov ds,ax
  18676. mov ss,ax
  18677. mov es,ax
  18678. ;
  18679. ; Once the processor is initialized, it usually requires
  18680. ; the proper setting of the stack pointer(s). For the
  18681. ; 8086 case, all we need is to initialize the sp and bp
  18682. ; registers.
  18683. ;
  18684. mov sp,offset DGROUP:tos
  18685. mov bp,sp
  18686. ;
  18687. ; We are now ready to make any special hardware
  18688. ; initialization. This includes initializing the interrupt
  18689. ; vector table, moving the data segment to RAM and
  18690. ; initializing the BSS to zero to match C programming
  18691. ; conventions. This should end with a call to __hdw_init
  18692. ; to initialize I/O, etc.
  18693. call near ptr __hdw_init; initialize the system without ints
  18694. sti ;now enable them
  18695. ;
  18696. ; After the hardware is initialized, we wish to enter
  18697. ; main(). This is accomplished by building the stack for
  18698. ; envp, argv and argc. This is a convienent mechanism
  18699. ; for passing system configuration, dip switch settings,
  18700. ; etc. to our C code.
  18701. ;
  18702. mov ax,offset env ; envp for this example
  18703. push ax
  18704. mov ax,offset args ; argv for this example
  18705. push ax
  18706. mov ax,2 ; argc = 2
  18707. push ax
  18708. call near ptr _main ; finally enter C code
  18709. add sp,6 ; clean stack
  18710. ;
  18711. ; If main should return, we need a mechanism to catch this
  18712. ; condition. Depending on the system design, we should
  18713. ; alarm, restart, etc., therefore, the following code
  18714. ; is very implementation dependant. In our case, all we
  18715. ; want to do is call exit with a special -1 exit code.
  18716. ;
  18717. mov ax,-1
  18718. push ax
  18719. call near ptr _exit
  18720. jmp $ ; belt and suspenders
  18721.  
  18722. entry endp
  18723.  
  18724. ;
  18725. ; exit:
  18726. ; Where to go for error conditions (typically) or shutdown
  18727. ; conditions. This code is very implementaion sensitive, since
  18728. ; we may want to light LEDs and sound alarms. In this case, we'll
  18729. ; only silently stop. In certain cases, we may want to only
  18730. ; display a message and restart. This should be decided upon
  18731. ; during system design.
  18732. ;
  18733.  
  18734. _exit proc near
  18735. public _exit
  18736.  
  18737. cli
  18738. hlt
  18739. jmp _exit
  18740.  
  18741. _exit endp
  18742.  
  18743. T_EXT ends
  18744.  
  18745. _DATA segment word public 'DATA'
  18746. env0 label byte
  18747. db 'ENV=ROM',0
  18748. arg0 label byte
  18749. db 'ex0s', 0
  18750. arg1 label byte
  18751. db 'rom', 0
  18752. env label word
  18753. dw DGROUP:env0
  18754. dw 0
  18755. args label word
  18756. dw DGROUP:arg0
  18757. dw DGROUP:arg1
  18758. dw 0
  18759. _DATA ends
  18760.  
  18761. ;
  18762. ; The stack segment. This size should be adjusted to fit
  18763. ; any particular system requirements.
  18764. ;
  18765. _STACK SEGMENT
  18766. dw 512 dup (?)
  18767. tos label byte
  18768. dd (?) ; safety area
  18769. last label word ; must always be end of stack area
  18770. _STACK ENDS
  18771.  
  18772. _BSS segment word public 'BSS'
  18773. _errno label word ; c lib errno
  18774. public _errno
  18775. dw (?)
  18776. _BSS ends
  18777.  
  18778. _BSSEND segment byte public 'STACK'
  18779. _BSSEND ends
  18780.  
  18781. end
  18782.  
  18783.  
  18784. Listing 3
  18785. /***************************************/
  18786. /* */
  18787. /* osem.c */
  18788. /* */
  18789. /* Operating system emulation routines */
  18790. /* for embedded system example ROM */
  18791. /* monitor. */
  18792. /* */
  18793.  
  18794. /* Copyright (c) 1990 */
  18795. /* Pasquale J. Villani */
  18796. /* All Rights Reserved */
  18797. /* */
  18798. /* */
  18799. /***************************************/
  18800.  
  18801. #include <sys/types.h>
  18802. #include <sys/stat.h>
  18803. #include <fcntl.h>
  18804. #include <errno.h>
  18805.  
  18806. /* convience defines */
  18807. #define STDIN 0
  18808. #define STDOUT 1
  18809. #define STDERR 2
  18810. #define MAXFD 3
  18811. #define BUFSIZ 512 /* buffers are 512 for this vsn */
  18812.  
  18813. #ifndef TRUE
  18814. #define TRUE 1
  18815. #endif
  18816. #ifndef FALSE
  18817. #define FALSE 0
  18818. #endif
  18819. #ifndef ERROR
  18820. #define ERROR -1
  18821. #endif
  18822. #ifndef OK
  18823. #define OK 0
  18824. #endif
  18825. #ifndef EOF
  18826. #define EOF -1
  18827. #endif
  18828.  
  18829. /* make errno accessible to the entire file */
  18830. extern int errno;
  18831.  
  18832. /* special data structure for ioctl optional argument */
  18833. union arg
  18834. {
  18835. int i;
  18836. void *p;
  18837. };
  18838.  
  18839. /* a simple file table to keep track of stdin & stdout status */
  18840. static char fd[MAXFD] =
  18841. {
  18842. FALSE, /* stdin */
  18843. FALSE, /* stdout */
  18844. FALSE /* stderr */
  18845. };
  18846.  
  18847. /* a simple set of i/o buffers for input buffering */
  18848. struct _buf
  18849. {
  18850. int nbytes;
  18851. char *bp;
  18852. char buf[BUFSIZ];
  18853.  
  18854. };
  18855.  
  18856. static struct _buf iobuf =
  18857. {
  18858. -1, (char *)0
  18859. };
  18860.  
  18861. /* */
  18862. /* smatch: */
  18863. /* match two strings. return true if equal */
  18864. /* */
  18865. static int smatch(s1, s2)
  18866. char *s1, *s2;
  18867.  
  18868. {
  18869. while(*s1 != '\0')
  18870. {
  18871. if(*s1 != *s2)
  18872. break;
  18873. ++s1;
  18874. ++s2;
  18875. }
  18876. return s1 == s2;
  18877. }
  18878.  
  18879. /* */
  18880. /* open: */
  18881. /* open a file for read or write */
  18882. /* follows posix specification */
  18883. /* */
  18884. int open(path, oflag, mode)
  18885. char *path;
  18886. int oflag, mode;
  18887. {
  18888. if(smatch(path, "/dev/con") ((oflag & O_RDWR) == O_RDWR))
  18889. {
  18890. errno = EACCES;
  18891. return ERROR; /* error - only console allowed */
  18892. }
  18893. /* for this system, only stdin and stdout are available
  18894. /* based on oflag and availability, assign appropraie fd
  18895. if((oflag & O_RDONLY) && !fd[STDIN])
  18896. {
  18897. fd[STDIN] = TRUE;
  18898. return STDIN;
  18899. }
  18900. else if(oflag & O_WRONLY)
  18901. {
  18902. fd[STDOUT] = TRUE;
  18903. return STDOUT;
  18904. }
  18905. else
  18906. {
  18907. errno = EACCES;
  18908. return ERROR;
  18909. }
  18910. }
  18911.  
  18912. /* */
  18913.  
  18914. /* close: */
  18915. /* close a file */
  18916. /* */
  18917. int close(fildes)
  18918. int fildes;
  18919. {
  18920. if((fildes < 0) (fildes > MAXFD) !(fd[fildes]))
  18921. {
  18922. errno = EBADF;
  18923. return ERROR;
  18924. }
  18925. fd[fildes] = FALSE;
  18926. return OK;
  18927. }
  18928.  
  18929. /* */
  18930. /* ioctl: */
  18931. /* direct device control */
  18932. /* */
  18933. int ioctl(fildes, request, arg)
  18934. int fildes, request;
  18935. union arg arg;
  18936. {
  18937. errno = EINVAL;
  18938. return ERROR;
  18939. }
  18940. /* */
  18941. /* write: */
  18942. /* write to a file */
  18943. /* */
  18944. int write(fildes, buf, nbyte)
  18945. int fildes;
  18946. void *buf;
  18947. unsigned int nbyte;
  18948. {
  18949. int cnt = nbyte;
  18950. if(fildes != STDOUT && fildes != STDERR)
  18951. {
  18952. errno = EBADF;
  18953. return ERROR;
  18954. }
  18955. while(cnt-- > 0)
  18956. {
  18957. if(*(char *)buf == '\n')
  18958. _cout('\r');
  18959. _cout(*((char *)buf)++);
  18960. }
  18961. return nbyte;
  18962. }
  18963. int read(fildes, buf, nbyte)
  18964. int fildes;
  18965. void *buf;
  18966. unsigned int nbyte;
  18967. {
  18968. register char *bp;
  18969. register int c;
  18970.  
  18971. if(fildes != STDIN)
  18972. {
  18973.  
  18974. errno = EBADF;
  18975. return ERROR;
  18976. }
  18977. if(iobuf.nbytes <= 0)
  18978. {
  18979. bp = iobuf.buf;
  18980. iobuf.nbytes = 0;
  18981. do
  18982. {
  18983. *bp++ = (_cout(c = _cin()));
  18984. if(c == '\r')
  18985. _cout('\n');
  18986. ++iobuf.nbytes;
  18987. } while(c != '\n' && c != '\r');
  18988. iobuf.bp = iobuf.buf;
  18989. }
  18990. for(bp = iobuf.bp, c = 0; c < iobuf.nbytes && c <nbyte; c++)
  18991. {
  18992. *((char *)buf)++ = *bp == '\r' ? ++bp, '\n' : *bp++;
  18993. }
  18994. iobuf.bp = bp;
  18995. iobuf.nbytes -= c;
  18996. return c;
  18997. }
  18998.  
  18999.  
  19000. Listing 4
  19001. ;
  19002. ; _cio - console i/o for embedded system example
  19003. ; near version
  19004. ;
  19005.  
  19006. _TEXT segment byte public 'CODE'
  19007. DGROUP group _DATA,_BSS
  19008. assume cs:_TEXT,ds:DGROUP,ss:DGROUP
  19009. _TEXT ends
  19010.  
  19011. _DATA segment word public 'DATA'
  19012. _DATA ends
  19013.  
  19014. _BSS segment word public 'BSS'
  19015. _BSS ends
  19016.  
  19017. _TEXT segment byte public 'CODE'
  19018.  
  19019.  
  19020. ;
  19021. ; _cin - input to console
  19022. ; near version
  19023. ;
  19024.  
  19025. __cin proc near
  19026. public __cin
  19027. push bp ; perform c entry
  19028. mov bp,sp
  19029. push bx ; save bx for c
  19030. mov ah,0h ; get the byte
  19031. mov bx,7
  19032. int 16h ; using the bios
  19033.  
  19034. mov ah,0 ; "sign extend" - convert to int
  19035. pop bx ; restore context
  19036. pop bp
  19037. ret
  19038. __cin endp
  19039.  
  19040. ;
  19041. ;_cout - output to console
  19042. ; near version
  19043. ;
  19044.  
  19045. __cout proc near
  19046. public __cout
  19047. push bp ; c entry
  19048. mov bp,sp
  19049. push bx ; save register for c
  19050. mov ax,word ptr [bp+4] ; get c
  19051. mov ah,0eh ; and output the byte
  19052. mov bx,7
  19053. int 10h ; call bios
  19054. pop bx ; restore context
  19055. pop bp
  19056. ret
  19057. __cout endp
  19058.  
  19059. ;
  19060. ; _cinit - console initialization
  19061. ; **** No initialization required for bios version ****
  19062. ;
  19063. ; near version
  19064. ;
  19065. __cinit proc near
  19066. public __cinit
  19067. push bp
  19068. ; perform any required hardware initialization here
  19069. mov ax,1 ; return true for success
  19070. pop bp
  19071. ret
  19072.  
  19073. __cinit endp
  19074.  
  19075. _TEXT ends
  19076.  
  19077. end
  19078.  
  19079.  
  19080. Listing 5
  19081. /****************************************************/
  19082. /* */
  19083. /* iolink.c */
  19084. /* */
  19085. /* stdio tie for Turbo C/C++ Compilers */
  19086. /* */
  19087. /* Copyright (C) 1990 */
  19088. /* Pasquale J. Villani */
  19089. /* All Rights Reserved */
  19090. /* */
  19091. /****************************************************/
  19092.  
  19093.  
  19094. #include <stdio.h>
  19095.  
  19096. FILE _streams[20];
  19097.  
  19098. fputc(c, f)
  19099. char c;
  19100. FILE *f;
  19101. {
  19102. return _fputc(c, f);
  19103. }
  19104. _fputc(c, f)
  19105. char c;
  19106. FILE *f;
  19107. {
  19108. if(write(1, &c, 1) == 1)
  19109. return c;
  19110. else
  19111. return EOF;
  19112. }
  19113.  
  19114. _fgetc(f)
  19115. FILE *f;
  19116. {
  19117. char c;
  19118.  
  19119. if(read(0, &c, 1) != 1)
  19120. return EOF;
  19121. else
  19122. return c;
  19123. }
  19124.  
  19125.  
  19126. Listing 6
  19127. /*************************************************/
  19128. /* */
  19129. /* puts.c */
  19130. /* */
  19131. /* Put a string to console. Uses stdio.h putchar */
  19132. /* macro for operating system interface. Outputs */
  19133. /* a newline after the string */
  19134. /* */
  19135. /* Copyright (c) 1990 */
  19136. /* Pasquale J. Villani */
  19137. /* All Rights Reserved */
  19138. /* */
  19139. /* */
  19140. **************************************************/
  19141.  
  19142. #include <stdio.h>
  19143.  
  19144. puts(s)
  19145. const char *s;
  19146. {
  19147. int c;
  19148.  
  19149. while ((c = *s++) != '\0')
  19150. putchar(c);
  19151. putchar('\n');
  19152. }
  19153.  
  19154.  
  19155.  
  19156. Listing 7
  19157. /*
  19158. * abbreviated printf routine
  19159. *
  19160. * embedded system version
  19161. * 11/30/90
  19162. */
  19163.  
  19164. /* ltob -- convert an long integer to a string in any
  19165. base (2-36) */
  19166. static char *ltob(n, s, base)
  19167. long n;
  19168. char *s;
  19169. {
  19170. unsigned long u;
  19171. register char *p, *q;
  19172. register negative, c;
  19173.  
  19174. if (n < 0 && base == -10)
  19175. {
  19176. negative = 1;
  19177. u = -n;
  19178. }
  19179. else
  19180. {
  19181. negative = 0;
  19182. u = n;
  19183. }
  19184. if (base == -10) /* signals signed
  19185. conversion */
  19186. base = 10;
  19187. p = q = s;
  19188. do
  19189. { /* generate digits in reverse
  19190. order */
  19191. *p++ = "0123456789abcdef"[u % base];
  19192. } while ((u /= base) > 0);
  19193. if (negative)
  19194. *p++ = -;
  19195. *p = '\0'; /* terminate the string */
  19196. while (q < --p)
  19197. { /* reverse the digits */
  19198. c = *q;
  19199. *q++ = *p;
  19200. *p = c;
  19201. }
  19202. return s;
  19203. }
  19204.  
  19205. #define NONE 0
  19206. #define LEFT 1
  19207. #define RIGHT 2
  19208.  
  19209. /* printf -- short version of printf to conserve
  19210. space */
  19211. int printf(fmt, args)
  19212. const char *fmt;
  19213.  
  19214. char *args;
  19215. {
  19216. register base;
  19217. register char **arg;
  19218. char s[11], *p, *ltob();
  19219. char c, slen, flag, size, fill;
  19220.  
  19221. arg = &args;
  19222. flag = NONE;
  19223. size = 0;
  19224. while ((c = *fmt++) != '\0')
  19225. {
  19226. if (size == 0 && flag == NONE && c != '%')
  19227. {
  19228.  
  19229. write(1, &c, 1);
  19230. continue;
  19231. }
  19232. switch(*fmt)
  19233. {
  19234. case '-':
  19235. flag = RIGHT;
  19236. fill = *(fmt + 1) == '0' ? '0' : ' ';
  19237. continue;
  19238.  
  19239. case '0':
  19240. case '1':
  19241. case '2':
  19242. case '3':
  19243. case '4':
  19244. case '5':
  19245. case '6':
  19246. case '7':
  19247. case '8':
  19248. case '9':
  19249. if(flag == NONE)
  19250. flag = LEFT;
  19251. size = *fmt++ - '0';
  19252. while((c = *fmt++) != '\0')
  19253. {
  19254. switch(c)
  19255. {
  19256. case '0':
  19257. case '1':
  19258. case '2':
  19259. case '3':
  19260. case '4':
  19261. case '5':
  19262. case '6':
  19263. case '7':
  19264. case '8':
  19265. case '9':
  19266. size = size * 10 + (c - '0');
  19267. continue;
  19268.  
  19269. default:
  19270. --fmt;
  19271. break;
  19272. }
  19273.  
  19274. break;
  19275. }
  19276. break;
  19277. }
  19278. switch (c = *fmt++)
  19279. {
  19280. case 'c':
  19281. write(1, (char *)arg++, 1);
  19282. continue;
  19283.  
  19284. case 'd':
  19285. base = -10;
  19286. goto prt;
  19287.  
  19288. case 'o':
  19289. base = 8;
  19290. goto prt;
  19291.  
  19292. case 'u':
  19293. base = 10;
  19294. goto prt;
  19295.  
  19296. case 'x':
  19297. base = 16;
  19298.  
  19299. prt:
  19300. ltob((long)(*((int *)arg)++), s, base);
  19301. if(flag == RIGHT flag == LEFT)
  19302. {
  19303. for(slen = 0, p = s; *p != '\0'; p++)
  19304. ++slen;
  19305. }
  19306. if(flag == RIGHT && slen < size)
  19307.  
  19308. {
  19309. int i;
  19310.  
  19311. for(i = size - slen; i > 0; i--)
  19312. write(1, &fill, 1);
  19313. }
  19314. for(p = s; *p != '\0'; p++)
  19315. write(1, p, 1);
  19316. if(flag == LEFT)
  19317. {
  19318. int i;
  19319. char sp = ' ';
  19320.  
  19321. for(i = size - slen; i > 0; i--)
  19322. write(1, &sp, 1);
  19323. }
  19324. size = 0;
  19325. flag = NONE;
  19326. continue;
  19327.  
  19328. case 'l':
  19329. switch(c = *fmt++)
  19330. {
  19331. case 'd':
  19332. base = -10;
  19333.  
  19334. goto lprt;
  19335.  
  19336. case 'o':
  19337. base = 8;
  19338. goto lprt;
  19339.  
  19340. case 'u':
  19341. base = 10;
  19342. goto lprt;
  19343.  
  19344. case 'x':
  19345. base = 16;
  19346.  
  19347. lprt:
  19348. ltob(*((long *)arg)++, s, base);
  19349. if(flag == RIGHT flag == LEFT)
  19350. {
  19351. for(slen = 0, p = s; *p != '\0'; p++)
  19352.  ++slen;
  19353. }
  19354. if(flag == RIGHT && slen < size)
  19355. {
  19356. int i;
  19357.  
  19358. for(i = size - slen; i > 0; i--)
  19359.  write(1, &fill, 1);
  19360. }
  19361. for(p = s; *p != '\0'; p++)
  19362. write(1, p, 1);
  19363. if(flag == LEFT)
  19364.  
  19365. {
  19366. int i;
  19367. char sp = ' ';
  19368.  
  19369. for(i = size - slen; i > 0; i--)
  19370.  write(1, &sp, 1);
  19371. }
  19372. size = 0;
  19373. flag = NONE;
  19374. continue;
  19375.  
  19376. default:
  19377. write(1, &c, 1);
  19378. }
  19379.  
  19380. case 's':
  19381. for(p = *arg; *p != '\0'; p++)
  19382. write(1, p, 1);
  19383. ++arg;
  19384. continue;
  19385.  
  19386. default:
  19387. write(1, &c, 1);
  19388. continue;
  19389. }
  19390. }
  19391. }
  19392.  
  19393.  
  19394.  
  19395. Listing 8
  19396. #
  19397. # makefile for example 0
  19398. #
  19399. # 12/02/90 1830 EST
  19400. #
  19401.  
  19402. CC = tcc
  19403. CFLAGS = -ms -v
  19404. AS = masm
  19405. ASFLAGS = /Mx/Zi
  19406. LD = tlink
  19407. LDFLAGS = /v/c
  19408.  
  19409. OBJSROM = start1.obj cio.obj osem.obj iolink.obj\
  19410. hdwinit.obj prf.obj puts.obj gets.obj
  19411. LIBSROM = libm.lib
  19412.  
  19413. all: ex0.exe ex0s.exe mon86.exe mon86r.exe
  19414.  
  19415. mon86.exe: mon86.obj
  19416. $(CC) $(CFLAGS) -emon86 mon86
  19417.  
  19418. osem.obj: osem.c
  19419. $(CC) $(CFLAGS) -c osem.c
  19420.  
  19421. mon86.obj: mon86.c
  19422. $(CC) $(CFLAGS) -c mon86.c
  19423.  
  19424. iolink.obj: iolink.c
  19425. $(CC) $(CFLAGS) -c iolink.c
  19426.  
  19427. prf.obj: prf.c
  19428. $(CC) $(CFLAGS) -c prf.c
  19429.  
  19430. puts.obj: puts.c
  19431. $(CC) $(CFLAGS) -c puts.c
  19432.  
  19433. gets.obj: gets.c
  19434. $(CC) $(CFLAGS) -c gets.c
  19435.  
  19436. start0.obj: start0.asm
  19437. $(AS) $(ASFLAGS) start0,,,;
  19438.  
  19439. ex0s.exe: ex0.obj $(OBJSROM)
  19440. $(LD) $(LDFLAGS) $(OBJSROM) ex0.obj,\
  19441. ex0s,nul,$(LIBSROM)
  19442.  
  19443. mon86r.exe: mon86.obj $(OBJSROM)
  19444. $(LD) $(LDFLAGS) $(OBJSROM) mon86.obj,\
  19445. mon86r,nul,$(LIBSROM)
  19446.  
  19447. start1.obj: start1.asm
  19448. $(AS) $(ASFLAGS) start1,,,;
  19449.  
  19450. cout.obj: cout.asm
  19451. $(AS) $(ASFLAGS) cout,,,;
  19452.  
  19453.  
  19454. cin.obj: cin.asm
  19455. $(AS) $(ASFLAGS) cin,,,;
  19456.  
  19457. cio.obj: cio.asm
  19458. $(AS) $(ASFLAGS) cio,,,;
  19459.  
  19460. ex0.obj: ex0.c
  19461. $(CC) $(CFLAGS) -c ex0.c
  19462.  
  19463.  
  19464.  
  19465.  
  19466.  
  19467.  
  19468.  
  19469.  
  19470.  
  19471.  
  19472.  
  19473.  
  19474.  
  19475.  
  19476.  
  19477.  
  19478.  
  19479.  
  19480.  
  19481.  
  19482.  
  19483.  
  19484.  
  19485.  
  19486.  
  19487.  
  19488.  
  19489.  
  19490.  
  19491.  
  19492.  
  19493.  
  19494.  
  19495.  
  19496.  
  19497.  
  19498.  
  19499.  
  19500.  
  19501.  
  19502.  
  19503.  
  19504.  
  19505.  
  19506.  
  19507.  
  19508.  
  19509.  
  19510.  
  19511.  
  19512.  
  19513.  
  19514.  
  19515.  
  19516.  
  19517. Pointer-Pointers In C
  19518.  
  19519.  
  19520. Anthony Dos Reis and Li Yun
  19521.  
  19522.  
  19523. Anthony Dos Reis is an assistant professor of computer science at the College
  19524. of New Paltz in New York. Li Yun is a graduate student in the college's
  19525. Mathematics and Computer Science Department.
  19526.  
  19527.  
  19528. Pointer-pointers are literally pointers that point to other pointers. They are
  19529. used in functions that must change the value stored in a parameter. Because
  19530. variables in C are passed by value, not by reference, such functions must be
  19531. passed the variable's address (pass-by-reference), not its value. A parameter
  19532. can be a pointer. A pointer's address is, of course, just a pointer-pointer.
  19533. For example, the increment function below increments an integer pointer. It
  19534. declares parameter p as
  19535. int **p;
  19536. Breaking this declaration into two parts -- int * and *p -- makes its meaning
  19537. clear. *p indicates that p is a pointer. int * means that p points to an
  19538. integer pointer. In the function body, (*p) references the integer pointer to
  19539. which p points. Thus, (*p)++ increments this integer pointer.
  19540. increment (p)
  19541. int **p;
  19542. {
  19543. (*p)++;
  19544. }
  19545. Using a typedef statement for int* simplifies the parameter declaration.
  19546. typedef int * LINK;
  19547. increment (p)
  19548. LINK *p;
  19549. {
  19550. (*p)++;
  19551. }
  19552. Using pointer-pointers carefully can considerably reduce the complexity of
  19553. functions that perform pointer manipulation. For example, consider two
  19554. functions that both add a node to a binary search tree. The first function,
  19555. add1, uses the standard textbook technique of two pointers, one trailing the
  19556. other, to determine the insertion point for the new node. The two pointers,
  19557. current and previous, move down the tree until current takes the value NULL.
  19558. The function then adds the new node to the previous node. Notice that though
  19559. add1 is passed a pointer-pointer (rootpp), its use is limited to accessing the
  19560. root pointer of the tree.
  19561. The second function, add2, uses a single pointer-pointer, pp, that moves down
  19562. the tree pointing to either the node's left or right pointer fields. When the
  19563. pointer reaches the appropriate leaf, the function adds the new node.
  19564. The add1 function is more complex than add2 in several respects. First, it
  19565. moves two pointers, current and previous, instead of one. Second, add1
  19566. performs a special test to determine if the tree is NULL (in which case the
  19567. new node becomes the root of the tree). add2 needs no special test. Third,
  19568. after finding the leaf at which the insertion must occur, add1 must determine
  19569. whether to add to the left or to the right of the leaf, even though this
  19570. left/right test was just performed in the preceding while loop. In contrast,
  19571. add2 exits its while loop with pp pointing to the left or right field that
  19572. must be modified to add the new node.
  19573. Pointer-pointers may be a bit harder to understand at first, but the
  19574. investment can pay off in simpler code.
  19575.  
  19576. Listing 1
  19577. /*-------------------------------------
  19578. * This program was used to test the
  19579. * add1 and add2 functions
  19580. *-------------------------------------*/
  19581. #include <stdio.h>
  19582.  
  19583. typedef struct NODE* LINK;
  19584.  
  19585. struct NODE {
  19586. int info;
  19587. LINK left;
  19588. LINK right;
  19589. };
  19590.  
  19591. main()
  19592. {
  19593. LINK rootptr1=NULL,rootptr2=NULL;
  19594. int x;
  19595.  
  19596. printf("Enter integers terminated \
  19597. with ctrl z\n");
  19598. /* Create tree */
  19599. while (scanf("%d",&x) == 1) {
  19600.  
  19601. add1(&rootptr1,x);
  19602. add2(&rootptr2,x);
  19603.  
  19604. }
  19605. printf("Using add1\n");
  19606. recurse_traverse(rootptr1); /* Traverse tree */
  19607. printf("Using add2\n");
  19608. recurse_traverse(rootptr2);
  19609. }
  19610. recurse_traverse(rootptr) LINK rootptr;
  19611. {
  19612. if (rootptr != NULL) { /* Is tree null? */
  19613. recurse_traverse(rootptr -> left); /* Recurse */
  19614. printf("%d\n",rootptr -> info);
  19615. recurse_traverse(rootptr -> right); /* Recurse */
  19616. }
  19617. }
  19618. add1(rootpp,val) LINK *rootpp;int val;
  19619. {
  19620. LINK newnode,current,previous;
  19621.  
  19622. /* Create a new node */
  19623. newnode = (LINK) malloc(sizeof(struct NODE));
  19624. newnode->info = val;
  19625. newnode->left = NULL;
  19626. newnode->right = NULL;
  19627. current = *rootpp;
  19628. previous = NULL;
  19629. while (current != NULL) { /* Search for leaf */
  19630. previous = current;
  19631. if (val < current->info)
  19632. current = current->left;
  19633. else
  19634. current = current->right;
  19635. }
  19636. if (previous == NULL) /* Null tree? */
  19637. *rootpp = newnode; /* Node becomes tree */
  19638. else
  19639. if (val < previous->info) /* Add left/right? */
  19640. previous->left = newnode; /* Add to left */
  19641. else
  19642. previous->right = newnode; /* Add to right */
  19643.  
  19644. }
  19645. add2(pp,val) LINK *pp; int val;
  19646. {
  19647. LINK newnode;
  19648.  
  19649. /* Create a new node */
  19650. newnode = (LINK) malloc(sizeof(struct NODE));
  19651. newnode->info = val;
  19652. newnode->left = NULL;
  19653. newnode->right = NULL;
  19654. while (*pp != NULL) { /* Search for leaf */
  19655. if (val < (*pp)->info)
  19656. pp = &(*pp)->left; /* Move pp */
  19657. else
  19658. pp = &(*pp)->right; /* Move pp */
  19659. }
  19660. *pp = newnode; /* Add node to tree */
  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.  
  19688.  
  19689.  
  19690.  
  19691.  
  19692.  
  19693.  
  19694.  
  19695.  
  19696.  
  19697.  
  19698.  
  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. X Window Programming
  19728.  
  19729.  
  19730. Part 1: The X Window System
  19731.  
  19732.  
  19733.  
  19734.  
  19735. Eric F. Johnson and Kevin Reichard
  19736.  
  19737.  
  19738. Johnson and Reichard are authors of X Window Applications Programming,
  19739. Advanced X Window Applications Programming and Power Programming Motif (all
  19740. MIS: Press, 800-MANUALS). Johnson can be reached at erc@pai.mn.org via UUCP
  19741. mail. Reichard's Compuserve address is 73670,3422.
  19742.  
  19743.  
  19744. As any C programmer who has gone near Windows, OS/2, or the Macintosh knows,
  19745. programming in graphical interface environments can be a struggle.
  19746. Furthermore, the resulting application is usually tied to the machine for
  19747. which it was written, defying the portability C provided in the first place.
  19748. There exists, however, a graphical interface programming environment that
  19749. retains the portability associated with C and that runs on everything from an
  19750. Amiga to an IBM PS/2 to a Cray supercomputer -- the X Window System.
  19751. X acts as an elegant and highly functional link between disparate systems,
  19752. serving as a common interface across many different computers running
  19753. different operating systems with several different displays. For instance,
  19754. using X, Cray supercomputers can display output on an Apollo workstation, on
  19755. an Amiga, or on any X station across a network. Major computer vendors, such
  19756. as Apple, Xerox, IBM, Hewlett-Packard, and Sun, offer and promote X products.
  19757. For the programmer, the X Window System offers a variety of tools, shared
  19758. resources and widespread portability. For the user, X Window provides a
  19759. consistent interface, regardless of the platform.This article begins a series
  19760. that will cover some basic X Window programming techniques. In this series, we
  19761. plan to cover not only the mechanics of X Window programming, but also the
  19762. theoretical underpinnings that make X such a unique programming environment.
  19763.  
  19764.  
  19765. Why X?
  19766.  
  19767.  
  19768. Several reasons explain why X has gained momentum in the workplace. First of
  19769. all, X is an open environment. MIT holds rights to X, but allows unrestricted
  19770. use. Thus the X developer isn't tied to a single vendor (such as Microsoft/IBM
  19771. and MS-DOS).
  19772. The X Window System is also a graphical windowing system. Studies indicate
  19773. that graphical interfaces make computing easier and users more productive.
  19774. X has also piggybacked on the growing interest in UNIX. While not designed
  19775. specifically for UNIX, X's greatest acceptance as a windowing system has been
  19776. on UNIX. Unlike other UNIX windowing systems, such as NeWS from Sun and the
  19777. NeXT interface, X is an open system usable without licensing fees.
  19778. In an era where linking different kinds of computers is paramount for software
  19779. designers, broad-based acceptance for a windowing system is paramount for
  19780. cross-vendor development. X's client-server model makes it possible to link
  19781. disparate makes of hardware without costly emulation cards and exotic
  19782. networking schemes. The user interface, which makes only X calls, doesn't rely
  19783. on any one operating system.
  19784. A list of important X features would include the following characteristics.
  19785.  
  19786.  
  19787. Portability
  19788.  
  19789.  
  19790. X runs on computers ranging from the Amiga personal computer to the Cray-2
  19791. supercomputer. You will find X on PCs, Macintoshes, Hewlett-Packards, PS/2s,
  19792. Suns, Data Generals, DECs, and most everything else. Barring a few minor
  19793. differences, the C interface is the same across all these platforms, a major
  19794. benefit over most other graphics systems.
  19795.  
  19796.  
  19797. Network Transparency
  19798.  
  19799.  
  19800. An X program, if written correctly, can run on one machine and display its
  19801. output on another machine. X, by being operating-system independent,
  19802. encourages portable software. X's standard C library routines are common
  19803. across hardware platforms, meaning that your interface code ports directly
  19804. from one machine to the other.
  19805.  
  19806.  
  19807. Vendor Backing
  19808.  
  19809.  
  19810. X runs on most every UNIX workstation available today. Digital Equipment,
  19811. Hewlett-Packard, IBM, and Apple all offer their own versions of X. Other
  19812. vendors offer the generic MIT X with their workstations, such as Data
  19813. General's Aviion series. For MS-DOS users, several companies -- Quarter-deck
  19814. and Integrated Inference among them -- offer versions of X for MS-DOS and
  19815. Windows. Those who use UNIX on a PC will discover that almost all major
  19816. vendors -- Interactive, SCO, Apple, Everex -- offer X Window with their UNIX
  19817. systems. And finally, other vendors have merged X with proprietary windowing
  19818. systems (NeWS in the case of Sun Microsystems) and now offer merged X servers.
  19819.  
  19820.  
  19821. Inexpensive Licensing
  19822.  
  19823.  
  19824. You can obtain the X sources (written in C) free. The information later in
  19825. this article will help you order X, sually for the price of a tape and a small
  19826. handling fee. You are not required to pay licensing fees if you release
  19827. X-based applications.
  19828.  
  19829.  
  19830. Shared Resources
  19831.  
  19832.  
  19833. The X Window System allows programs to concurrently share devices such as
  19834. mice, keyboards, and graphics displays. With X, your entire workstation is a
  19835. display, consisting of a keyboard, a pointing device (usually a mouse), and
  19836. one or more monitor screens. Multiple screens can work together, linked by the
  19837. keyboard and the pointing device.
  19838.  
  19839. Lest we give the impression that X is a bed of roses, we should make it clear
  19840. that X is a high-level programming environment. A good deal of X development
  19841. today is done at the workstation level. PC users can't merely slap X on a hard
  19842. disk and expect a high-performance system. Those interested in X should obtain
  19843. as much RAM as possible and invest in large hard drives -- 80Mb is good, 120Mb
  19844. is better, and 300Mb is even better. You should look into high- resolution
  19845. displays, and in networking cards, Ethernet, and TCP/IP. As for software
  19846. development expertise, there's a certain amount of sophistication required to
  19847. program in X Window. Experience with C programming and net- working is
  19848. necessary.
  19849.  
  19850.  
  19851. The History Of X
  19852.  
  19853.  
  19854. In 1984, Massachusetts Institute of Technology (MIT) officials were faced with
  19855. a motley set of incompatible workstations acquired through donation and
  19856. purchase. Their goal was to build a network of graphical workstations. Faced
  19857. with a crazy quilt of operating systems and hardware vendors, MIT officials
  19858. formed Project Athena, an MIT development team working in association with DEC
  19859. and IBM.
  19860. Project Athena's solution was to design a network-based windowing system that
  19861. would run local applications while being able to call on remote sources. This
  19862. system was loosely based on a Stanford University software environment called
  19863. W. By linking these workstations through a graphical networking environment,
  19864. the designers created the first operating environment that was truly hardware
  19865. and vendor independent: The X Window System.
  19866. The development team had the following goals:
  19867. Do not add new functionality unless an implementation cannot complete a real
  19868. application without it.
  19869. Do not serve all the world's needs, but make the system extensible so that
  19870. additional needs can be met in an upwardly compatible fashion.
  19871. If a problem is not completely understood, it is probably best to provide no
  19872. solution at all.
  19873. If you get 90 percent of the desired effect for 10 percent of the work
  19874. required to get 100 percent, use the simpler solution.
  19875. Isolate complexity as much as possible.
  19876. Provide mechanism rather than policy. In particular, place user-interface
  19877. policy in the client's hands.
  19878. By 1986, the outside world was clamoring for access to the X Window system. In
  19879. March 1988, MIT officially issued Version 11, Release 2. Later that year,
  19880. Release 3 became available. In January 1990, Release 4 was unleashed. As of
  19881. this writing, only a few vendor-supported X products are at Release 4. Release
  19882. 5 will probably appear in late 1991.
  19883. When the X Consortium people refer to X, they call it the "X Window System,"
  19884. "X Window," "X11," or just plain X." People in the know never call it "X
  19885. Windows" (note the "s").
  19886.  
  19887.  
  19888. What Is X?
  19889.  
  19890.  
  19891. The X Window System is based on a client/server relationship. The display
  19892. server program controls and draws all output to the display monitors--that is,
  19893. it draws the dots on your physical monitor, tracks client input, and updates
  19894. windows accordingly. Clients are application programs that perform specific
  19895. tasks. (The terms client and applications are used interchangeably.) Because X
  19896. is a networked environment, the client and server don't necessarily compute on
  19897. the same system (although they certainly can in a number of situations).
  19898. Instead, X allows distributed processing. For example, a Macintosh running
  19899. A/UX as an X server can call upon a Cray supercomputer's processing power
  19900. within the network, displaying the Cray's computations on the Mac's monitor.
  19901. In the micro and mini worlds, a server is the hardware device at the center of
  19902. a network, distributing data and processing power to networked workstations
  19903. and terminals. In contracts, an X server is a local software program that
  19904. controls the display hardware and runs on your local workstation. Because
  19905. other systems on the network can access your display, you cannot view the X
  19906. server as a file server in local area networks (LANs).
  19907. In X, a display consists of a keyboard, pointing device (usually a mouse), and
  19908. one or more screens. The display server tracks multiple input, allowing users
  19909. to run several clients (such as a database manager, word processor, and
  19910. graphics application). A display can power multiple screens linked by a
  19911. keyboard and mouse.
  19912. The server controls traffic between clients, or applications, running on local
  19913. or remote systems. It also controls the power of the local system. The server:
  19914. allows access to a display by a client
  19915. sends network messages
  19916. intercepts other network messages from other clients
  19917. performs two-dimensional drawing, freeing the client from processing-intensive
  19918. graphics
  19919. tracks resources (such as windows, cursors, fonts, and graphics contexts) that
  19920. are shared between clients
  19921. allows distributed processing
  19922. allows multitasking, if X is used with a multitasking operating system (such
  19923. as UNIX)
  19924. Most importantly, the server tracks input from the display and informs the
  19925. clients. In X, such inputs are called events. Pressing down on a key is an
  19926. event; letting the key back up is another event. Similarly, moving a cursor
  19927. with a mouse is an event. These events are delivered to applications through
  19928. an event queue.
  19929.  
  19930.  
  19931. The Sum Of Its Parts
  19932.  
  19933.  
  19934. MIT's generic X Window System consists of the Xlib graphics subroutine
  19935. library, the X network protocol, an X toolkit, and several window managers
  19936. (such as twm). The application programmer links the client program through
  19937. Xlib.
  19938. Xlib is the main programmer's interface to X. Xlib is a low-level library
  19939. providing access to X graphics and interface functions. Unlike most DOS-based
  19940. graphics, however, these low-level routines don't directly munge the hardware.
  19941. With the Xlib routines, you almost never directly interface to the underlying
  19942. hardware. While this may not seem optimal for performance, it does provide X's
  19943. much-vaunted portability.
  19944. Xlib provides functions and macros for drawing lines, creating windows,
  19945. displaying text and defining colors. Xlib hides most of the gory details of
  19946. connecting with an X server and maintaining network links.
  19947. Xlib contains about 300 routines that map to X Protocol requests or provide
  19948. utility functions. Xlib converts C function calls to the X protocol request
  19949. that actually implements the given function, such as XDrawLine to draw a line.
  19950. These functions include creating, destroying, moving, and sizing windows;
  19951. drawing lines and polygons; setting background patterns; and tracking the
  19952. mouse. Xlib provides various ways to access windows, including overlapping and
  19953. simultaneous output to multiple windows. It supports multiple fonts, raster
  19954. opera- operations, line drawing, and both color and monochrome applications.
  19955. All of these features facilitate portability, so your program should work the
  19956. same on a Sun workstation as it does on a IBM PS/2 running SCO Open Desktop.
  19957. Above Xlib sit many X toolkits, such as Motif and Open Look.
  19958. X toolkits are collections of sub-routines that can make programming easier.
  19959. They are prewritten graphics routines -- you can put together different parts
  19960. to form a program. Different vendors are constantly revising toolkits.
  19961. These toolkits (after a steep initial learning curve) can speed the creation
  19962. of X applications. Even if you plan on using toolkits exclusively, you will
  19963. still need to understand how Xlib operates in order to create quality,
  19964. commercial- grade X applications.
  19965. The X network protocol defines data structures used to transmit requests
  19966. between clients and servers. The X network protocol is not based on procedure
  19967. calls or a kernel-call interface. It is an asynchronous stream-based
  19968. inter-process communication.
  19969. The X Consortium supplies the protocol specification on tape. X Window System
  19970. Protocol (Version 11) by Robert Scheifler defines the protocol specification.
  19971. X Window System Protocol is included with the distribution tape. It can be
  19972. found on UNIX systems under ~TOP-LEVEL/mit/doc/Protocol/spec (TOP-LEVEL is the
  19973. base of your X directory tree).
  19974.  
  19975.  
  19976. Obtaining X Window
  19977.  
  19978.  
  19979. If you're a workstation or UNIX user, you can easily obtain the X Window
  19980. System free, and you don't need to license X or pay royalties.
  19981. You can obtain the X Window system directly from MIT, through Internet and
  19982. UUCP, and from commercial consulting firms and vendors.
  19983. MIT sells X on a set of four 1,600bpi tapes in UNIX tar format. A book (X
  19984. Window System: The Complete Reference to Xlib, X Protocol, ICCCM, XLFD) and
  19985. manuals sell for $125. Tapes, book, and manuals sell for $400. If interested,
  19986. write to:
  19987. MIT Software Distribution Center
  19988. Technology Licensing Office, room
  19989. E32-300
  19990.  
  19991. 77 Massachusetts Ave.
  19992. Cambridge, MA 02139
  19993. 617/258-8330 (the "X Ordering
  19994. Hotline")
  19995. If you're on Internet or UUCP, you can retrieve the release from the locations
  19996. shown in Table 1.
  19997. The FTP directories contain a README file with further instructions. (We don't
  19998. have room to print the README file, but you should persue that file before
  19999. proceeding.)
  20000. For DOS or PC users, implementing X means purchasing it from a commercial
  20001. vendor. Many UNIX vendors support X Window. SCO, for instance, offers X Window
  20002. and Motif as part of its Open Desktop software. Interactive Systems provides X
  20003. Window and Motif as part of 386/ix. Apple Computer integrates X Window and the
  20004. Macintosh operating system in v2.0 of A/UX. Hummingbird Communications
  20005. (416/470-1203) sells HCL-eXceed, a family of X servers for DOS-based PCs.
  20006. XVision (Visionware; sold through UniMarket 800-222-0550) displays both DOS
  20007. and X Window applications on a PC. PC-XView (again through UniMarket) turns a
  20008. PC into an X Window terminal.
  20009. Details regarding Desqview/X from Quarterdeck have been floating around for
  20010. months. We haven't seen the product, but it appears to be an interesting
  20011. marriage of X and DOS. Integrated Inference Machines (714-978- 6776) offers
  20012. X11/AT, a marriage of MS-Windows and the X Window System.
  20013. You can also obtain X Window from consulting firms:
  20014. Integrated Computer Solutions
  20015. 163 Harvard St.
  20016. Cambridge, MA 02139
  20017. 617-547-0510
  20018. info@ics. com; uunet!ics.com!info
  20019. O'Reilly and Associates
  20020. 632 Petaluma Ave.
  20021. Sebastopol, CA 95472
  20022. 800/338-6887
  20023. In addition, the following workstation and software vendors include the X
  20024. Window System in their products: AT&T, Apple, Bull, DEC, Data General, Everex,
  20025. Hewlett-Packard, IBM, IXI Limited, Intel, Interactive Systems, Motorola, NCR,
  20026. Open Software Foundation, SCO, Solbourne, Sony, Sun Microsystems, Visix, and
  20027. many more every day.
  20028.  
  20029.  
  20030. Information On Programming With X
  20031.  
  20032.  
  20033. These books are geared toward programming in X Window, and specifically Xlib.
  20034. During this series of articles, we'll mention other books tied to specific
  20035. topics. X beginners may want to start with any of the books on the following
  20036. list. The easiest way to learn X programming is by doing it, so have fun.
  20037. Johnson, Eric F. and Kevin Reichard, X Window Applications Programming, MIS:
  20038. Press, Portland, OR, 1989. ISBN 1-55828-016-2.
  20039. Johnson, Eric F. and Kevin Reichard, Advanced X Window Applications
  20040. Programming, MIS: Press, Portland, OR, 1990. ISBN 1-55828-029- 4.
  20041. Jones, Oliver, Introduction to the X Window System, Prentice-Hall, Englewood
  20042. Cliffs, NJ, 1989. ISBN 0-13- 499997-5.
  20043. Nye, Adrian, Xlib Programming Manual, Vol. 1, 2nd Ed., O'Reilly and Assoc.,
  20044. Sebastopol, CA, 1990. ISBN 0- 937175-11-0.
  20045. Nye, Adrian (ed.), Xlib Reference Manual, vol. 2, 2nd Ed., O'Reilly and
  20046. Assoc., Sebastopol, CA, 1990. ISBN 0- 937175-12-9.
  20047. Scheifler, Robert W. and James Gettys, with Jim Flowers, Ron Newman, and David
  20048. Rosenthal, X Window System: The Complete Reference to Xlib, X Protocol, ICCCM,
  20049. XLFD, 2nd Ed., Digital Press, Bedford, MA, 1990. ISBN (Digital Press)
  20050. 1-5558-050-5, (Prentice-Hall) 0-13-972050-2.
  20051. Table 1
  20052. LOCATION MACHINE INTERNET ANONYMOUS
  20053.  NAME ADDRESS FTP DIRECTORY
  20054. Western US gatekeeper.dec.com 16.1.0.2 pub/X11/R4
  20055. Central US mordred. cs.purdue. edu 128.10.2.2 pub/X11/R4
  20056.  
  20057. Central US giza. cis.ohio-state.edu 128.146.8.62 pub/X.VllR4
  20058. Southeast US uunet. uu. net 192.48.96.2 XR4
  20059.  
  20060. Northeast US crl.dec.com 192.58.206.2 pub/Xll/R4
  20061.  
  20062. UK Janet src.doc. ic. ac. uk 129.31.81.36 X.V11R4
  20063. UK niftp uk. ac. ic. doc. src <XV11R4>*
  20064.  
  20065. Australia munnari.oz.au 128.250.1.21 X.V11/R4
  20066.  
  20067.  
  20068.  
  20069.  
  20070.  
  20071.  
  20072.  
  20073.  
  20074.  
  20075.  
  20076.  
  20077.  
  20078.  
  20079.  
  20080.  
  20081.  
  20082.  
  20083. Image Processing
  20084.  
  20085.  
  20086. Part 1: Reading The Tag Image File Format
  20087.  
  20088.  
  20089.  
  20090.  
  20091. Dwayne Phillips
  20092.  
  20093.  
  20094. Dwayne Phillips works as a computer and electronics engineer with the United
  20095. States Department of Defense. He has a Ph.D. in electrical and computer
  20096. engineering at Louisiana State University. His interests include computer
  20097. vision, artificial intelligence, software engineering, and programming
  20098. languages.
  20099.  
  20100.  
  20101.  
  20102.  
  20103. Introduction
  20104.  
  20105.  
  20106. Programmers increasingly must obtain images, manipulate them, improve them,
  20107. and output them. This article is the first in a series on images and image
  20108. processing. Each article will include source code to demonstrate the concepts
  20109. it covers. The code implements the C Image Processing System (CIPS), a small
  20110. system that combines image processing operators with a simple user interface.
  20111. The first article of the series discusses image input and the Tag Image File
  20112. Format (TIFF). The second and third articles will discuss image output --
  20113. displaying and printing images. The fourth article will discuss histograms and
  20114. histogram equalization. [These articles will not all appear in sequential
  20115. issues. -- pjp]
  20116. Image processing involves processing or altering an existing image in a
  20117. desired manner. The first step is obtaining an image. While this may sound
  20118. obvious, it is not a simple matter, since usable image data is not readily
  20119. available. The programmer needs a simple method of obtaining image data in a
  20120. standard, usable format. The Tag Image File Format was designed to promote the
  20121. interchange of digital image data.[1]
  20122. Aldus (of PageMaker fame), Microsoft, and several other computer and scanner
  20123. companies decided to write an industry standard for digital image data
  20124. communication. Their collaboration resulted in the TIFF specification. Since
  20125. most scanner manufacturers support the standard in their PC and Macintosh
  20126. products, TIFF is a natural for PC-based image processing.
  20127. The goals of the TIFF specification are extensibility, portability, and
  20128. revisability. TIFF must be extensible in the future. TIFF must be able to
  20129. adapt to new types of images and data and must be portable between different
  20130. computers, processors, and operating systems. TIFF must be revisable -- it is
  20131. not a read-only format. Software systems should be able to edit, process, and
  20132. change TIFF files.
  20133. The tag in Tag Image File Format refers to the file's basic structure. A TIFF
  20134. tag provides information about the image, such as its width, length, and
  20135. number of pixels. Tags are organized in tag directories. Tag directories have
  20136. no set length or number, since pointers lead from one directory to another.
  20137. The result is a flexible file format that can grow and survive in the future.
  20138. Figure 3 contains the existing standard tags.
  20139.  
  20140.  
  20141. Image Data Basics
  20142.  
  20143.  
  20144. An image consists of a two-dimensional array of numbers. The color or gray
  20145. shade displayed for a given picture element (pixel) depends on the number
  20146. stored in the array for that pixel. The simplest type of image data is black
  20147. and white. It is a binary image since each pixel is either 0 or 1. You should
  20148. scan line drawings as binary images because you will save storage space and
  20149. because you will not lose any information in the process.
  20150. The next more complex type of image data is grayscale, where each pixel takes
  20151. on a value between 0 and the number of gray scales or gray levels that the
  20152. scanner can record. These images appear like common black-and-white
  20153. photographs -- they are black, white, and shades of gray. Some scanners record
  20154. only 16 shades of gray, sufficient for simple applications. In more serious
  20155. work, however, you should try to use scanners that produce 256 shades of gray.
  20156. Since the average person can distinguish about 40 shades of gray, a 16-shade
  20157. image may appear rough, while a 256-shade image "looks like a photograph."
  20158. This series of articles will concentrate on grayscale images.
  20159. The most complex type of image is color. Color images are similar to grayscale
  20160. except that there are three bands, or channels, corresponding to the additive
  20161. primary colors red, green, and blue. Thus, each pixel has three values
  20162. associated with it. A color scanner uses red, green, and blue filters to
  20163. produce those values.
  20164. You can obtain TIFF images by using a desktop scanner and your own
  20165. photographs. Color photos work reasonably well, but black-and-white photos are
  20166. better. I have used the Hewlett-Packard ScanJet and ScanJet Plus with good
  20167. results. The code disk for this issue contains several TIFF images. (See page
  20168. 80 to order. --Ed.) The ScanJet is limited to 16 shades of gray. The ScanJet
  20169. Plus gives up to 256 shades of gray. Buying your own scanner (about $1,500) is
  20170. not necessary since many printers and copy services now support desktop
  20171. publishing with scanners. They will typically charge $10 to $20 per scan. Some
  20172. PC dealers sell scanners and will also provide this service.
  20173. Be prepared to fail on your first try at scanning photos. The biggest problem
  20174. is file size. The scanner can scan 300 dots per inch, so a 3x5 photo at 300
  20175. dpi provides 900x1500 pixels. At eight bits per pixel (256 shades of gray) the
  20176. image file comes to over 1,350,000 bytes. This is not practical for many
  20177. applications. Instead, select a one- or two-inch square in your photo and work
  20178. with it. The scanning software allows you to preview the scanned photo and to
  20179. select just the area you want scanned onto disk, the resolution in dots per
  20180. inch, and the number of gray shades.
  20181.  
  20182.  
  20183. TIFF Specifics
  20184.  
  20185.  
  20186. Figure 1 (a) shows the structure of TIFF. The first eight bytes of the file
  20187. are the header. These eight bytes have the same format on all TIFF files. They
  20188. are the only items set in concrete for TIFF files. The remainder of the file
  20189. differs from image to image. Figure 1 (b) shows the IFD or Image File
  20190. Directory, which contains the number of directory entries and the directory
  20191. entries themselves. Figure 1 (c) shows the structure of each directory entry.
  20192. Each entry contains a tag indicating what type of information the file holds,
  20193. the data type of the information, the length of the information, and a pointer
  20194. to the information or the information itself.
  20195. Figure 2 shows the beginning of a TIFF file. The addresses are located on the
  20196. left side in decimal, and the bytes and their valves are in the table in hex.
  20197. Glancing between Figure 1 and Figure 2 should clarify the structure. The first
  20198. eight bytes are the header. Bytes zero and one tell whether the file stores
  20199. numbers most significant byte (MSB) or least significant byte (LSB) first. If
  20200. bytes zero and one are II (0x4949), then the least significant byte is first
  20201. (predominant in the PC world). If the value is MM (0x4D4D), then the most
  20202. significant byte is first (predominant in the Macintosh world). Your software
  20203. needs to read both formats.
  20204. The example in Figure 2 shows LSB first. Bytes two and three give the TIFF
  20205. version number, which should be 42 (0x2A) in all TIFF images. Bytes four to
  20206. seven give the offset to the first image file directory (IFD). Note that all
  20207. offsets in TIFF indicate locations with respect to the beginning of the file.
  20208. The first byte in the file has the offset 0. The offset in Figure 2 is 8, so
  20209. the IFD begins in the ninth byte of the file.
  20210.  
  20211.  
  20212. The IFD
  20213.  
  20214.  
  20215. The content of address eight is 27, indicating that this file has 27 12-byte
  20216. directory entries. The first two bytes of the entry contain the tag, which
  20217. tells the type of information the entry contains. The directory entry at
  20218. location zero (Figure 2) contains tag=255. This tag tells the file type.
  20219. (Refer to Figure 3 for possible tags.) The next two bytes of the entry give
  20220. the data type of the information (Figure 4 lists the possible data types and
  20221. their lengths). Directory entry zero in Figure 2 is type 3, a short (two-byte
  20222. unsigned integer). The next four bytes of the entry give the length of the
  20223. information. This length is not in bytes, but rather in multiples of the data
  20224. type. If the data type is a short and the length is 1, then the length is one
  20225. short, or two bytes. An entry's final four bytes give either the value of the
  20226. information or a pointer to the value. If the size of the information is four
  20227. bytes or less, then the information is stored here. If it is longer than four
  20228. bytes, then a pointer to it is stored. The information in directory entry zero
  20229. is two bytes long and is stored here with a value of 1. (This value has no
  20230. meaning for this tag.)
  20231. As for the next two entries, the first entry has tag=256. This is the image
  20232. width of the image in number of columns. The type is short and the length of
  20233. the value is one short, or two bytes. The value 600 means that there are 600
  20234. columns in the image. The second entry has tag=257. This is the image length
  20235. or height in number of rows. The type is short, the length is 1, and the value
  20236. is 602, meaning that the image has 602 rows.
  20237. You continue through the directory entries until you reach the offset to the
  20238. next IFD. If this offset is 0, as in Figure 2, then no more IFDs follow in the
  20239. file.
  20240.  
  20241.  
  20242. The Code
  20243.  
  20244.  
  20245. The code in Listing 1, Listing 2, and Listing 3 read image data from a TIFF
  20246. file into a 100x100 array of shorts. The code can read four or eight bit gray
  20247. scale data either most significant byte first or least significant byte first.
  20248. Future articles will build CIPS (the C Image Processing System) on top of
  20249. these routines.
  20250.  
  20251. Listing 1 (cips.h) contains the #include files and the data structures. The
  20252. structure tiff_header_struct holds the essential tags we must extract from the
  20253. TIFF header.
  20254. The function read_tiff_header in Listing 2 first determines whether the file
  20255. uses LSB first or MSB first since the method used influences the manner in
  20256. which the functions extract_long_from_buffer and extract_short_from_buffer
  20257. read the remainder of the file header. Next, the offset to the Image File
  20258. Directory is read. The next section seeks to the IFD and reads the entry
  20259. count, or number of entries in the IFD. Finally, the code loops over the
  20260. number of entries. It reads each entry and picks out the necessary tags. The
  20261. essential information is the width and length of the image, the bits per pixel
  20262. (four-bit or eight-bit data), and the offset to the start of the data.
  20263. The function read_tiff_image in Listing 3 uses read_tiff_header and the header
  20264. information to read data into a 100x100 array of shorts. The code seeks to the
  20265. beginning of the data and then to the first line to read. The code in the for
  20266. loop seeks to the first element on the line, reads the line, and seeks to the
  20267. end of the line. Each seek depends on the number of bits per pixel. If the
  20268. file uses eight bits per pixel, then the seeks are integer multiples of the
  20269. number of pixels. If the files uses four bits per pixel, the seeks are half as
  20270. long. The function read_line reads the image data into a buffer, then places
  20271. the data into the array of shorts. read_line uses unions defined in cips.h and
  20272. also depends on the number of bits per pixel.
  20273. The next article in this series will introduce the framework for the C Image
  20274. Processing System and describe how to display images.
  20275. References
  20276. 1. TIFF Developer's Toolkit, Aldus Corporation, 1988.
  20277. Figure 1. Structure of the Tag Image File Format.
  20278. Figure 2
  20279. address contents
  20280. (decimal) (hex)
  20281.  header
  20282.  0 49 49
  20283.  2 2A 00
  20284.  4 08 00 00 00
  20285.  
  20286.  IFD
  20287.  8 1B 00
  20288.  0th directory entry
  20289.  10 FF 00 tag=255
  20290.  12 03 00 type=3 (short)
  20291.  14 01 00 00 00 length=1
  20292.  18 01 00 00 00 value=1
  20293.  
  20294.  1rst directory entry
  20295.  22 00 01 tag=256
  20296.  24 03 00 type=3 (short)
  20297.  26 01 00 00 00 length=1
  20298.  30 58 02 00 00 value=600
  20299.  2nd directory entry
  20300.  34 01 01 tag=257
  20301.  36 03 00 type=3 (short)
  20302.  38 01 00 00 00 length=1
  20303.  42 5A 02 00 00 value=602
  20304.  .
  20305.  .
  20306.  .
  20307.  offset to next IFD
  20308.  334 00 00 00 00
  20309.  offset=0 so there are no more IFD's
  20310. Figure 3
  20311. SubfileType
  20312.  Tag = 255 (FF) Type = short N = 1
  20313.  Indicates the kind of data in the subfile.
  20314. -----------------------------------------------------------------------------
  20315. ImageWidth
  20316.  Tag = 256 (100) Type = short N = 1
  20317.  The width (x or horizontal) of the image in pixels.
  20318. -----------------------------------------------------------------------------
  20319. ImageLength
  20320.  Tag = 257 (101) Type = short N = 1
  20321.  The length (y or height or vertical) of the image in pixels.
  20322. -----------------------------------------------------------------------------
  20323. RowsPerStrip
  20324.  Tag = 278 (116) Type = long N = 1
  20325.  The number of rows per strip. The default is the entire image in one strip.
  20326. -----------------------------------------------------------------------------
  20327. StripOffsets
  20328.  Tag = 273 (111) Type = short or long N = strips per image
  20329.  The byte offset for each strip.
  20330.  
  20331. -----------------------------------------------------------------------------
  20332. StripByteCounts
  20333.  Tag = 279 (117) Type = long N = 1
  20334.  The number of bytes in each strip.
  20335. -----------------------------------------------------------------------------
  20336. SamplesPerPixel
  20337.  Tag = 277 (115) Type = short N = 1
  20338.  The number of samples per pixel (1 for monochrome data, 3 for color).
  20339. -----------------------------------------------------------------------------
  20340. BitsPerSample
  20341.  Tag = 258 (102) Type = short N = SamplesPerPixel
  20342.  The number of bits per pixel. 2**BitsPerSample = # of gray levels.
  20343. -----------------------------------------------------------------------------
  20344. Figure 4
  20345. Type Length of the Type
  20346. ----------------------------------------------------
  20347. 1 = byte 8 bit unsigned integer
  20348. 2 = ASCII 8 bit bytes that store ASCII codes
  20349.  (the last byte must be null)
  20350. 3 = short 16 bit (2 byte) unsigned integer
  20351. 4 = long 32 bit (4 byte) unsigned integer
  20352. 5 = rational Two longs: The first is the numerator,
  20353.  the second is the denominator
  20354.  
  20355. Listing 1 (cips.h)
  20356. /*********************************************
  20357. * file d:\cips\cips.h
  20358. *
  20359. * Functions: This file contains no functions.
  20360. * It contains declarations of the data structures
  20361. * used by the C Image Processing Systems CIPS.
  20362. *
  20363. * Purpose: To declare data structures.
  20364. *
  20365. * Modifications: created June 1990
  20366. **********************************************/
  20367.  
  20368. #include "d:\c600\include\stdio.h"
  20369. #include "d:\c600\include\graph.h"
  20370. #include "d:\c600\include\io.h"
  20371. #include "d:\c600\include\fcntl.h"
  20372. #include "d:\c600\include\dos.h"
  20373. #include "d:\c600\include\math.h"
  20374. #include "d:\c600\include\sys\types.h"
  20375. #include "d:\c600\include\sys\stat.h"
  20376.  
  20377. #define MAX_NAME_LENGTH 80
  20378. #define ROWS 100
  20379. #define COLS 100
  20380. #define GRAY_LEVELS 255
  20381.  
  20382. /**********************************************
  20383. * The following struct defines the information
  20384. * you need to read from the tiff file
  20385. * header.
  20386. **********************************************/
  20387.  
  20388. struct tiff_header_struct{
  20389. short lsb;
  20390.  
  20391. long bits_per_pixel;
  20392. long image_length;
  20393. long image_width;
  20394. long strip_offset;
  20395. };
  20396.  
  20397. /****************************************
  20398. * The following four unions are used
  20399. * to put the bytes from the header
  20400. * into either an integer or a floating
  20401. * point number.
  20402. ****************************************/
  20403.  
  20404. union short_char_union{
  20405. short s_num;
  20406. char s_alpha[2];
  20407. };
  20408.  
  20409. union int_char_union{
  20410. int i_num;
  20411. char i_alpha[2];
  20412. };
  20413.  
  20414. union long_char_union{
  20415. long l_num;
  20416. char l_alpha[4];
  20417. };
  20418.  
  20419. union float_char_union{
  20420. float f_num;
  20421. char f_alpha[4];
  20422. };
  20423.  
  20424.  
  20425. Listing 2 (tiff.c)
  20426. /***********************************************
  20427. * file d:\cips\tiff.c
  20428. *
  20429. * Functions: This file contains
  20430. * read_tiff_header
  20431. * extract_long_from_buffer
  20432. * extract_short_from_buffer
  20433. *
  20434. * Purpose: This file contains the subroutines
  20435. * that read the tiff files header information.
  20436. *
  20437. * External Calls:
  20438. * mof.c - my_open_file
  20439. * mrw.c - my_read
  20440. *
  20441. * Modifications: created 23 June 1990
  20442. ***********************************************/
  20443.  
  20444. #include "d:\cips\cips.h"
  20445.  
  20446. read_tiff_header(file_name, image_header)
  20447. char file_name[];
  20448. struct tiff_header_struct *image_header;
  20449. {
  20450.  
  20451. char buffer[12];
  20452.  
  20453. int bytes_read,
  20454. closed,
  20455. file_desc,
  20456. i,
  20457. j,
  20458. lsb,
  20459. not_finished;
  20460.  
  20461. long bits_per_pixel,
  20462. image_length,
  20463. image_width,
  20464. length_of_field,
  20465. offset_to_ifd,
  20466. position,
  20467. strip_offset,
  20468. subfile,
  20469. value;
  20470.  
  20471. short entry_count,
  20472. field_type,
  20473. s_bits_per_pixel,
  20474. s_image_length,
  20475. s_image_width,
  20476. s_strip_offset,
  20477. tag_type;
  20478.  
  20479. file_desc = my_open(file_name);
  20480.  
  20481. /*************************************
  20482. * Determine if the file uses MSB
  20483. * first or LSB first
  20484. *************************************/
  20485.  
  20486. bytes_read = my_read(file_desc, buffer, 8);
  20487.  
  20488. if(buffer[0] == 0x49)
  20489. lsb = 1;
  20490. else
  20491. lsb = 0;
  20492.  
  20493. /*************************************
  20494. * Read the offset to the IFD
  20495. *************************************/
  20496.  
  20497. extract_long_from_buffer(buffer, lsb, 4,
  20498. &offset_to_ifd);
  20499.  
  20500. not_finished = 1;
  20501. while(not_finished){
  20502.  
  20503. /************************************
  20504. * Seek to the IFD and read the
  20505. * entry_count, i.e. the number of
  20506. * entries in the IFD.
  20507. *************************************/
  20508.  
  20509. position = lseek(file_desc, offset_to_ifd, 0);
  20510.  
  20511. bytes_read = my_read(file_desc, buffer, 2);
  20512. extract_short_from_buffer(buffer, lsb, 0,
  20513. &entry_count);
  20514.  
  20515. /***************************************
  20516. * Now loop over the directory entries.
  20517. * Look only for the tags we need. These are:
  20518. * ImageLength
  20519. * ImageWidth
  20520. * BitsPerPixel(BitsPerSample)
  20521. * StripOffset
  20522. ***************************************/
  20523.  
  20524. for(i=0; i<entry_count; i++){
  20525. bytes_read = my_read(file_desc, buffer, 12);
  20526. extract_short_from_buffer(buffer, lsb, 0,
  20527. &tag_type);
  20528.  
  20529. switch(tag_type){
  20530. case 255: /* Subfile Type */
  20531. extract_short_from_buffer(buffer, lsb, 2,
  20532. &field_type);
  20533. extract_short_from_buffer(buffer, lsb, 4,
  20534. &length_of_field);
  20535. extract_long_from_buffer(buffer, lsb, 8,
  20536. &subfile);
  20537. break;
  20538.  
  20539. case 256: /* ImageWidth */
  20540. extract_short_from_buffer(buffer, lsb, 2,
  20541. &field_type);
  20542. extract_short_from_buffer(buffer, lsb, 4,
  20543. &length_of_field);
  20544. extract_long_from_buffer(buffer, lsb, 8,
  20545. &subfile);
  20546. break;
  20547. case 256: /* ImageWidth */
  20548. extract_short_from_buffer(buffer, lsb, 2,
  20549. &field_type);
  20550. extract_short_from_buffer(buffer, lsb, 4,
  20551. &length_of_field);
  20552. if(field_type == 3){
  20553. extract_short_from_buffer(buffer, lsb, 8,
  20554. &s_image_width);
  20555. image_width = s_image_width;
  20556. }
  20557. else
  20558. extract_long_from_buffer(buffer, lsb, 8,
  20559. &image_width);
  20560. break;
  20561. case 257: /* ImageLength */
  20562. extract_short_from_buffer(buffer, lsb, 2,
  20563. &field_type);
  20564. extract_short_from_buffer(buffer, lsb, 4,
  20565. &length of_field);
  20566. if(field_type == 3){
  20567. extract_short_from_buffer(buffer, lsb, 8,
  20568. &s_image_length);
  20569. image_length = s_image_length;
  20570.  
  20571. }
  20572. else
  20573. extract_long_from_buffer(buffer, lsb, 8,
  20574. &image_length);
  20575. break;
  20576. case 258: /* BitsPerPixel */
  20577. extract_short_from_buffer(buffer, lsb, 2,
  20578. &field_type);
  20579. extract_short_from_buffer(buffer, lsb, 4,
  20580. &length_of_field);
  20581. if(field_type == 3){
  20582. extract_short_from_buffer(buffer, lsb, 8,
  20583. &s_bits_per_pixel);
  20584. bits_per_pixel = s_bits_per_pixel;
  20585. }
  20586. else
  20587. extract_long_from_buffer(buffer, lsb, 8,
  20588. &bits_per_pixel);
  20589. break;
  20590.  
  20591. case 273: /* StripOffset */
  20592. extract_short_from_buffer(buffer, lsb, 2,
  20593. &field_type);
  20594. extract_short_from_buffer(buffer, lsb, 4,
  20595. &length_of_field);
  20596. if(field_type == 3){
  20597. extract_short_from_buffer(buffer, lsb, 8,
  20598. &s_strip_offset);
  20599. strip_offset = s_strip_offset;
  20600. }
  20601. else
  20602. extract_long_from_buffer(buffer, lsb, 8,
  20603. &strip_offset);
  20604. break;
  20605.  
  20606. default:
  20607. break;
  20608.  
  20609. } /* ends switch tag_type */
  20610. } /* ends loop over i directory entries */
  20611.  
  20612. bytes_read = my_read(file_desc, buffer, 4);
  20613. extract_long_from_buffer(buffer, lsb, 0,
  20614. &offset_to_ifd);
  20615. if(offset_to_ifd == 0) not_finished = 0;
  20616.  
  20617. } /* ends while not_finished */
  20618.  
  20619. image_header->lsb = lsb;
  20620. image_header->bits_per_pixel = bits_per_pixel;
  20621. image_header->image_length = image_length;
  20622. image_header->image_width = image_width;
  20623. image_header->strip_offset = strip_offset;
  20624.  
  20625. closed = close(file_desc);
  20626.  
  20627. } /* ends read_tiff_header */
  20628.  
  20629. /****************************************
  20630.  
  20631. * extract_long_from_buffer(...
  20632. *
  20633. * This takes a four byte long out of a
  20634. * buffer of characters. It is important
  20635. * to know the byte order LSB or MSB.
  20636. ****************************************/
  20637.  
  20638. extract_long_from_buffer(buffer, lsb, start, number)
  20639. char buffer[];
  20640. int lsb, start;
  20641. long *number;
  20642. {
  20643. int i;
  20644. union long_char_union lcu;
  20645.  
  20646. if(lsb == 1){
  20647. lcu.l_alpha[0] = buffer[start+0];
  20648. lcu.l_alpha[1] = buffer[start+1];
  20649. lcu.l_alpha[2] = buffer[start+2];
  20650. lcu.l_alpha[3] = buffer[start+3];
  20651. } /* ends if lsb = 1 */
  20652.  
  20653. if(lsb == 0){
  20654. lcu.l_alpha[0] = buffer[start+3];
  20655. lcu.l_alpha[1] = buffer[start+2];
  20656. lcu.l_alpha[2] = buffer[start+1];
  20657. lcu.l_alpha[3] = buffer[start+0];
  20658. } /* ends if lsb = 0 */
  20659.  
  20660. *number = lcu.l_num;
  20661.  
  20662. } /* ends extract_long_from_buffer */
  20663.  
  20664. /****************************************
  20665. * extract_short_from_buffer(...
  20666. *
  20667. * This takes a two byte short out of a
  20668. * buffer of characters. It is important
  20669. * to know the byte order LSB or MSB.
  20670. ****************************************/
  20671.  
  20672. extract_short_from_buffer(buffer, lsb, start, number)
  20673. char buffer[];
  20674. int lsb, start;
  20675. short *number;
  20676. {
  20677.  
  20678. int i;
  20679. union short_char_union_lcu;
  20680.  
  20681. if(lsb == 1){
  20682. lcu.s_alpha[0] = buffer[start+0];
  20683. lcu.s_alpha[1] = buffer[start+1];
  20684. } /* ends if lsb = 1 */
  20685.  
  20686. if(lsb == 0){
  20687. lcu.s_alpha[0] = buffer[start+1];
  20688. lcu.s_alpha[1] = buffer[start+0];
  20689. } /* ends if lsb = 0 */
  20690.  
  20691.  
  20692. *number = lcu.s_num;
  20693.  
  20694. } /* ends extract_short_from_buffer */
  20695.  
  20696.  
  20697. LISTING 3 (rtiff.c)
  20698. /*********************************************
  20699. * file d:\cips\rtiff.c
  20700. *
  20701. * Functions: This file contains
  20702. * read_tiff_image
  20703. * read_line
  20704. * seek_to_first line
  20705. * seek_to_end_of_line
  20706. *
  20707. * Purpose: These functions read a TIFF image
  20708. * and insert the data into a ROWSxCOLS array
  20709. * of short.
  20710. *
  20711. * External Calls:
  20712. * mof.c - my_open
  20713. * mrw.c - my_read
  20714. * tiff.c - read_tiff_header
  20715. *
  20716. * Modifications: created 25 June 1990
  20717. *********************************************/
  20718.  
  20719. #include "d:\cips\cips.h"
  20720.  
  20721. read_tiff_image(image_file_name, array, il, ie, ll, le)
  20722. char image_file_name[];
  20723. int il, ie, ll, le;
  20724. short array[ROWS] [COLS];
  20725. {
  20726. char buffer[100],
  20727. rep[80];
  20728. int bytes_read,
  20729. closed,
  20730. file_descriptor,
  20731. i;
  20732. float a;
  20733. long line_length, offset;
  20734. unsigned long position;
  20735. struct tiff_header_struct image_header;
  20736. read_tiff_header(image_file_name, &image_header);
  20737.  
  20738. /****************************************************
  20739. * Procedure:
  20740. * Seek to the strip offset where the data begins.
  20741. * Seek to the first line you want.
  20742. * Loop over the lines you want to read:
  20743. * Seek to the first element of the line.
  20744. * Read the line.
  20745. * Seek to the end of the data in that line.
  20746. ****************************************************/
  20747.  
  20748. file_descriptor = my_open(image_file_name);
  20749. position = lseek(file_descriptor,
  20750.  
  20751. image_header.strip_offset, 0);
  20752. position = seek_to_first_line(file_descriptor,
  20753. &image_header, il);
  20754.  
  20755. for(i=0; i<(ll-il); i++){
  20756. offset = (ie-1)/(8/image_header.bits_per_pixel);
  20757. position = lseek(file_descriptor, offset, 1);
  20758. bytes_read = read_line(file_descriptor, array, i,
  20759. &image_header, ie, le);
  20760. position = seek_to_end_of_line(file_descriptor,
  20761. le, &image_header);
  20762. position = lseek(file_descriptor, 1, 1); /*???*/
  20763. } /* ends loop over i */
  20764.  
  20765. closed = close(file_descriptor);
  20766.  
  20767. } /* ends read_tiff_image */
  20768.  
  20769. /*******************************************************
  20770. * read_line(...
  20771. *
  20772. * This function reads bytes from the TIFF file into
  20773. * a buffer, extracts the numbers from that buffer,
  20774. * and puts them into a 100x100 array of shorts.
  20775. * The process depends on the number of bits per
  20776. * pixel used in the file (4 or 8).
  20777. *******************************************************/
  20778.  
  20779. read_line(file_descriptor, array, line_number,
  20780. image_header, ie, le)
  20781. int file_descriptor, ie, le, line_number;
  20782. short array[ROWS][COLS];
  20783. struct tiff_header_struct *image_header;
  20784.  
  20785. {
  20786. char buffer[100], first, second;
  20787. float a, b;
  20788. int bytes_read, i;
  20789. unsigned int bytes_to_read;
  20790. union short_char_union scu;
  20791.  
  20792. for(i=0; i<100; i++)
  20793. buffer[i] = '\0';
  20794.  
  20795. /**********************************************
  20796. * Use the number of bits per pixel to calculate
  20797. * how many bytes to read.
  20798. *************************************************/
  20799.  
  20800. bytes_to_read = (le-ie)/(8/image_header->bits_per_pixel);
  20801. bytes_read = read(file_descriptor, buffer,
  20802. bytes_to_read);
  20803.  
  20804. for(i=0; i<bytes_read; i++){
  20805.  
  20806. /**********************************************
  20807. * Use unions defined in cips.h to stuff bytes
  20808. * into shorts.
  20809. ************************************************/
  20810.  
  20811.  
  20812. if(image_header->bits_per_pexil == 8){
  20813. scu.s_num = 0;
  20814. scu.s_alpha[0] = buffer[i];
  20815. array[line_number] [i] = scu.s_num;
  20816. } /* ends if bits_per_pixel == 8 */
  20817.  
  20818. if(image_header->bits_per_pixel == 4){
  20819.  
  20820. scu.s_num = 0;
  20821. second = buffer[i] & 0X000F;
  20822. scu.s_alpha[0] = second;
  20823. array[line_number] [i*2+1] = scu.s_num;
  20824.  
  20825. scu.s_num = 0;
  20826. first = buffer[i] >> 4;
  20827. first = first & 0x000F;
  20828. scu.s_alpha[0] = first;
  20829. array[line_number] [i*2] = scu.s_num;
  20830.  
  20831. } /* ends if bits_per_pixel == 4 */
  20832. } /* ends loop over i */
  20833.  
  20834. return(bytes_read);
  20835. } /* ends read_line */
  20836.  
  20837. /*****************************************
  20838. * seek_to_first_line(...
  20839. ******************************************/
  20840.  
  20841. seek_to_first_line(file_descriptor, image_header, il)
  20842. int file_descriptor, il;
  20843. struct tiff_header_struct *image_header;
  20844. {
  20845. long offset;
  20846. unsigned long position;
  20847.  
  20848. offset = (il-1)*image_header->image_width/
  20849. (8/image_header->bits_per_pixel);
  20850. /* seek from current position */
  20851. position = lseek(file_descriptor, offset, 1);
  20852. return(position);
  20853. ) /* ends seek_to_first_line */
  20854.  
  20855. /*****************************************
  20856. * seek_to_end_of_line(...
  20857. *******************************************/
  20858.  
  20859. seek_to_end_of_line(file_descriptor, le, image_header)
  20860. int file_descriptor, le;
  20861. struct tiff_header_struct *image_header;
  20862. {
  20863. int origin;
  20864. long offset;
  20865. unsigned long position;
  20866.  
  20867. offset = (image_header->image_width-le)/
  20868. (8/image_header->bits_per_pixel);
  20869. origin = 1; /* seek from the current position */
  20870.  
  20871. position = lseek(file_descriptor, offset, origin);
  20872. return(position);
  20873. } /* ends seek_to_end_of_line */
  20874.  
  20875.  
  20876.  
  20877.  
  20878.  
  20879.  
  20880.  
  20881.  
  20882.  
  20883.  
  20884.  
  20885.  
  20886.  
  20887.  
  20888.  
  20889.  
  20890.  
  20891.  
  20892.  
  20893.  
  20894.  
  20895.  
  20896.  
  20897.  
  20898.  
  20899.  
  20900.  
  20901.  
  20902.  
  20903.  
  20904.  
  20905.  
  20906.  
  20907.  
  20908.  
  20909.  
  20910.  
  20911.  
  20912.  
  20913.  
  20914.  
  20915.  
  20916.  
  20917.  
  20918.  
  20919.  
  20920.  
  20921.  
  20922.  
  20923.  
  20924.  
  20925.  
  20926.  
  20927.  
  20928.  
  20929.  
  20930.  
  20931.  
  20932.  
  20933.  
  20934. Standard C
  20935.  
  20936.  
  20937. The Header <locale.h>
  20938.  
  20939.  
  20940.  
  20941.  
  20942. P.J. Plauger
  20943.  
  20944.  
  20945. P.J. Plauger is senior editor of The C Users Journal. He is secretary of the
  20946. ANSI C standards committee, X3J11, and convenor of the ISO C standards
  20947. committee, WG14. His latest book is Standard C, which he co-authored with Jim
  20948. Brodie. You can reach him at pjp@plauger. uunet.
  20949.  
  20950.  
  20951.  
  20952.  
  20953. History
  20954.  
  20955.  
  20956. The header <locale.h> is an invention of X3J11, the committee that developed
  20957. the C standard. You will find little that resembles locales in earlier
  20958. implementations of C. That stands at odds with the committee's stated purpose,
  20959. "to codify existing practice." Nevertheless, those of us active within X3Jll
  20960. at that time felt we were acting out of the best of motives -- self defense.
  20961. This particular header popped up about five years after work began on the
  20962. standard. At that time, many of us felt that the standard was essentially
  20963. complete. We were simply putting a few finishing touches on a product in which
  20964. we had invested five years of our lives. Resistance was mounting to change of
  20965. any sort.
  20966. I recall mentioning a change that I would have liked. (I forget now just what
  20967. the change was.) In the interest of speeding closure, however, I suggested
  20968. that the committee not make the change. An attendee from the UK, Keith Winter,
  20969. also expressed support for the change. But he, too, was willing to let it
  20970. slide. After all, he said, it was just one of many changes that would have to
  20971. be made to the ISO standard for C.
  20972. Silence.
  20973. After we collected our collective wits, several of us simultaneously uttered
  20974. the moral equivalent of, "Say what?" Winter went on to explain calmly that a
  20975. number of Europeans were unhappy with certain parts of the C standard being
  20976. developed by X3J11. It was simply too American in several critical ways. They
  20977. despaired of trying to educate us insular Yanks about the needs of the world
  20978. marketplace. Rather, they were content to wait and fight their battles on a
  20979. more congenial field. The Europeans took it for granted that an ISO standard
  20980. for C must differ from the ANSI standard.
  20981. Many of us disagreed with that position. We felt it imperative that whatever
  20982. standard ANSI developed had to be acceptable to the international community.
  20983. We had seen the effects in the past of computer language standards that
  20984. differed around the world. Our five years of effort would be in vain, we felt,
  20985. if the final word on C came from a separate committee second-guessing all our
  20986. decisions.
  20987. So we sighed a deep sigh and asked the Europeans to show us their shopping
  20988. list of changes. Most of the items on the list dealt with ways to adapt C
  20989. programs to different cultures. That is a much more obvious problem in a land
  20990. of many languages and nations such as Europe. Americans enjoy the luxury of a
  20991. single (official) language and a fairly simple alphabet.
  20992. AT&T Bell Labs went so far as to host a special meeting to deal with various
  20993. issues of internationalization. (This is a big word that people are uttering
  20994. more and more often. It seems to have no acceptable synonym that is any
  20995. shorter. The techie solution is to introduce the barbarism I18N, pronounced
  20996. EYE eighteen EN. The 18 stands for the number of letters omitted.) Out of that
  20997. meeting came the proposal for adding locale support to Standard C. The
  20998. machinery eventually adopted is remarkably close to the original proposal.
  20999. Adding locales to C had the desired effect. Many of the objections to ANSI C
  21000. as an international standard were derailed. It cost X3Jll an extra year, by my
  21001. estimation, to hammer out locales. And we probably spent yet another year
  21002. dealing with residual gripes from the international community. (And WG14, the
  21003. ISO C standard committee, is still working on additions to the existing
  21004. Standard.) Nevertheless, we succeeded in producing a standard for C that is
  21005. currently identical at both ANSI and ISO levels.
  21006.  
  21007.  
  21008. Before Locales
  21009.  
  21010.  
  21011. Writing adaptive code is not entirely new. An early form sprung up about
  21012. fifteen years ago in the UNIX operating system. Folks got the idea of adding
  21013. environment variables to the system call that launches new processes. (That
  21014. service is called exec, or some variant thereof, in UNIX land.) Environment
  21015. variables are an open-ended set of names, each of which identifies a
  21016. null-terminated string that represents its value. You can add, alter, or
  21017. delete environment variables in a process. Should that process launch another
  21018. process, the environment variables are automatically copied into the image of
  21019. the new guy.
  21020. The new process can simply ignore environment variables. It loses a few dozen,
  21021. or a few hundred, bytes of storage that it might otherwise enjoy. Or it can
  21022. look for certain environment variables and study their current values. A
  21023. common variable is TZ, which provides information to the library date
  21024. functions about the current time zone. If the value of TZ is, say, EST05E0T,
  21025. the time functions know to label local standard time as EST and local daylight
  21026. savings time as EDT. The local (standard) time zone is five hours later than
  21027. UTC, known in the past as Greenwich Mean Time.
  21028. Environment variables have many uses. They are a great way to smuggle file
  21029. names into an application program. It is almost always a bad idea to wire file
  21030. names directly into a program. Prompting the user for file names is mostly a
  21031. good idea, except for secret files about which the user should not have to be
  21032. informed. Asking for such a file name on the command line that starts the
  21033. program is somewhat better, but it can be a nuisance. It is a particular
  21034. nuisance is several programs in a suite need access to the same file name.
  21035. That's why it is often much nicer to set an environment variable to the file
  21036. name once and for all in a script that starts a session. The file name is
  21037. captured in one place, but is made available to a whole hierarchy of programs.
  21038. If you are at all literate about MS-DOS, you probably know that that system
  21039. supports environment variables too. They are just one of many good ideas
  21040. borrowed from past experience with UNIX. I have purchased several bits of
  21041. commercial software that use environment variables to advantage. A common use
  21042. is to locate special directories that contain support files or that are
  21043. well-suited for hosting temporary files. But they have many other uses as
  21044. well.
  21045. The Standard C library includes the function getenv. You will find it declared
  21046. in the standard header <stdlib.h>. Call getenv with the name of an environment
  21047. variable and it will return a pointer to its value string, if there is one. It
  21048. is not considered an error to reference a variable that is not defined.
  21049. Note, however, that the Standard does not include setenv, the usual companion
  21050. to getenv. That is the common name for the function that lets you alter the
  21051. values associated with environment variables. Simply put, committee X3Jll
  21052. couldn't decide how to describe the semantics of setenv. They differ too much
  21053. among various single-user and multiprocessing systems. So you can write
  21054. portable code that reads environment variables, but you can't alter them in a
  21055. standard way.
  21056.  
  21057.  
  21058. Why Locales?
  21059.  
  21060.  
  21061. What do locales provide that environment variables do not? In a word,
  21062. structure. This is the era of object-oriented hoopla. So you can look on
  21063. locales, if you wish, as object-oriented environment variables. A single
  21064. locale provides information on many related parameters. The values are
  21065. consistent for a given culture. You would have to pump dozens of reserved
  21066. names into the name space for environment variables to transmit the same
  21067. amount of information. And you run a greater risk that subsets of the
  21068. information get altered inconsistently.
  21069. When I talk about a culture, by the way, I don't mean just a group that speaks
  21070. a common language. People in the USA write dates as 7/4/1776 (Independence
  21071. Day). The same day in the UK is written as 4/7/1776 (Thanksgiving Day). Even
  21072. within the USA, practices can vary. Where we civilians might write a debit as
  21073. $-123.45, an accountant may well prefer (123.45).
  21074. For this reason, and others, locales have substructure. You can set an entire
  21075. locale, or you can alter one or more categories. Separate categories exist for
  21076. controlling collation sequences, character classification, monetary
  21077. formatting, other numeric formatting, and times. The header <locale.h> defines
  21078. several macros with names such as LC_COLLATE and LC_TIME. Each expands to an
  21079. integer value that you can use as the category argument to setlocale, the
  21080. function that alters locales. An implementation can choose to provide
  21081. additional categories as well. A program that uses such added categories will,
  21082. of course, be less portable than one that does not.
  21083. The idea behind categories is that an application may wish to tailor its
  21084. locale. It may want to print dates in the local language and by the formatting
  21085. rules of that language. But it may still opt to use the dot for a decimal
  21086. point even though speakers of that language customarily write a comma. Or the
  21087. application may adapt completely to a given locale, then change the monetary
  21088. category to match a worldwide corporate standard for expressing accounting
  21089. information.
  21090. Much of the information provided in a locale is purely informative. C has
  21091. never treated currency amounts as a special data type. It is no surprise,
  21092. therefore, that the Standard C library is unaffected by a change in the
  21093. monetary category. On the other hand, some changes in locale very definitely
  21094. affect how certain library functions behave. If a culture uses a comma for a
  21095. decimal point, then the scanf family should accept commas and the printf
  21096. family (and strtod) should produce commas in the proper places. That is indeed
  21097. what happens.
  21098. Here are all the places where library behavior changes with locale:
  21099. The functions strcoll and strxfrm in <string.h> can change how they collate
  21100. when category LC_COLLATE changes.
  21101. The functions in <ctype.h>, the printf and scanf families, and the numeric
  21102. conversion functions in <stdlib.h>, can change how they test and alter certain
  21103. characters when category LC_CTYPE changes.
  21104. The multibyte character functions in <stdlib.h>, and the printf and scanf
  21105. families, can change how they parse and translate multibyte strings when
  21106. category LC_CTYPE changes.
  21107. The printf and scanf families, and atof and strtod in <stdlib.h>, can change
  21108. what they use for the decimal point character when category LC_NUMERIC
  21109. changes.
  21110. The strftime function in <time.h> can change how it converts times to
  21111. character strings when category LC_TIME changes.
  21112. The localeconv function in <locale.h> can change the information reported for
  21113. the current locale when categories LC_MONETARY or LC_NUMERIC change.
  21114.  
  21115.  
  21116.  
  21117. Using Locales
  21118.  
  21119.  
  21120. If you are half as nervous as I am, this litany of changes should scare you.
  21121. How do you write portable code if large chunks of the Standard C library can
  21122. change behavior underfoot? Can you ship code to Germany and know what isalpha
  21123. will do when it runs there? If you mix your code with functions from another
  21124. source, how much trouble can they cause? Each time your functions get control,
  21125. you may be running in a different locale. How do you code under those
  21126. conditions?
  21127. X3Jll anguished about such issues when we spelled out the behavior of locales.
  21128. We recognized that many people don't want to be bothered with this machinery
  21129. at all. Those folks should suffer little from the addition of locales. Still
  21130. others have only modest goals. They want to trade in the Americanisms wired
  21131. into older C for conventions more in tune with their culture. Still others are
  21132. ambitious. They want to write code that can be sold unchanged, in binary form,
  21133. in numerous markets. That code must be very sophisticated about changing
  21134. locales.
  21135. The simplest way to use locales is to ignore them. Every Standard C program
  21136. starts up in the "C" locale. In this locale, the traditional library functions
  21137. behave pretty much as they always have. islower returns a nonzero value only
  21138. for the 26 letters of the English alphabet, for example. The decimal point is
  21139. a dot. If your program never calls setlocale, none of this behavior can
  21140. change.
  21141. The next simplest way to use locales is to change once, just after program
  21142. startup, and leave it at that. The C Standard requires no other locale names
  21143. besides "C". But it does define a defalut locale designated by the empty
  21144. string "". If your program executes
  21145. setlocale (LC_ALL, "")
  21146. it should shift to this default locale. Presumably, each implementation will
  21147. devise a way to determine a default locale that pleases the locals. (An
  21148. implementation that doesn't care a hoot about locales can make the default
  21149. locale the same as the "C" locale, of course.)
  21150. You must be more careful in using the library, once the locale can change on
  21151. you. Some things get easier, such as displaying pretty dates or skipping the
  21152. appropriate characters for white space. Other things get chancier, such as
  21153. parsing strings with the functions in <ctype.h>. In a pinch, you can always
  21154. revert part or all of the locale to the "C" locale, as in:
  21155. char *s1 = setlocale(LC_CTYPE, "C");
  21156. char *s2 = malloc(strlen(s1) + 1);
  21157. if (s2 == NULL)
  21158. <despair>
  21159. strcpy(s2, s1);
  21160. <use ctype functions safely>
  21161. <setlocale(LC_ALL, s2);
  21162. You can omit the business about copying the locale string returned by
  21163. setlocale only if you are sure that no other calls to that function can
  21164. intervene between the two shown above.
  21165. I won't go into more sophisticated manipulation of locales at this point. That
  21166. must wait until we have covered some of the implementation issues raised so
  21167. far. You will find that they are dizzying enough in their own right.
  21168.  
  21169.  
  21170. What the Standard Says
  21171.  
  21172.  
  21173. If your primary goal is to query the current locale, you need to read at least
  21174. two chunks of the Standard. The first provides an introduction to the standard
  21175. header <locale.h>:
  21176.  
  21177.  
  21178. 4.4 Localization <locale.h>
  21179.  
  21180.  
  21181. The header <locale.h> declares two functions, one type, and defines several
  21182. macros.
  21183. The type is
  21184. struct lconv
  21185. which contains members related to the formatting of numeric values. The
  21186. structure shall contain at least the following members, in any order. The
  21187. semantics of the members and their normal ranges is explained in 4.4.2.1. In
  21188. the "C" locale, the members shall have the values specified in the comments.
  21189. char *decimal_point; /*"." */
  21190. char *thousands_sep; /* "" */
  21191. char *grouping; /* "" */
  21192. char *int_curr_symbol; /* "" */
  21193. char *currency_symbol; /* "" */
  21194. char *mon_decimal_point; /* "" */
  21195. char *mon_thousands_sep; /* "" */
  21196. char *mon_grouping; /* "" */
  21197. char *positive_sign; /* "" */
  21198. char *negative_sign; /* "" */
  21199. char int_frac_digits; /* CHAR_MAX */
  21200. char frac_digits; /* CHAR_MAX */
  21201. char p_cs_precedes; /* CHAR_MAX */
  21202. char p_sep_by_space; /* CHAR_MAX */
  21203. char n_cs_precedes; /* CHAR_MAX */
  21204. char n_sep by_space; /* CHAR_MAX */
  21205. char p_sign_posn; /* CHAR_MAX */
  21206. char n_sign_posn; /* CHAR_MAX */
  21207. The macros defined are NULL (described in 4.1.5); and
  21208. LC_ALL
  21209. LC_COLLATE
  21210. LC_CTYPE
  21211. LC_MONETARY
  21212. LC_NUMERIC
  21213.  
  21214. LC_TIME
  21215. which expand to integral constant expressions with distinct values, suitable
  21216. for use as the first argument to the setlocale function. Additional macro
  21217. definitions, beginning with the characters LC_ and an upper-case letter,100
  21218. may also be specified by the implementation. [end of excerpt]
  21219. The second chunk you must read is the description of localeconv, the function
  21220. that lets you query the current locale:
  21221.  
  21222.  
  21223. 4.4.2 Numeric Formatting Convention Inquiry
  21224.  
  21225.  
  21226.  
  21227.  
  21228. 4.4.2.1 The localeconv Function
  21229.  
  21230.  
  21231.  
  21232.  
  21233. Synopsis
  21234.  
  21235.  
  21236. #include <locale.h>
  21237. struct lconv *localeconv(void);
  21238.  
  21239.  
  21240. Description
  21241.  
  21242.  
  21243. The localeconv function sets the components of an object with type struct
  21244. lconv with values appropriate for the formatting of numeric quantities
  21245. (monetary and otherwise) according to the rules of the current locale.
  21246. The members of the structure with type char * are pointers to strings, any of
  21247. which (except decimal_point) can point to "", to indicate that the value is
  21248. not available in the current locale or is of zero length. The members with
  21249. type char are non-negative numbers, any of which can be CHAR_MAX to indicate
  21250. that the value is not available in the current locale. The members include the
  21251. following:
  21252. char *decimal_point
  21253. The decimal-point character used to format non-monetary quantities.
  21254. char *thousands_sep
  21255. The character used to separate groups of digits before the decimal-point
  21256. character in formatted non-monetary quantities.
  21257. char *grouping
  21258. A string whose elements indicate the size of each group of digits in formatted
  21259. non-monetary quantities.
  21260. char *int_curr_symbol
  21261. The international currency symbol applicable to the current locale. The first
  21262. three characters contain the alphabetic international currency symbol in
  21263. accordance with those specified in ISO 4217 Codes for the Representation of
  21264. Currency and Funds. The fourth character (immediately preceding the null
  21265. character) is the character used to separate the international currency symbol
  21266. from the monetary quantity.
  21267. char *currency_symbol
  21268. The local currency symbol applicable to the current locale.
  21269. char *mon_decimal_point
  21270. The decimal-point used to format monetary quantities.
  21271. char *mon_thousands_sep
  21272. The separator for groups of digits before the decimal-point in formatted
  21273. monetary quantities.
  21274. char *man_grouping
  21275. A string whose elements indicate the size of each group of digits in formatted
  21276. monetary quantities.
  21277. char *positive_sign
  21278. The string used to indicate a non-negative-valued formatted monetary quantity.
  21279. char *negative_sign
  21280. The string used to indicate a negative-valued formatted monetary quantity.
  21281. char int_frac_digits
  21282. The number of fractional digits (those after the decimal-point) to be
  21283. displayed in a internationally formatted monetary quantity.
  21284. char frac_digits
  21285. The number of fractional digits (those after the decimal-point) to be
  21286. displayed in a formatted monetary quantity.
  21287. char p_cs_precedes
  21288. Set to 1 or 0 if the currency_symbol respectively precedes or succeeds the
  21289. value for a non-negative formatted monetary quantity.
  21290. char p_sep_by_space
  21291. Set to 1 or 0 if the currency_symbol respectively is or is not separated by a
  21292. space from the value for a non-negative formatted monetary quantity.
  21293. char n_cs_precedes
  21294. Set to 1 or 0 if the currency_symbol respectively precedes or succeeds the
  21295. value for a negative formatted monetary quantity.
  21296. char n_sep_by_space
  21297. Set to 1 or 0 if the currency_symbol respectively is or is not separated by a
  21298. space from the value for a negative formatted monetary quantity.
  21299.  
  21300. char p_sign_posn
  21301. Set to a value indicating the positioning of the positive_sign for a
  21302. non-negative formatted monetary quantity.
  21303. char n_sign_posn
  21304. Set to a value indicating the positioning of the negative_sign for a negative
  21305. formatted monetary quantity.
  21306. The elements of grouping and mon_grouping are interpreted according to the
  21307. following:
  21308. CHAR_MAX No further grouping is to be performed.
  21309. 0 The previous element is to be repeatedly used for the remainder of the
  21310. digits.
  21311. other The integer value is the number of digits that comprise the current
  21312. group. The next element is examined to determine the size of the next group of
  21313. digits before the current group.
  21314. The value of p_sign_posn and n_sign_posn is interpreted according to the
  21315. following:
  21316. 0 Parentheses surround the quantity and currency_symbol.
  21317. 1 The sign string precedes the quantity and currency_symbol.
  21318. 2 The sign string succeeds the quantity and currency_symbol.
  21319. 3 The sign string immediately precedes the currency_symbol.
  21320. 4 The sign string immediately succeeds the currency_symbol.
  21321. The implementation shall behave as if no library function calls the localeconv
  21322. function.
  21323.  
  21324.  
  21325. Returns
  21326.  
  21327.  
  21328. The localeconv function returns a pointer to the filled-in object. The
  21329. structure pointed to by the return value shall not be modified by the program,
  21330. but may be overwritten by a subsequent call to the localeconv function. In
  21331. addition, calls to the setlocale function with categories LC_ALL, LC_MONETARY,
  21332. or LC_NUMERIC may overwrite the contents of the structure.
  21333. Footnote:
  21334. 100. See future library directions (4.13.3). [end of excerpt]
  21335.  
  21336.  
  21337. Future Attractions
  21338.  
  21339.  
  21340. Next month, I will discuss ways to implement locales. Since there is little or
  21341. no history in this area, I can be particularly inventive. That makes locales
  21342. particularly interesting to tinkerers like me. Internationalization is
  21343. becoming more important. That should make the topic of interest to many of
  21344. you. As for the rest of you, at least you can see how much trouble your code
  21345. will encounter when everyone starts altering locales under foot. 
  21346.  
  21347.  
  21348.  
  21349.  
  21350.  
  21351.  
  21352.  
  21353.  
  21354.  
  21355.  
  21356.  
  21357.  
  21358.  
  21359.  
  21360.  
  21361.  
  21362.  
  21363.  
  21364.  
  21365.  
  21366.  
  21367.  
  21368.  
  21369.  
  21370.  
  21371.  
  21372.  
  21373.  
  21374.  
  21375.  
  21376.  
  21377.  
  21378.  
  21379.  
  21380. Doctor C's Pointers(R)
  21381.  
  21382.  
  21383. Puzzles, Part V
  21384.  
  21385.  
  21386.  
  21387.  
  21388. Rex Jaeschke
  21389.  
  21390.  
  21391. Rex Jaeschke is an independent computer consultant, author and seminar leader.
  21392. He participates in both ANSI and ISO C Standards meetings and is the editor of
  21393. The Journal of C Language Translation, a quarterly publication aimed at
  21394. implementors of C language translation tools. Readers are encouraged to submit
  21395. column topics and suggestions to Rex at 2051 Swans Neck Way, Reston, VA 22091
  21396. or via UUCP at uunet!aussie!rex or aussie!rex@uunet.uu. net.
  21397.  
  21398.  
  21399. This month we'll complete our quality of implementation set of puzzles. Try to
  21400. debug them yourself first and see what messages your compiler produces.
  21401.  
  21402.  
  21403. The Puzzles
  21404.  
  21405.  
  21406. 1. I just know the @#$% file exists. See Listing 1.
  21407. 2. When is a string literal not literal?
  21408. /* 1*/#include <stdio.h>
  21409.  
  21410. /* 3*/main ()
  21411. /* 4*/{
  21412. /* 5*/ printf("?? (Error:
  21413. message) \n");
  21414. /* 6*/}
  21415. 3. Side-effects in unused arguments.
  21416. /* l*/#include <stdio.h>
  21417.  
  21418. /* 3*/main ()
  21419. /* 4*/{
  21420. /* 5*/ int i = 5, j = 6;
  21421. /* 7*/ printf("Hello\n",
  21422. ++i, j- -);
  21423. /* 8*/}
  21424. Output:
  21425. Hello
  21426. Are i and j incremented and decremented, respectively?
  21427. 4. It compiles, it works, so what's the problem?
  21428. /* 1*/#include <stdio.h>
  21429.  
  21430. /* 3*/main()
  21431. /* 4*/{
  21432. /* 5*/ void f();
  21433.  
  21434. /* 7*/ f(-32768);
  21435. /* 8*/}
  21436.  
  21437. /*10*/void f(i)
  21438. /*11*/int i;
  21439. /*12*/{
  21440. /*13*/ printf("i = %d\n", i);
  21441. /*14*/}
  21442. 5. Hey, someone stole my loop!
  21443.  
  21444. /* 1*/#include <stdio.h>
  21445.  
  21446. /* 3*/main()
  21447. /* 4*/{
  21448. /* 5*/ int i;
  21449.  
  21450. /* 7*/ for (i = 0; i < 5; ++i);
  21451. /* 8*/ printf("i = %d\n", i);
  21452. /* 9*/}
  21453. Output:
  21454. i = 5
  21455. 6. I forgot to declare the formal parameter list but it doesn't seem to mind!
  21456. /* 1*/double f(i, j)
  21457. /* 2*/{
  21458. /* 3*/ return i * j;
  21459. /* 4*/}
  21460.  
  21461.  
  21462. The Solutions
  21463.  
  21464.  
  21465. 1. As indicated in the comment, this is a DOS-specific problem. A file does
  21466. exist by the name of test.dat in the root directory of the default disk, but
  21467. you can't seem to get at it. The message produced, however, does indicate some
  21468. kind of problem. For some reason, the leading part of the filename is missing.
  21469. On closer inspection you realize that not only is the t missing but so too is
  21470. the \ and the two together mean something special in C.
  21471. Of course, the compiler sees the filename as a tab character followed by
  21472. est.dat. Why didn't the tab get displayed in the output? It did, but the tab
  21473. stops of the terminal were set such that the tab was not obvious.
  21474. The solution? Use \test.dat instead. Now the compiler sees no escape sequence.
  21475. Note that according to ANSI C, a different rule is applied with the #include
  21476. preprocessor directive. For example,
  21477. #include "\test.h"
  21478. causes the preprocessor to search for the file test.h in the root directory.
  21479. The \t is not interpreted as a tab. The grammar of this directive is specific
  21480. to the preprocessor -- the construct ". . ." must not be treated as a string
  21481. literal. It is simply a string of arbitrary characters delimited by double
  21482. quote characters. (ANSI C defines header names as quite separate tokens from
  21483. string literals. The actual grammar used here is "xxx" where xxx is called a
  21484. q-char-sequence.)
  21485. 2. It is not unreasonable to expect the following output:
  21486. ??(Error: message)
  21487. However, ANSI C requires:
  21488. [Error: message)
  21489. According to PC-Lint you have stumbled on one of ANSI C's quiet changes," an
  21490. instance where a correct program's behavior has been changed by the standard.
  21491. line 5 - Trigraph Sequence \
  21492. '??(' in literal (Quiet Change)
  21493. I will not discuss trigraphs except to say that a trigraph is a
  21494. three-character sequence beginning with ?? that permits certain punctuation
  21495. characters to have an alternative representation. ANSI C invented trigraphs to
  21496. allow C source to be mechanically converted to machines supporting the ISO-646
  21497. character set (which can have alternate graphics for #, , [, [, ], and \, for
  21498. example). Trigraphs are recognized before any tokens (such as string literals)
  21499. are processed.
  21500. 3. Yes. Before a function can be called, each of its arguments must be
  21501. evaluated. Then their values are put into the function's call frame. A
  21502. compiler is not required to know anything about any of the standard library
  21503. functions (although it is permitted to), so provided the actual argument list
  21504. is compatible with the function's prototype, the call is OK. In this case, the
  21505. ellipsis notation is used in the printf prototype so there's no conflict.
  21506. Interestingly, PC-Lint had the following to say:
  21507. line 7 - number of arguments \
  21508. inconsistent 'with format
  21509. That is, it really did check the number and type of trailing arguments against
  21510. the format string. This can be a very useful check, but it can only be
  21511. performed if the first argument is a string literal. The inconsistency would
  21512. not be detectable if the format argument were the name of a char array or a
  21513. pointer initialized at runtime.
  21514. 4. On the 16-bit compilers I tested, by far the most common output was
  21515. i = -32768
  21516. However, another result is possible:
  21517. i = -1
  21518. [You can even get 0 on some machine -- pjp]
  21519. One compiler gave the following hints as to why:
  21520. line 7 - Function f has no \
  21521. prototype
  21522. line 7 - Constant has long type 
  21523. line 10 - Parameter list for f \
  21524. is inconsistent with previous call
  21525. We know that on a 16-bit twos-complement machine, the smallest int value is
  21526. -32768. As such, many people expect that is what we have passed to f.
  21527. Certainly that's what f is expecting. Note, however, that the compiler warned
  21528. that a long int was actually passed. Let's accept that for now and see what
  21529. follows. We pass a 32-bit long, yet f expects a 16-bit int. You can pass the
  21530. long in two ways -- low word first or high word first. Depending on which way
  21531. the implementation chooses, f either maps into -32768 or -1.
  21532. By adding a prototype in main for f, of the form
  21533. /* 5*/ void f(int);
  21534. the long int -32768 would be silently truncated to an int. Interestingly
  21535. enough, this really would have the value -32768.
  21536. Now, back to the type of -32768. In the April 1990 issue of CUJ (Volume 8,
  21537. Number 8), I made the following statement: "An expression such as -32768
  21538. consists of two source tokens; the unary minus operator and the integer
  21539. constant 32768. Note there is no such thing as a negative constant in C. The
  21540. constant is non-negative and it is preceded by a unary minus operator. An
  21541. interesting situation exists on 16-bit twos-complement machines where -32768
  21542. is the smallest value that can be stored in an int. It so happens that the
  21543. type of -32768 when written in this form is not int; it's long int, but that's
  21544. another story."
  21545. This resulted in reader mail (and subsequent reply), but here is the
  21546. explanation.
  21547. According to the ANSI Standard, (page 28, lines 37-41), "The type of an
  21548. integer constant is the first of the corresponding list in which its value can
  21549. be represented. Unsuffixed decimal: int, long int, unsigned long int;
  21550. unsuffixed octal or hexadecimal: int, unsigned int, long int, unsigned long
  21551. int; suffixed by the letter u or U: unsigned int, unsigned long int; suffixed
  21552. by the letter 1 or L: long int, unsigned long int; suffixed by both the
  21553. letters u or U and 1 or L: unsigned long int."
  21554. 32768 is an unsuffixed decimal, and it won't fit into an int. The compiler
  21555. tries long and it works, so long is its type. The compiler then applies
  21556. negation to the result of that long int. If you look carefully, you will see
  21557. that the rules are different for decimal and octal/hex. The following program
  21558. demonstrates this:
  21559.  
  21560. #include <stdio.h>
  21561. main() {
  21562. printf("sizeof(-32768) = %lu\n",
  21563. (unsigned long)sizeof(-32768));
  21564. printf("sizeof(0x8000) = %lu\n",
  21565. (unsigned long) sizeof(0x8000));
  21566. printf("sizeof(0100000) = %lu\n",
  21567. (unsigned long)sizeof(0100000));
  21568. The values -32768, 0x8000, and 0100000 have exactly the same bit pattern when
  21569. stored in 16 bits. However, the type of the first expression is long, while
  21570. that of the second and third is unsigned int. The correct output produced is
  21571. sizeof(-32768) = 4
  21572. sizeof(0x8000) = 2
  21573. sizeof(0100000) = 2
  21574. 5. I see this kind of problem all the time, mostly with programmers new to C.
  21575. When starting C, you learn that all statements must be terminated with a
  21576. semicolon. However, if a while or for statement really is a statement, where
  21577. does its semicolon go? The answer is "They don't have one, but each of their
  21578. subordinate primitive statements does need one." In my introductory C
  21579. textbook, I call for, while, if, etc., constructs rather than statements to
  21580. avoid students putting in unneeded semicolons.
  21581. The problem is that the language supports a null statement represented simply
  21582. by a semicolon. As a result, spurious semicolons may be hazardous to your
  21583. program, as in the previous example. The trailing semicolon on line 7
  21584. represents a null statement that is subsequently taken as the body of the
  21585. loop. The call to printf, therefore, occurs once, after the loop terminates.
  21586. In this case, the output indicates the problem, but in cases where the actual
  21587. loop body produces no visibly strange behavior, the problem can be difficult
  21588. to find.
  21589. PC-Lint did issue the following, very useful message:
  21590. line 7 - Suspicious use of ;
  21591. I didn't experiment to see just which uses of ; are not suspicious, but even
  21592. if all null statements were flagged that would be useful, since null
  21593. statements are not very commonly needed.
  21594. 6. We all know that you must explicitly declare an identifier before using it.
  21595. Of course, many rules have exeptions, and the obvious one here is for
  21596. functions. If the compiler comes across a call to an unknown function, it
  21597. presumes that function returns an int. It is not able to check the argument
  21598. list because no prototype is available.
  21599. There is one other, very obscure exception. If you omit the type from any
  21600. formal arguments in a function definition, the compiler assumes that the
  21601. argument has type int. The following old-style definitions are equivalent:
  21602. double f(i, j)
  21603. int i, j;
  21604. {}
  21605. double f(i, j)
  21606. int i;
  21607. int j;
  21608. {}
  21609. double f(i, j)
  21610. int i;
  21611. {}
  21612. double f(i, j)
  21613. int j;
  21614. {}
  21615. double f(i, j)
  21616. {}
  21617. The equivalent prototype version is:
  21618. double f(int i, int j)
  21619. {}
  21620. However, you are not permitted to mix the old and new styles in certain ways.
  21621. For example, the following are invalid:
  21622. double f(i, int j)
  21623. {}
  21624. double f(int i, j)
  21625. {}
  21626. Having said all this, I strongly recommend you not use such default typing.
  21627.  
  21628. Listing 1
  21629. /* 1*/ #include <stdio.h> /* MS-DOS-specific */
  21630.  
  21631. /* 3*/ main()
  21632. /* 4*/ {
  21633. /* 5*/ FILE *fp;
  21634. /* 6*/ char filename[] = "\test.dat";
  21635.  
  21636. /* 8*/ fp = fopen(filename, "r");
  21637. /* 9*/ if (fp == NULL)
  21638. /*10*/ printf ("Can't open file %s\n", filename);
  21639. /*11*/ else {
  21640. /*12*/ printf("File open\n");
  21641. /*13*/ fclose(fp);
  21642. /*14*/ }
  21643.  
  21644. /*15*/ }
  21645.  
  21646. Output:
  21647.  
  21648. Can't open file est.dat
  21649.  
  21650.  
  21651.  
  21652.  
  21653.  
  21654.  
  21655.  
  21656.  
  21657.  
  21658.  
  21659.  
  21660.  
  21661.  
  21662.  
  21663.  
  21664.  
  21665.  
  21666.  
  21667.  
  21668.  
  21669.  
  21670.  
  21671.  
  21672.  
  21673.  
  21674.  
  21675.  
  21676.  
  21677.  
  21678.  
  21679.  
  21680.  
  21681.  
  21682.  
  21683.  
  21684.  
  21685.  
  21686.  
  21687.  
  21688.  
  21689.  
  21690.  
  21691.  
  21692.  
  21693.  
  21694.  
  21695.  
  21696.  
  21697.  
  21698.  
  21699.  
  21700.  
  21701.  
  21702.  
  21703.  
  21704.  
  21705.  
  21706.  
  21707. Questions & Answers
  21708.  
  21709.  
  21710. Readers' Replies
  21711.  
  21712.  
  21713.  
  21714.  
  21715. Ken Pugh
  21716.  
  21717.  
  21718. Kenneth Pugh, a principal in Pugh-Killeen Associates, teaches C language
  21719. courses for corporations. He is the author of C Language for Programmers and
  21720. All On C, and was a member on the ANSI C committee. He also does custom C
  21721. programming for communications, graphics, image databases, and hypertext. His
  21722. address is 4201 University Dr., Suite 102, Durham, NC 27707. You may fax
  21723. questions for Ken to (919) 493-4390. When you hear the answering message,
  21724. press the * button on your telephone. Ken also receives email at
  21725. kpugh@dukemvs.ac.duke.edu (Internet).
  21726.  
  21727.  
  21728. Q
  21729. Why do computer programmers confuse Halloween and Christmas?
  21730. Thor Parrish
  21731. Cambridge, MA
  21732. A
  21733. Let me think about that for a while. Check the end of the column. (KP)
  21734.  
  21735.  
  21736. Reader's Replies
  21737.  
  21738.  
  21739.  
  21740.  
  21741. Matching Braces
  21742.  
  21743.  
  21744. In response to Kim Tsang about the closing braces in C programs, you can
  21745. improve the readability of the code by defining the braces with #define at the
  21746. top of the file to be BEGIN and END.
  21747. Then they are easily found when scanning a listing and the comments at the end
  21748. will help match them up. I also put the END in the same column as the BEGIN,
  21749. then indent everything in between so that nothing should show up in that same
  21750. column between the BEGIN and END, as shown in Listing 1.
  21751. I can keep better track of my blocks and cut down on searching. (It also saves
  21752. on colored pens.)
  21753. I find that a four-character display works best for me. Any more and you crowd
  21754. the right-hand side of the screen, any less reduces readability.
  21755. Garrett J. Boni
  21756. Baton Rouge, LA
  21757. Thanks for another way of handling the blocking problem. I prefer braces, as
  21758. they are only a keystroke apiece. (KP)
  21759.  
  21760.  
  21761. Token Pasting
  21762.  
  21763.  
  21764. Thank you for printing Matthias Hansen's reply to my problem from the July
  21765. issue. His trick of using the token-pasting operator with the angle brackets
  21766. was something I just didn't think of, and it should be useful in the future.
  21767. Unfortunately, I can't get Herr Hansen's example to work. I tried Turbo C 2.0,
  21768. Turbo C++ 1.0, and Microsoft C 6.0, and all report that they can't find the
  21769. file MH_PRG.A. His variation for Quick C does work with all three and is
  21770. similar to the solution I finally used.
  21771. I've come up with some new solutions to the problem. First, if the
  21772. name-dependent code has some regularity you can use the token-pasting and
  21773. stringizing operators in a block of preprocessor code to produce what you
  21774. need. This can easily become unreadable, though, and so is hard to maintain.
  21775. Another solution would be to produce the code through an awk program, though
  21776. it, too, requires some regularity. It will be easier to maintain. I did
  21777. something like this years ago, and am still kicking myself for not remembering
  21778. it.
  21779. There is yet a third way to do this, one that can handle any arbitrary
  21780. differences. In our main source file, we put the line
  21781. #include "progxh.h"
  21782. and in that file we put lines like
  21783. #include "prog1_a.h"
  21784. #include "prog1_b.h"
  21785. The trick is to create this file only when needed. In your make file, before
  21786. the compiler is called, add a line to create progxh.h. You can use the ECHO
  21787. command in a simple batch file, or maybe an awk program if the file is
  21788. complicated. A clever programmer could use a feature in some make
  21789. implementations (like PolyMake) to create temporary files with the appropriate
  21790. contents.
  21791. None of the solutions we've discussed are clean; they all have the potential
  21792. for developing portability and/or maintenance problems. Which one you use will
  21793. depend upon the circumstances and whatever pay-now/pay-later philosophy you
  21794. have.
  21795. Jim Howell
  21796. Lafayette, Colorado
  21797. Jim's original question revolved around using the preprocessor to force a
  21798. #include to use different source files, based on the value of a #define. (KP)
  21799.  
  21800.  
  21801.  
  21802. qsort Function Prototypes
  21803.  
  21804.  
  21805. This letter is in response to Firdaus Irani's question concerning calls to
  21806. qsort (CUJ Dec. 1990, p. 92). I, too, had similar problems with qsort. Unlike
  21807. Firdaus, I had better luck with Borland's technical support. I was told that
  21808. the function was strictly prototyped to only accept const void * as arguments
  21809. to _fcmp(). I don't remember exactly why he said it was this way. He did tell
  21810. me how to use it correctly.
  21811. Using the example Firdaus supplied I changed the prototype for comp to
  21812. comp(const void *, const void *) and then changed the comp routine to look
  21813. like this Listing 2.
  21814. This compiles under Turbo C++. I tried the MicroSoft C compiler and it also
  21815. compiled fine.
  21816. This answers Firdaus'question of multiple calls to qsort with different
  21817. compare functions. You prototype the functions with const void * and then cast
  21818. the pointers to whatever type of pointer you need. Hopefully this will help
  21819. others who have had the same problem.
  21820. Mike Beard
  21821. Abilene, Texas
  21822. Thanks for your answer. For the efficiency-minded, you might put the function
  21823. that calls qsort in its own source file. Do not include <string.h>, but simply
  21824. have a prototype:
  21825. int strcmp(const void *a, const void *b);
  21826. Then you could call qsort with strcmp. This bypasses the prototype mechanism
  21827. for the sake of efficiency. (KP)
  21828. [This declaration violates the C standard, but you can probably get away with
  21829. it on most implementations. -- PJP]
  21830.  
  21831.  
  21832. Large Arrays In Turbo C
  21833.  
  21834.  
  21835. I'm writing in reference to a question in your column in the November 1990
  21836. issue of The C Users Journal. The question was by Abdel Hindi from Montreal,
  21837. Canada concerning large static structures in Turbo C v2.0.
  21838. I just recently had occasion to write a program that used a large static
  21839. three-dimensional array of characters. When I calculated the number of bytes
  21840. needed to hold the array, it came out to more than 150Kb. I tried to compile
  21841. the program with the compact memory model and received the same error message
  21842. as was mentioned, "Too much data...."
  21843. Please see Listing 3 for my solution to this problem. I declared a
  21844. two-dimensional array of huge pointers as global data. In the program I use
  21845. the function farcalloc to add the third dimension to the array. After the
  21846. memory data area has been allocated, all accesses to the data appear just like
  21847. a three-dimensional array.
  21848. I hope this helps.
  21849. D. F. Spencer
  21850. San Jose, CA
  21851.  
  21852.  
  21853. Externs In Headers
  21854.  
  21855.  
  21856. In reference to your column in the October 1990 issue (page 83), the following
  21857. is an approach that I use to solve the question posed by Andreas Lang.
  21858. #ifdef MAIN
  21859.  
  21860. #define EX
  21861. #define EQ(i) = i
  21862.  
  21863. #else
  21864.  
  21865. #define EX extern
  21866. #define EQ(i)
  21867.  
  21868. #endif
  21869.  
  21870. EX int i EQ(5);
  21871. This code excerpt allows a single header file to provide both the declarations
  21872. and the external references.
  21873. I always #undef EX and EQ at the end of the header, but this is not required.
  21874. Gary Hoskins
  21875. Castroville, CA
  21876.  
  21877.  
  21878. Pointers
  21879.  
  21880.  
  21881. Rex Jaeschke's illuminations about pointers and your recent column got me to
  21882. do some experimenting on my own as follows:
  21883. int p[10];
  21884. int (*q)[10] = (int (*)[10])
  21885. malloc( 2 * 10 * sizeof(int));
  21886. I thought I could assign *q, q[0], or q[1] another address just like I can any
  21887. other pointer. I understand that p is a constant pointer and can't be
  21888. reassigned another address, but what should stop me from treating any single
  21889. dereferencing of q as just another assignable address. After all, with my
  21890. Turbo C compiler I can do q = &p, but why can't I do *q = p? What I got was a
  21891. "need lvalue" error in compilation.
  21892. I was mystified at first and tried a parallel-to-q creation of r.r and q were
  21893. now of the same type. I tried to assign q[1] = r[1], bonnnkkkk!, but q = r and
  21894. q[1][1] = r[1][1] worked fine. I concluded that the compiler must simply treat
  21895. int [10] like it does p. So q[0] now references a declared array just like p
  21896. and can't be assigned another value as far as my compiler is concerned.
  21897.  
  21898. I hope my analysis of this is correct and I've not slipped a gear this
  21899. evening. Thank you for the hearing.
  21900. Mike Hanna
  21901. San Francisco, CA
  21902. You are right on. q is a pointer to objects that are int[10], that is, arrays
  21903. of 10 integers. *q represents that array of integers. Note that some compilers
  21904. will give a warning of "& operator on array name ignored" if you assign q =
  21905. &p. All you need is q = p.
  21906. To follow along with your example, suppose you had:
  21907. int array_of_arrays[3][10] = {
  21908. {1,2,3,4,5,6,7,8,9,10},
  21909. {11,12,13,14,15,16,17,18,19,20},
  21910. {21,22,23,24,25,26,27,28,29,30},
  21911. };
  21912. int (*q)[10];
  21913. q = array_of_arrays;
  21914. q contains the address of array_of_arrays. q[0] is the address of the first
  21915. element in array_of_arrays, i.e., the value of array_of_arrays[0]. Since this
  21916. happens to be an array, it is an address, which of course has the same value
  21917. (but not the same type) as array_of_arrays. q[1] is the address of the second
  21918. element in array_of_arrays, i.e., the value of array_of_arrays [1], and so
  21919. forth.
  21920. Let's put some numbers to this example. ints are two bytes and array_of_array
  21921. is located at address 100. See Listing 4. (KP)
  21922. [Confusion between pointers and arrays is endemic among even the brightest C
  21923. programmers. They are two quite different creatures, but array names get
  21924. converted to pointers so effortlessly and so often that they often appear
  21925. interchangeable. When they are not, as in the case reported by Mr. Hanna, look
  21926. out. -- PJP]
  21927.  
  21928.  
  21929. Structure Access
  21930.  
  21931.  
  21932. I'm writing in response to the letter from R. Palmer Benedict (The C Users
  21933. Journal, November 1990, p. 104). I wanted to address some of the issues raised
  21934. by that letter, which you didn't get into. First let me note that my
  21935. investigations (see Listing 5) support Benedict's statement that accessing a
  21936. structure member directly is the fastest method of access. It was Benedict's
  21937. letter, by the way, that induced me to run timing tests, since I prefer to
  21938. access data by means of arrays whenever possible.
  21939. In re-reading the letter, I ran across some rather strange logic. No code
  21940. accompanied the letter, so I may be misinterpreting Benedict's remarks.
  21941. However, there seems to be some incongruity in his method. Benedict states,
  21942. "Pointers or indices can access the data in the fields and subfields, which is
  21943. much slower than accessing elements of a structure." Three different methods
  21944. of access are represented in Benedict's statement. The fastest is direct
  21945. access to a member of a structure variable via the dot operator:
  21946. i = structure.member;
  21947. The slower methods include access via a structure pointer,
  21948. i = s_pointer->member;
  21949. and access via a properly initialized array index:
  21950. i = array[fieldnum];
  21951. Perhaps Benedict's somewhat convoluted method of creating a structure variable
  21952. (by using an array to set aside memory, and aiming a structure pointer at that
  21953. memory) has led to some confusion on his part. He remarks that he can "access
  21954. the elements of the structure through the structure pointer." Certainly he
  21955. can, but by doing so he loses the speed advantage of direct access to a
  21956. structure variable! He winds up defeating his own apparent purpose of using
  21957. the fastest access method. As it turns out, he's using the slowest of the
  21958. three methods of access (slowest on my machine, at least).
  21959. I was disappointed to find I had to agree with Benedict that access via an
  21960. array is slower than some other methods. I really prefer keeping the fields of
  21961. a database record in an array as opposed to a structure, because I can access
  21962. any field simply by changing an array index value. Or I can print all the
  21963. fields in a concise loop containing only one print statement, while the loop
  21964. increments the array index. (For relevant code examples see the function
  21965. Verify-Contents() in Listing 5.) On the other hand, direct access to a
  21966. structure variable requires that I specifically enter the ID of the structure
  21967. member that I want. To print them all, I have to request each one
  21968. individually. The same is true for access via the structure pointer. I was
  21969. relieved to observe that access via an array is faster than access by
  21970. structure pointer.
  21971. I couldn't understand why there would be any speed variation involved in this.
  21972. It seemed to me that the same thing was happening in each case. The parts of a
  21973. structure are accessed by means of offsets, which is what array indexing is
  21974. all about. So when I found that there was a difference in speed, I decided to
  21975. delve a little deeper. I switched to the tiny memory model, and told the
  21976. compiler to generate a map file. By using the tiny model, I could ignore
  21977. memory segments and find locations in the .EXE file by offsets alone. Then I
  21978. used DEBUG to view the code. It turns out that the speed of the routine
  21979. depends heavily upon when the address calculation occurs -- at compile time or
  21980. runtime. Machine constraints also affect performance.
  21981. The fastest access that I came upon was the direct access to a member of a
  21982. structure variable via the dot operator. Using the map file, I found the
  21983. location of the UsingS_Object function in the .EXE file. Stripping away the
  21984. loop code to leave just the access statements produced
  21985. MOV WORD PTR [1E4A],1D7A
  21986. MOV WORD PTR [1E4A],1DAC
  21987. MOV WORD PTR [1E4A],1DDE
  21988. MOV WORD PTR [1E4A],1E10
  21989. In assembly language, the assignment is from right to left (as it is in BASIC
  21990. and C). Each of the four hex constants 1D7A...lE10 in turn is assigned to the
  21991. destination variable [1E4A]. This is the pointer variable named access in
  21992. Listing 5. The four hex constants are hard-coded addresses of the structure
  21993. members being accessed. There is in fact no indexing, no offset calculation at
  21994. runtime. That's why this is the fastest method.
  21995. The second fastest access that I measured was access via array. The following
  21996. code is the machine representation of array access, extracted from the
  21997. function UsingArray().
  21998. MOV AX, [1E42]
  21999. MOV [1E4A] ,AX
  22000. ;
  22001. MOV AX, [1E44]
  22002. MOV [1E4A],AX
  22003. ;
  22004. MOV AX,[1E46]
  22005. MOV [1E4A],AX
  22006. ;
  22007. MOV AX,[1E48]
  22008. MOV [1E4A],AX
  22009. Each of the four accesses requires two machine statements. The hex value
  22010. [1E42] in brackets is array element 0. The value referenced by this pointer is
  22011. moved to the machine register AX and from there to the same destination that
  22012. was used in the faster code above. The machine cannot perform transfers from
  22013. one memory location to another, without the intermediate step of storage in a
  22014. register. If I had declared access to be a register variable, this routine
  22015. might have been as fast as the previous method.
  22016. Since each access by array requires two steps, it isn't quite as fast as the
  22017. direct access approach. Yet it is faster than accessing structure members via
  22018. a structure pointer:
  22019. MOV AX,[1786]
  22020. MOV [1E4A],AX
  22021. ;
  22022. MOV AX,[1786]
  22023. ADD AX, 0032
  22024. MOV [1E4A],AX
  22025. ;
  22026. MOV AX,[11786]
  22027. ADD AX, 0064
  22028.  
  22029. MOV [1E4A],AX
  22030. ;
  22031. MOV AX,[1786]
  22032.  
  22033. ADD AX, 0096
  22034. MOV [1E4A],AX
  22035. These lines are from the Using-Structure() function. Here, an offset is added
  22036. to the contents of the structure pointer at runtime to locate the desired
  22037. member. Now three steps are required for each access (except the first). That
  22038. slows this routine down a bit more than the access via array.
  22039. It would be interesting to see how the offsetof macro translates into machine
  22040. code. I suspect that the code would look like the final example above.
  22041. offsetof would be figured at compile time, to calculate the values which in
  22042. the above example are the constants that get added to the AX register at
  22043. run-time.
  22044. It would also be interesting to see how close Mr. Benedict is to the target
  22045. with another remark: To "read each field or subfield individually into its
  22046. place in the structure... can be dreadfully slow." I wonder how much slower it
  22047. is to assign one field at a time, rather than all at once. We're not talking
  22048. interpreted BASIC here. In C, using the <stdio.h> FILE functions, a disk
  22049. access fills a buffer in RAM. Then individual fields and subfields would be
  22050. read from this buffer. Shouldn't be all that slow...
  22051. I'm not sure that the search for the fastest algorithm is as important as it
  22052. used to be. There have been great advances in hardware speed over the last
  22053. several years. The fine-tuning speed adjustments that software allows are
  22054. small by comparison. They can be sacrificed in a tradeoff. This permits
  22055. selection of algorithms and writing of code that is easier to read and
  22056. maintain. One can let the hardware take care of the speed factor. In just the
  22057. same way, high-level languages now do what used to be done in assembley
  22058. language. The more competent machines can handle these larger, less efficient
  22059. programs with ease. Ultimate machine code efficiency is no longer a primary
  22060. consideration.
  22061. Some comments on Listing 5:
  22062. As written, a routine is iterated 70,000 times, and then the elapsed time is
  22063. measured. The routines should probably be set up to run for a given period of
  22064. time while the repetitions are counted. At the very least, they should be
  22065. clocked by a timer with better resolution than the BIOS clock. Some of these
  22066. functions perform 70,000 iterations of a test in about 18 clock ticks, which
  22067. is one second. That's almost 4,000 iterations per tick. If one routine is on
  22068. the quick side of 17 ticks, and another is on the slow side of 18 ticks, the
  22069. difference could almost be 8,000 iterations, or well over 10 percent. That's a
  22070. wide error margin. Nonetheless, I'm using the BIOS clock for a quick benchmark
  22071. timer.
  22072. One of the test functions in Listing 5 is an empty loop. This is included as a
  22073. sort of test of my benchmark routine logic. The analyze routine compares the
  22074. time to execute two separate loops against the time to execute a single loop
  22075. which does the work of the other two. The time difference between these should
  22076. be close to the time to execute an empty loop that does no work. It is.
  22077. The .EXE file analyzed above was produced by TurboC v1.5 and compiled (for
  22078. this analysis) in the tiny memory model. The benchmark times in Listing 6 were
  22079. produced on a 10 MHz AT clone.
  22080. Art Shipman
  22081. Westbrookville, NY 12785
  22082. Thanks for your tests and comments. I agree with your comment that ultimate
  22083. machine efficiency is not a primary consideration these days -- unless the
  22084. time required by inefficient code makes the user go out for coffee.
  22085. If I wanted to access the individual fields in the structures by using a loop,
  22086. I would declare
  22087. field_addresses[] = {
  22088. &S_object. field1,
  22089. &S_object.field2,
  22090. &S_object.field3,
  22091. &S_object.field4};
  22092. and then use a loop as
  22093. for (i = 0; i < 4; i++)
  22094. {
  22095. printf("\n Field %d is %50.50s",
  22096. field_addresses [i]);
  22097. }
  22098. That would probably be almost as fast as using an array access, since the
  22099. offset addresses are precomputed. (KP)
  22100.  
  22101.  
  22102. Casts And ANSI Standard
  22103.  
  22104.  
  22105. Your column in the November 1990 issue included a letter from Hans-Gabriel
  22106. Ridder, concerning the construct
  22107. char *ptr;
  22108. ((long *) ptr)++;
  22109. His ANSI-conformant Lattice C compiler gave him an "lvalue required" error on
  22110. this. Though he had used constructs like this for years, Lattice told him that
  22111. "casts not being lvalues: was required by ANSI, in the name of portability."
  22112. As far as I can tell, a cast has never yielded an lvalue, even in "classic"
  22113. K&R C. And ++ has always required an lvalue. I'm not surprised that many early
  22114. compilers would accept such a construct without complaint and even give the
  22115. expected results. I think early compilers often accepted syntax that was not
  22116. really legal.
  22117. I'm also pretty sure that some pre-ANSI compilers would (correctly) reject
  22118. such an expression. Allowing casts (even to pointer values) to be lvalues
  22119. would permit such strange things as
  22120. (int *)f() = g();
  22121. when what was probably intended was
  22122. *(int *)f() = g();
  22123. Mr. Ridder was especially concerned about the case where the pointer is
  22124. dereferenced along with the post-increment, in a manner similar to
  22125. char *cptr;
  22126. f(*cptr++);
  22127. but treating cptr as a pointer to long. You suggested using
  22128. *(cptr += sizeof(long))
  22129. instead of
  22130. *(char *) (((long *) ptr)++)
  22131. but this does not have the same effect, since cptr would be incremented before
  22132. dereferencing, and I think Mr. Ridder wanted the dereferenced value to be a
  22133. long instead of a char anyway. (If not, the solution below won't help him).
  22134. Your editor claimed parenthetically that "you can also get the old behavior by
  22135. writing: ((long*)&ptr)++," but this is also clearly wrong. This still tries to
  22136. increment the result of a cast.
  22137. I think the editor was trying to say the following:
  22138. *(*(long **)&cptr)++
  22139. This will treat cptr as a pointer to long, dereference it, and post-increment
  22140. it. This is legal C, both classic and ANSI, but it is not guaranteed to work.
  22141. It assumes that you represent and manipulate pointers to char and pointers to
  22142. long in the same way, and that there is no problem with the alignment of the
  22143. objects being pointed to. You can think of it as a tricky way of getting the
  22144. effect of a union without actually using one.
  22145. As a test, I tried the following functions on Zortech C 2.12:
  22146. char *g1(char *cptr)
  22147. {
  22148.  
  22149. f(*(*(long **)&cptr)++);
  22150. return cptr;
  22151. }
  22152.  
  22153. long *g2(long *cptr)
  22154. {
  22155. f(*cptr++);
  22156. return cptr;
  22157. }
  22158. With no optimization, g1 generated shorter code than g2! With optimization,
  22159. the code generated was identical for these functions.
  22160. Raymond Gardner
  22161. Englewood, CO
  22162. Thanks for your tests. As you say, mixing pointers to ints and longs or any
  22163. other data objects for that matter is a tricky business. I prefer more
  22164. straightforward methods, as the unions you mentioned.
  22165. When I see expressions like
  22166. *(*(long **)&cptr)++
  22167. it reminds me why some novices wonder how they ever got into this language. I
  22168. prefer breaking a tricky expression into two parts, with the increment being
  22169. put before or after the use of cptr, as desired.
  22170. *cptr = 'a';
  22171. cptr += sizeof(long))
  22172. Okay, it'll take a microsecond or so more. It took me a few seconds to figure
  22173. out that *(*(long *)&cptr)++ = 'a' actually transfers four bytes (size of a
  22174. long), even though cptr is a pointer to char. That's a tradeoff of more than a
  22175. million to one. Of course, if you really wanted to transfer just a character,
  22176. it would be * (char *) (*(long *)&cptr)++ = 'a'. Say that fast 10 times. (KP)
  22177.  
  22178.  
  22179. Answer to Halloween/Christmas
  22180.  
  22181.  
  22182. Because OCT 31 equals DEC 25. (Octal 31 equals decimal 25.)
  22183.  
  22184. Listing 1
  22185. #define BEGIN {
  22186. #define END }
  22187.  
  22188. main()
  22189. BEGIN
  22190. ....
  22191.  
  22192. for ()
  22193. BEGIN
  22194. ....
  22195. END
  22196. END
  22197.  
  22198.  
  22199. Listing 2
  22200. #include <stdio.h>
  22201. #include <stdlib.h>
  22202. #include <string.h>
  22203.  
  22204. int comp(const void *, const void *);
  22205. unsigned char *list[] = { "cat", "car", "cab",
  22206. "cap", "can" };
  22207.  
  22208. int main()
  22209. {
  22210. int x;
  22211.  
  22212. qsort(list, 5, sizeof(unsigned char *), comp);
  22213. for (x = 0; x < 5; x++)
  22214. printf("%s\n", list[x]);
  22215. return 0;
  22216. }
  22217.  
  22218.  
  22219. int comp(const void *a, const void *b)
  22220. {
  22221. unsigned **A = (unsigned char **)a;
  22222. unsigned **B = (unsigned char **)b;
  22223. return strcmp(*A, *B);
  22224. }
  22225.  
  22226.  
  22227. Listing 3
  22228. /* memtest.c --- test bed program for checking some
  22229. huge arrays */
  22230. /* ...use COMPACT memory model */
  22231.  
  22232. #include <stdio.h>
  22233. #include <alloc.h>
  22234.  
  22235. char (huge *pinno)[20][8]; /* each term can have up
  22236. to 20 pins */
  22237.  
  22238. void main(void)
  22239. {
  22240. int i, j, k;
  22241.  
  22242. if ( (pinno=farcalloc(sizeof(*pinno),1000)) ==NULL)
  22243. {
  22244. printf("\nallocation error");
  22245. printf("...insufficient memory available...\n");
  22246. exit(1);
  22247. }
  22248.  
  22249. printf("\narray of 160000 characters starts at %p \
  22250. (norm)\n",pinno);
  22251. printf("\narray at %Fp (far)\n",pinno);
  22252.  
  22253. for (i = 0; i < 1000; i++)
  22254. {
  22255. for (j = 0; j < 20; j++)
  22256. {
  22257. for (k = 0; k < 7; k++)
  22258. pinno[i][j][k]=0x41+k+j;
  22259. pinno[i][j][k]='\0';
  22260. }
  22261. }
  22262.  
  22263. for (i = 0; i < 1000; i++)
  22264. {
  22265. for (j = 0; j < 20; j++)
  22266. printf("\narray element [%d][%d] = %s",i,j,
  22267. pinno[i][j]);
  22268. }
  22269. }
  22270.  
  22271.  
  22272. Listing 4
  22273. Expression Value Type
  22274.  
  22275. array_of_arrays 100 (int *)[10]
  22276. array_of_arrays[0] 100 int *
  22277.  
  22278. array_of_arrays[0][0] 1 int
  22279. array_of_arrays[1] 120 int *
  22280. array_of_arrays + 1 120 (int *)[10]
  22281. array_of_arrays[0] + 1 102 int *
  22282. q 100 (int *)[10]
  22283. q[0] 100 int *
  22284. q[0][0] 1 int
  22285. q[1] 120 int *
  22286. q + 1 120 (int *)[10]
  22287. q[0] + 1 102 int *
  22288.  
  22289.  
  22290. Listing 5
  22291. #include <stdio.h>
  22292. #include <bios.h>
  22293. #include <string.h>
  22294.  
  22295. #define LIMIT 70000L
  22296.  
  22297. #define INIT \
  22298. long i; \
  22299. long t = biostime(0,0)
  22300.  
  22301. #define TEST(x) \
  22302. INIT; \
  22303. for(i=0; i<LIMIT; i++){ \
  22304. x; \
  22305. } \
  22306. return (int)(biostime(0,0)-t)
  22307.  
  22308. #define Access(x,y) \
  22309. printf("Accessing %s(): result = %d\n", #x,
  22310. elapsed[y]=x() )
  22311.  
  22312. typedef struct anything
  22313. {
  22314. char field1[50],
  22315. field2[50],
  22316. field3[50],
  22317. field4[50];
  22318. } anything;
  22319.  
  22320. anything s_object, *s_pointer=&s_object;
  22321.  
  22322. char *DestArray[4];
  22323. char *SourceArray[4];
  22324. char *access;
  22325. int j=0;
  22326.  
  22327. enum list { STRUCTURE, ARRAY, BOTH, EMPTY, ONELOOP,
  22328. TWOLOOPS, S_OBJECT };
  22329.  
  22330. int UsingS_Object(void)
  22331. {
  22332. TEST( access = (char *)&s_object.field1;
  22333. access = (char *)&s_object.field2;
  22334. access = (char *)&s_object.field3;
  22335. access = (char *)&s_object.field4;);
  22336. }
  22337.  
  22338.  
  22339. int UsingStructure(void)
  22340. {
  22341. TEST( access = s_pointer->field1;
  22342. access = s_pointer->field2;
  22343. access = s_pointer->field3;
  22344. access = s_pointer->field4; ); 
  22345.  
  22346. int UsingArray(void)
  22347. {
  22348. TEST( access = SourceArray[0];
  22349. access = SourceArray[1];
  22350. access = SourceArray[2];
  22351. access = SourceArray[3]; );
  22352. }
  22353.  
  22354. zint UsingStrucAndArrayBoth(void)
  22355. {
  22356. TEST( access = s_pointer->field1;
  22357. access = s_pointer->field2;
  22358. access = s_pointer->field3;
  22359. access = s_pointer->field4;
  22360.  
  22361. access = SourceArray[0];
  22362. access = SourceArray[1];
  22363. access = SourceArray[2];
  22364. access = SourceArray[3]; );
  22365. }
  22366.  
  22367. int EmptyLoop(void)
  22368. {
  22369. TEST( ; );
  22370. }
  22371.  
  22372. int LoopToLoop(void)
  22373. {
  22374. TEST( for(j=0; j<4; j++){ DestArray[j] =
  22375. SourceArray[j]; } );
  22376. }
  22377.  
  22378. int OneLoop(void)
  22379. {
  22380. TEST( for(j=0; j<4; j++){ access = SourceArray[j]; } );
  22381. }
  22382.  
  22383. void analyze(int *times)
  22384. {
  22385. int total = times[STRUCTURE] + times[ARRAY];
  22386. int difference = total - times[BOTH];
  22387.  
  22388. printf("\n1. Sum of time for (UsingStructure + \
  22389. UsingArray) = %d\n", total );
  22390.  
  22391. printf("2. Sum less time for both in one loop = %d\n",
  22392. difference );
  22393. printf("3. Time to do an empty loop = %d\n",
  22394. times[EMPTY] );
  22395. printf("4. Error (item 3 less item 2 should be near \
  22396. zero) = %d\n",
  22397.  
  22398. times[EMPTY]-difference );
  22399. }
  22400.  
  22401. void CompareAccessTimes(void)
  22402. {
  22403. int elapsed[10];
  22404. Access(UsingS_Object,S_OBJECT);
  22405. Access(UsingStructure,STRUCTURE);
  22406. Access(UsingArray, ARRAY );
  22407. Access(UsingStrucAndArrayBoth, BOTH);
  22408. Access(EmptyLoop, EMPTY);
  22409. Access(LoopToLoop,TWOLOOPS);
  22410. Access(OneLoop,ONELOOP);
  22411.  
  22412. analyze(elapsed);
  22413. }
  22414.  
  22415. void VerifyContents(void)
  22416. {
  22417. int i;
  22418. puts("The data to be accessed is as follows:\n");
  22419.  
  22420. /*
  22421. printf("The structure contains the following...\n");
  22422. printf("\t%s\n\t%s\n\t%s\n\t%s\n",
  22423. s_pointer->field1,
  22424. s_pointer->field2,
  22425. s_pointer->field3,
  22426. s_pointer->field4);
  22427. */
  22428. printf("\nThe array provides access to...\n");
  22429. for(i=0; i<4; i++)
  22430. {
  22431. printf("\t%s\n", SourceArray[i] );
  22432. }
  22433. puts("\nEach function returns the count of clock ticks."\
  22434. "The lower the better.\n);
  22435. }
  22436.  
  22437. void main(void)
  22438. {
  22439. printf("In this example, the size of a data pointer is %d,",
  22440. sizeof(char*));
  22441. printf("and the size\nof a function pointer is %d\n",
  22442. sizeof(main) );
  22443.  
  22444. /* initialize the structure */
  22445.  
  22446. strcpy(s_pointer->field1,"This is field one");
  22447. strcpy(s_pointer->field2,"Here's the second field");
  22448. strcpy(s_pointer->field3,"Field Three at your service");
  22449. strcpy(s_pointer->field4,"Art was here");
  22450.  
  22451. /* initialize the purportedly slower array */
  22452.  
  22453. SourceArray[0] = s_pointer->field1;
  22454. SourceArray[0] = s_pointer->field2;
  22455. SourceArray[0] = s_pointer->field3;
  22456. SourceArray[0] = s_pointer->field4;
  22457.  
  22458.  
  22459. VerifyContents();
  22460.  
  22461. CompareAccessTimes();
  22462. }
  22463.  
  22464.  
  22465. Listing 6 Output from Listing 5
  22466. In this example, the size of a data pointer is 2, and the size
  22467. of a function pointer is 2
  22468. The data to be accessed is as follows:
  22469.  
  22470. The array provides access to...
  22471. This is field one
  22472. Here's the second field
  22473. Field Three at your service
  22474. Art was here
  22475.  
  22476. Each function returns the count of clock ticks. The lower the better.
  22477.  
  22478. Accessing UsingS_Object(): result = 14
  22479. Accessing UsingStructure(): result = 18
  22480. Accessing UsingArray(): result = 16
  22481. Accessing UsingStrucAndArrayBoth()(: result = 25
  22482. Accessing EmptyLoop(): result = 8
  22483. Accessing LoopToLoop(): result = 57
  22484. Accessing OneLoop(): result = 48
  22485.  
  22486. 1. Sum of time for (UsingStructure + UsingArray) = 34
  22487. 2. Sum less time for both in one loop = 9
  22488. 3. Time to do an empty loop = 8
  22489. 4. Error (item 3 less item 2 should be near zero) = -1
  22490.  
  22491.  
  22492.  
  22493.  
  22494.  
  22495.  
  22496.  
  22497.  
  22498.  
  22499.  
  22500.  
  22501.  
  22502.  
  22503.  
  22504.  
  22505.  
  22506.  
  22507.  
  22508.  
  22509.  
  22510.  
  22511.  
  22512.  
  22513.  
  22514.  
  22515.  
  22516.  
  22517.  
  22518.  
  22519.  
  22520.  
  22521. Stepping Up To C++
  22522.  
  22523.  
  22524. Writing Your First Class
  22525.  
  22526.  
  22527.  
  22528.  
  22529. Dan Saks
  22530.  
  22531.  
  22532. Dan Saks is the owner of Saks & Associates, which offers consulting and
  22533. training in C, C++ and Pascal. He is also a contributing editor of TECH
  22534. Specialist. He serves as secretary of the ANSI C++ committee and is a member
  22535. of the ANSI C committee. Readers can write to him at 287 W. McCreight Ave.,
  22536. Springfield, OH 45504 or by email at dsaks@wittenberg. edu.
  22537.  
  22538.  
  22539. Over the past two years I've spoken with many C programmers who are thinking
  22540. about switching to C++. Nearly all have heard about the highly touted benefits
  22541. of object-oriented programming (OOP) and C++. Although a few are skeptical
  22542. about the benefits of C++, most programmers are intrigued and want to know
  22543. more about it.
  22544. As I mentioned in my last column, I think some apprehension is justified. C++
  22545. is not as widely available as C. Where C++ is available, the development tools
  22546. are sometimes lacking. And there's no formal standard for C++. Many C
  22547. programmers are just starting to appreciate programming in a mature,
  22548. standardized language. Understandably, they are reluctant to switch to C++.
  22549. Most of all, I think C programmers are confused, intimidated, or just put off
  22550. by the overly zealous preaching of a few highly vocal "true believers" in OOP.
  22551. These zealots are hard to avoid -- every software development organization
  22552. seems to have at least one. The zealots want you to believe that you shouldn't
  22553. use C++ for anything other than OOP (using both inheritance and polymorphism).
  22554. I've heard C programmers express their frustration with learning C++ under the
  22555. shadow of a zealot. It's rather disconcerting to have your self-improvement
  22556. efforts belittled by someone who thinks you haven't improved enough. You
  22557. expect to hear, "Keep up the good work!" Instead you hear, "OK, but you really
  22558. could have done it better." (Some of my former university students will
  22559. probably be amused to read this, wondering, "When did this guy mellow out?"
  22560. Age works wonders on us all.)
  22561. The fact is, to a C programmer, C++ has many new features. Don't expect to
  22562. grasp them all at once. Many of these features are intended to solve problems
  22563. that arise in large-scale software systems. Small programming examples such as
  22564. printing Hello, world, rarely make a convincing case for the advantages of C++
  22565. over C. To really learn C++, you need to work through large examples that take
  22566. time to develop.
  22567. Complex programming problems usually have more than one solution. You should
  22568. try different approaches to see which one works best for each situation. As
  22569. Bjarne Stroustrup wrote in his first book on C++, "To write good programs
  22570. takes intelligence, taste, and patience. You are not going to get it right the
  22571. first time; experiment!" [1]
  22572. Experimenting is fine if you're just writing practice programs, but most
  22573. programmers must work for a living. They don't have much spare time to
  22574. experiment with different design and programming techniques. Programmers and
  22575. project managers must continue to try new tools and techniques to improve
  22576. quality and productivity, but too much innovation all at once is risky.
  22577. If you use object-oriented techniques in your first large C++ program, you
  22578. might develop some reusable components that dramatically reduce the program's
  22579. code size. More likely, you'll make lots of mistakes. If you believe, as Fred
  22580. Brooks suggests [2], that you should "plan to throw one away," then maybe you
  22581. can plunge into C++ and OOP all at once. If you're not prepared to throw the
  22582. program away, you're courting disaster.
  22583.  
  22584.  
  22585. Learning C++ In Stages
  22586.  
  22587.  
  22588. As an alternative, I think it's not only possible but preferable, to learn and
  22589. apply C++ in stages. You can't learn to use virtual functions (polymorphism)
  22590. unless you understand inheritance, and inheritance only makes sense if you
  22591. understand classes (encapsulation). The best way to learn encapsulation is to
  22592. apply it in real programs, and you can write lots of useful C++ programs using
  22593. encapsulation without inheritance. Languages that provide encapsulation
  22594. without inheritance are called object-based [3], whereas languages that
  22595. support inheritance are called object-oriented.
  22596. Most people learn to program by reliving the evolution of programming
  22597. languages. New languages appear as programmers run up against the limitations
  22598. of existing languages. Object-based languages, like Ada and Modula-2,
  22599. represent a major evolutionary step between data-structured languages, such as
  22600. C and Pascal, and object-oriented languages such as C++. Just as the languages
  22601. took years to evolve, programmers need time (months, if not years) to progress
  22602. through these evolutionary stages. Everyone progresses at a different rate.
  22603. If you've ever tried explaining the virtues of pointers and user-defined data
  22604. types to a FORTRAN programmer, then you probably know what I'm talking about.
  22605. Most FORTRAN programmers who crunch numbers for a living can't understand why
  22606. arrays aren't adequate for structuring any collection of data you could ever
  22607. want. I've had similar difficulty explaining information hiding
  22608. (encapsulation) to students who have never maintained someone else's code. If
  22609. you haven't experienced the problem, you can't appreciate the solution.
  22610. Many C programmers want to start using C++, but they're reluctant to take the
  22611. OOP plunge. OOP is not the only reservation programmers have about C++, but it
  22612. is significant. C++ is, for all practical purposes, a superset of C. C++ was
  22613. designed to be integrated with existing C code and practice. The language
  22614. permits you -- but doesn't force you -- to overhaul your design and
  22615. programming styles. You can gain a lot by using C++, even if you only apply it
  22616. a little bit at a time.
  22617. If you want to start small with C++, then use it as an object-based language.
  22618. Find some part of your application that could be a separate, well-defined
  22619. entity, and implement that entity as a class. Try writing the class so that
  22620. its public interface hides the implementation details from the rest of the
  22621. application. The following example does this by analyzing the weaknesses of an
  22622. existing C program.
  22623.  
  22624.  
  22625. A Cross-Reference Generator
  22626.  
  22627.  
  22628. A cross-reference generator program reads a document and prints an
  22629. alphabetized list of the words appearing in that document. Each word in the
  22630. output listing is followed by a sequence of line numbers on which that word
  22631. appears in the document. For example, if the word object appears once on lines
  22632. 3, 19, and 100, and twice on line 81, then the cross-reference listing entry
  22633. for object is
  22634. object 3 19 81 100
  22635. This program is suggested as exercise 6-3 in K & R [4].
  22636. Listing 1 shows a portion of xr.c, an implementation of the cross-reference
  22637. generator. xr is based on the solution presented by Tondo and Gimpel [5]. I
  22638. modified their solution to compile in C++ as well as C, and made a few small
  22639. stylistic changes.
  22640. xr works as follows: Each call to getword reads the next word, punctuation
  22641. character, or newline character from the input document. If getword returns a
  22642. word (a sequence of letters and digits starting with a letter), the program
  22643. adds an entry to the cross-reference table containing that word and the
  22644. current line number. If getword returns a newline, the programs increments the
  22645. current line number. After reading the entire input, the program prints the
  22646. cross-reference listing.
  22647.  
  22648.  
  22649. Hiding The Details
  22650.  
  22651.  
  22652. Listing 1 illustrates a problem that plagues most large programs. The program
  22653. contains declarations at the main level that reveal design and implementation
  22654. decisions made at lower levels. These declarations reduce the program's
  22655. readability and maintainability by cluttering the main level with
  22656. inappropriate detail. This clutter isn't much trouble in small programs but
  22657. can be overwhelming in programs with tens or hundreds of declarations.
  22658. For example, it's evident from Listing 1 that xr implements the
  22659. cross-reference table as a binary tree. Why? For starters, the function that
  22660. adds an entry to the cross-reference is called addtree, and the output
  22661. function is called printtree. Both functions accept root as an argument, and
  22662. root is of type treenode *.
  22663. If you refer to my description of how xr works, you'll see that it never
  22664. mentions trees. At this level in the program design, you don't need to know
  22665. how the table is implemented, but you should know what the table does
  22666. A good implementation of the cross-reference program keeps these concerns
  22667. separate. Each design decision, like the structure of the cross-reference
  22668. table, should be hidden in a separate part of the program. This practice of
  22669. isolating design decisions is known as information hiding or data hiding. In
  22670. the OOP world, it's called encapsulation.
  22671. C provides only limited support for encapsulation. You hide information by
  22672. placing code in separately compiled modules. Understanding the technique and
  22673. its limitations will help you appreciate C++ classes, so I'll demonstrate the
  22674. technique by applying it in the implementation of xr.
  22675.  
  22676.  
  22677. Encapsulation With C
  22678.  
  22679.  
  22680.  
  22681. Listing 2 through 4 present a better implementation of xr using some
  22682. encapsulation by separate compilation. xr.c (Listing 2) is the main source
  22683. file. The implementation of the cross-reference table is hidden in xrt.c
  22684. (Listing 3). The header xrt.h (Listing 4) defines the interface to xrt.c. That
  22685. is, the header declares the functions through which the main program accesses
  22686. the hidden table.
  22687. Notice that all evidence that the cross-reference table is a binary tree is
  22688. gone from the xr.c. The variable root, the functions addtree and printtree,
  22689. and the struct definitions have all been moved from xr.c to xrt.c. root,
  22690. addtree, and printtree are declared static in xrt.c, so they can't be
  22691. referenced directly by code in xr.c.
  22692. The functions xrt_add and xrt_print, declared in xrt.h and defined in xrt.c,
  22693. provide the only access from xr.c to cross-reference table data structure.
  22694. Instead of calling addtree directly, main must call xrt_add. xrt_add passes
  22695. root to a call to addtree but keeps both root and addtree hidden inside xrt.c.
  22696. Similarly, main must call xrt_print to invoke printtree.
  22697. The key to this encapsulation technique is the selective use of the static
  22698. storage specifier. You place the data to be hidden, along with the functions
  22699. that manipulate that data, in a single, separate source file. You declare all
  22700. the data at file scope, and almost all of the functions static. The only
  22701. functions that should be extern are those few that grant access to the data
  22702. structure from the outside world. The access function names and prototypes
  22703. should clearly indicate what they do, but give little or no clue as to how
  22704. they work.
  22705. This implementation of xr is more readable than the first one. The input
  22706. processing is clearly distinct from the table processing. main more clearly
  22707. describes what it does without unnecessary and intrusive details about the
  22708. inner workings of the table. This program is also more maintainable. The
  22709. structure of the cross-reference table is so well hidden that you can change
  22710. it to a b-tree or hash table without even recompiling the main source file.
  22711.  
  22712.  
  22713. Where C Breaks Down
  22714.  
  22715.  
  22716. Ideally, each encapsulation unit hides a single design decision. However, the
  22717. table implementation in xrt.c (Listing 3) actually embodies two decisions:
  22718. the table is a binary tree
  22719. each sequence of line numbers is a singly-linked list referenced by a single
  22720. pointer in each tree node
  22721. Just as the implementation of the table should be hidden from main, the
  22722. implementation of each line number sequence should be hidden from the table
  22723. module.
  22724. Unfortunately, the separate compilation technique that seems to work so well
  22725. at hiding the table is completely inadequate for hiding the implementation of
  22726. the line number sequences. The problem is that there's exactly one sequence
  22727. for each tree node. The representation of a sequence must be declared as part
  22728. of struct treenode. You can't store the sequences in static data in another
  22729. module.
  22730. So what do you do? Implement line number sequences as a C++ class.
  22731.  
  22732.  
  22733. Encapsulating With C++
  22734.  
  22735.  
  22736. The header file ln_seq. h (Listing 5) contains the declaration for a class of
  22737. line number sequences called ln_seq. ln_seq provides a constructor and two
  22738. public access functions: add and print. The class has one private data member,
  22739. first, which points to the first element in the linked list implementation of
  22740. the sequence.
  22741. Note that the type of each linked list node, listnode, is a nested type. That
  22742. is, listnode is declared inside class ln_seq. Versions of C++ compatible with
  22743. AT&T C++ 2.0 (or earlier) treat nested classes as if they were declared in the
  22744. scope of the enclosing class, but AT&T C++ 2.1 (and the current draft standard
  22745. for C++) treat nested classes as local to the enclosing class. My intention in
  22746. Listing 5 is that listnode should be private within ln_seq. Although many
  22747. compilers don't yet enforce this access restriction, they will eventually.
  22748. ln_seq. cpp (Listing 6) implements the member functions of the class. The
  22749. constructor ln_seq::ln_seq is trivial -- it just initializes the list to null.
  22750. The body of ln_seq::print is simply the for loop that appeared in xrt_print.
  22751. ln_seq::add is the addnumber function from Listing 3, with one noteworthy
  22752. change.
  22753. ln_seq::add contains additional code to handle the case where the list is
  22754. empty, i.e., first is null. addnumber never confronts this case because
  22755. addtree (Listing 3) creates each list with an initial node. I could have
  22756. written the constructor to initialize the sequence with an initial line
  22757. number, but handling the null in the add function is easier.
  22758. Listing 7 shows xrt.cpp, the cross-reference table handler rewritten using the
  22759. line number sequence class. It now shows no hint of how the sequences are
  22760. implemented. You can safely change the implementation of ln_seq without
  22761. changing xrt. cpp (although you will probably need to recompile).
  22762. For example, if you use only a single pointer to the head of each list, then
  22763. every time you add an element, you have to search through the entire list to
  22764. find the end. If you track the end of each list in a second pointer, you
  22765. eliminate the searching. Listing 8 and Listing 9 show this alternative
  22766. implementation.
  22767. An added benefit of using classes is that it forces you to think carefully
  22768. about the interfaces between components of your program. For example, notice
  22769. that the first parameter of addnumber (Listing 3) is a treenode, not a
  22770. listnode. The function adds a line number to the sequence in a tree, rather
  22771. than to a list by itself. The sloppiness of this design becomes apparent when
  22772. you try to transform it into a member function of the line number sequence
  22773. class.
  22774.  
  22775.  
  22776. Looking Ahead
  22777.  
  22778.  
  22779. xr is now an object-based program with lots of line number sequence objects.
  22780. It uses only the most basic features of C++, but I think the C++ version of
  22781. xrt (Listing 7) is better organized and more readable than the C version
  22782. (Listing 3). More improvements could be made, but they will keep until some
  22783. future column.
  22784. You don't need a crash course in object-oriented design to write your first
  22785. practical C++ class in a real-live application. Just identify a design
  22786. decision and create a class to hide it. With practice, you'll get good at it.
  22787. References
  22788. [1] Stroustrup, Bjarne, The C++ Programming Language. Addison-Wesley, Reading,
  22789. MA, 1986.
  22790. [2] Brooks, Fred, The Mythical Man-Month. Addison-Wesley, Reading, MA, 1975.
  22791. [3] Wegner, Peter, "Concepts and Paradigms of Object-Oriented Programming,"
  22792. OOPS Messenger, Vol. 1, No. 1, Aug 1990.
  22793. [4] Kernighan, Brian and Ritchie, Dennis, The C Programming Language, 2nd ed.
  22794. Prentice-Hall, Englewood Cliffs, NJ, 1988.
  22795. [5] Tondo, Clovis and Gimpel, Scott, The C Answer Book, 2nd ed. Prentice-Hall,
  22796. Englewood Cliffs, NJ, 1989.
  22797.  
  22798. Listing 1
  22799. /*
  22800. * xr.c - a cross-reference generator
  22801. */
  22802. #include <ctype.h>
  22803. #include <stdio.h>
  22804. #include <stdlib.h>
  22805. #include <string.h>
  22806.  
  22807. typedef struct listnode listnode;
  22808. struct listnode
  22809. {
  22810. unsigned number;
  22811. listnode *next;
  22812. };
  22813.  
  22814. typedef struct treenode treenode;
  22815.  
  22816. struct treenode
  22817. {
  22818. char *word;
  22819. listnode *lines;
  22820. treenode *left, *right;
  22821. };
  22822.  
  22823. treenode *addtree(treenode *t, char *w, unsigned n);
  22824. void printtree(treenode *t);
  22825. int getword(char *word, size_t lim);
  22826.  
  22827. #define MAXWORD 100
  22828.  
  22829. int main(void)
  22830. {
  22831. treenode *root = NULL;
  22832. char word[MAXWORD];
  22833. unsigned lineno = 1;
  22834.  
  22835. while (getword(word, MAXWORD) != EOF)
  22836. if (isalpha(word[0]))
  22837. root = addtree(root, word, lineno);
  22838. else if (word[0] == '\n')
  22839. ++lineno;
  22840. printtree(root);
  22841. return 0;
  22842. }
  22843.  
  22844.  
  22845. Listing 2
  22846. /*
  22847. * xr.c - a cross-reference generator
  22848. */
  22849. #include <assert.h>
  22850. #include <ctype.h>
  22851. #include <stdio.h>
  22852. #include <stdlib.h>
  22853. #include <string.h>
  22854.  
  22855. #include "xrt.h"
  22856.  
  22857. int getword(char *word, size_t lim)
  22858. {
  22859. int c;
  22860. char *w = word;
  22861.  
  22862. assert(lim > 2);
  22863. while (isspace(c = fgetc(stdin)) && c != '\n')
  22864. ;
  22865. if (c != EOF)
  22866. *w++ = c;
  22867. if (!isalpha(c))
  22868. {
  22869. *w = '\0';
  22870. return c;
  22871. }
  22872. for ( ; lim-- > 0; ++w)
  22873. if (!isalnum(*w = fgetc(stdin)))
  22874. {
  22875.  
  22876. ungetc(*w, stdin);
  22877. break;
  22878. }
  22879. *w = '\0';
  22880. return *word;
  22881. }
  22882.  
  22883. #define MAXWORD 100
  22884.  
  22885. int main(void)
  22886. {
  22887. char word[MAXWORD];
  22888. unsigned lineno = 1;
  22889.  
  22890. while (getword(word, MAXWORD) != EOF)
  22891. if (isalpha(word[0]))
  22892. xrt_add(word, lineno);
  22893. else if (word[0] == '\n')
  22894. ++lineno;
  22895. xrt_print();
  22896. return 0;
  22897. }
  22898.  
  22899.  
  22900. Listing 3
  22901. /*
  22902. * xrt.c - cross-reference table implementation
  22903. */
  22904. #include <stdio.h>
  22905. #include <stdlib.h>
  22906. #include <string.h>
  22907.  
  22908. #include "xrt.h"
  22909.  
  22910. typedef struct listnode listnode;
  22911. struct listnode
  22912. {
  22913. unsigned number;
  22914. listnode *next;
  22915. };
  22916.  
  22917. typedef struct treenode treenode;
  22918. struct treenode
  22919. {
  22920. char *word;
  22921. listnode *lines;
  22922. treenode *left, *right;
  22923. };
  22924.  
  22925. static void addnumber(treenode *t, unsigned n)
  22926. {
  22927. listnode *p;
  22928.  
  22929. p = t->lines;
  22930. while (p->next != NULL && p->number != n)
  22931. p = p->next;
  22932. if (p->number != n)
  22933. {
  22934. p = p->next
  22935.  
  22936. = (listnode *)malloc(sizeof(listnode));
  22937. p->number = n;
  22938. p->next = NULL;
  22939. }
  22940. }
  22941.  
  22942. static treenode *addtree
  22943. (treenode *t, char *w, unsigned n)
  22944. {
  22945. int cond;
  22946.  
  22947. if (t == NULL)
  22948. {
  22949. t = (treenode *)malloc(sizeof(treenode));
  22950. t->word =
  22951. strcpy((char *)malloc(strlen(w) + 1), w);
  22952. t->lines =
  22953. (listnode *)malloc(sizeof(listnode));
  22954. t->lines->number = n;
  22955. t->lines->next = NULL;
  22956. t->left = t->right = NULL;
  22957. }
  22958. else if ((cond = strcmp(w, t->word)) == 0)
  22959. addnumber(t, n);
  22960. else if (cord < 0)
  22961. t->left = addtree(t->left, w, n);
  22962. else
  22963. t->right = addtree(t->right, w, n);
  22964. return t;
  22965. }
  22966.  
  22967. static void printtree(treenode *t)
  22968. {
  22969. listnode *p;
  22970.  
  22971. if (t != NULL)
  22972. {
  22973. printtree(t->left);
  22974. printf("%12s: ", t->word);
  22975. for (p = t->lines; p != NULL; p = p->next)
  22976. printf("%4d ", p->number);
  22977. printf("\n");
  22978. printtree(t->right);
  22979. }
  22980. }
  22981.  
  22982. static treenode *root = NULL;
  22983.  
  22984. void xrt_add(char *w, unsigned n)
  22985. {
  22986. root = addtree(root, w, n);
  22987. }
  22988.  
  22989. void xrt_print(void)
  22990. {
  22991. printtree(root);
  22992. }
  22993.  
  22994.  
  22995.  
  22996. Listing 4
  22997. /*
  22998. * xrt.h - cross-reference table interface
  22999. */
  23000. void xrt_add(char *w, unsigned n);
  23001. void xrt_print(void);
  23002.  
  23003.  
  23004. Listing 5
  23005. /*
  23006. * ln_seq.h - line number sequence interface
  23007. */
  23008.  
  23009. class ln_seq
  23010. {
  23011. public:
  23012. ln_seq();
  23013. void add(unsigned n);
  23014. void print();
  23015. private:
  23016. struct listnode
  23017. {
  23018. unsigned number;
  23019. listnode *next;
  23020. };
  23021. listnode *first;
  23022. };
  23023.  
  23024.  
  23025. Listing 6 
  23026. /*
  23027. * ln_seq.cpp - line number sequence implementation
  23028. */
  23029. #include <stdio.h>
  23030.  
  23031. #include "ln_seq.h"
  23032.  
  23033. ln_seq::ln_seq()
  23034. {
  23035. first =0;
  23036. }
  23037.  
  23038. void ln_seq::add(unsigned n)
  23039. {
  23040. listnode *p = first;
  23041. if (first == 0)
  23042. {
  23043. first = new listnode;
  23044. first->number = n;
  23045. first->next = NULL;
  23046. }
  23047. else
  23048. {
  23049. while (p->next != 0 && p->number != n)
  23050. p = p->next;
  23051. if (p->number != n)
  23052. {
  23053. p = p->next = new listnode;
  23054. p->number = n;
  23055.  
  23056. p->next = 0;
  23057. }
  23058. }
  23059. }
  23060.  
  23061. void ln_seq::print()
  23062. {
  23063. listnode *p;
  23064.  
  23065. for (p = first; p != 0; p = p->next)
  23066. printf("%4d ", p->number);
  23067. }
  23068.  
  23069.  
  23070. Listing 7
  23071. /*
  23072. * xrt.cpp - cross-reference table implementation
  23073. */
  23074. #include <stdio.h>
  23075. #include <stdlib.h>
  23076. #include <string.h>
  23077.  
  23078. #inctude "ln_seq.h"
  23079. #include "xrt.h"
  23080.  
  23081. struct treenode
  23082. {
  23083. char *word;
  23084. ln_seq lines;
  23085. treenode *left, *right;
  23086. };
  23087.  
  23088. static treenode *addtree
  23089. (treenode *t, char *w, unsigned n)
  23090. {
  23091. int cond;
  23092.  
  23093. if (t == NULL)
  23094. {
  23095. t = new treenode;
  23096. t->word
  23097. = strcpy((char *)malloc(strlen(w) + 1), w);
  23098. t->lines.add(n);
  23099. t->left = t->right = NULL;
  23100. }
  23101. else if ((cond = strcmp(w, t->word)) == 0)
  23102. t->lines.add(n);
  23103. else if (cond < 0)
  23104. t->left = addtree(t->left, w, n);
  23105. else
  23106. t->right = addtree(t->right, w, n);
  23107. return t;
  23108. }
  23109.  
  23110. static void printtree(treenode *t)
  23111. {
  23112. if (t != NULL)
  23113. {
  23114. printtree(t->left);
  23115.  
  23116. printf("%12s: ", t->word);
  23117. t->tines.print();
  23118. printf("\n");
  23119. printtree(t->right);
  23120. }
  23121. }
  23122.  
  23123. static treenode *root = NULL;
  23124.  
  23125. void xrt_add(char *w, unsigned n)
  23126. {
  23127. root = addtree(root, w, n);
  23128. }
  23129.  
  23130. void xrt_print(void)
  23131. {
  23132. printtree(root);
  23133. }
  23134.  
  23135.  
  23136. Listing 8
  23137. /*
  23138. * ln_seq.h - line number sequence interface
  23139. */
  23140.  
  23141. class ln_seq
  23142. {
  23143. public:
  23144. ln_seq();
  23145. void add(unsigned n);
  23146. void print();
  23147. private:
  23148. struct listnode
  23149. {
  23150. unsigned number;
  23151. listnode *next;
  23152. };
  23153. listnode *first, *last;
  23154. };
  23155.  
  23156.  
  23157. Listing 9
  23158. /*
  23159. * ln_seq.cpp - line number sequence implementation
  23160. */
  23161. #incLude <stdio.h>
  23162.  
  23163. #incLude "ln_seq.h"
  23164.  
  23165. ln_seq::ln_seq()
  23166. {
  23167. first = Last = 0;
  23168. }
  23169.  
  23170. void Ln_seq::add(unsigned n)
  23171. {
  23172. listnode *p;
  23173. if (first == 0 last->number != n)
  23174. {
  23175.  
  23176. p = new listnode;
  23177. p->number = n;
  23178. p->next = NULL;
  23179. if (first == 0)
  23180. first = p;
  23181. else
  23182. last->next = p;
  23183. last = p;
  23184. }
  23185. }
  23186. void ln_seq::print() 
  23187. {
  23188. listnode *p;
  23189.  
  23190. for (p = first; p != 0; p = p->next)
  23191. printf("%4d ", p->number);
  23192. }
  23193.  
  23194.  
  23195.  
  23196.  
  23197.  
  23198.  
  23199.  
  23200.  
  23201.  
  23202.  
  23203.  
  23204.  
  23205.  
  23206.  
  23207.  
  23208.  
  23209.  
  23210.  
  23211.  
  23212.  
  23213.  
  23214.  
  23215.  
  23216.  
  23217.  
  23218.  
  23219.  
  23220.  
  23221.  
  23222.  
  23223.  
  23224.  
  23225.  
  23226.  
  23227.  
  23228.  
  23229.  
  23230.  
  23231.  
  23232.  
  23233.  
  23234.  
  23235.  
  23236.  
  23237.  
  23238.  
  23239. Implementer's Notebook
  23240.  
  23241.  
  23242. Implementing A trap Command
  23243.  
  23244.  
  23245.  
  23246.  
  23247. Don Libes
  23248.  
  23249.  
  23250. Don Libes is a computer scientist at the National Institute of Standards and
  23251. Technology. He is also the author of Life With UNIX, published by
  23252. Prentice-Hall. His electronic mail address is libes@cme.nist.gov. He can also
  23253. be reached at NIST, Bldg. 220, Rm A-127, Gaithersburg, MD 20899.
  23254.  
  23255.  
  23256. Almost everyone has written programs for interpreters (such as UNIX shell
  23257. scripts or DOS .BAT files). If you've ever tried to make them bulletproof,
  23258. you've probably handled signals.
  23259. The UNIX Bourne shell lets the user dictate actions using the trap command.
  23260. Many other interpreters offer a similar feature. For example, the C-shell
  23261. calls it onintr, and dBase calls it on error and on key. All of these commands
  23262. work similarly.
  23263. Even though they have different names, the idea is the same. In the event of a
  23264. signal (error), you can get control and clean up before the interpreter exits.
  23265. You might, for example, want to remove temporary files before exiting. In this
  23266. column, I'll show how you can implement a trap-like command for your own
  23267. interpreter.
  23268. The trap command I'm going to create has the following syntax:
  23269. trap [action] [list of signals]
  23270. The action is a statement in the language of the interpreter. Early UNIX
  23271. shells only accepted signal numbers, but modern shells (such as Gnu's bash)
  23272. accept the actual signal macro names as defined in signal.h. This is easier on
  23273. the user and more portable.
  23274. As an example, if you write a bash script that has the line
  23275. trap "echo ouch" SIGABRT
  23276. and the interpreter calls abort (which raises the SIGABRT signal), the
  23277. interpreter will print ouch.
  23278.  
  23279.  
  23280. Implementing trap -- It's Obvious!
  23281.  
  23282.  
  23283. To implement trap, you must simply call signal with the appropriate signal
  23284. translated from its macro name. Unfortunately, the rest is not so simple. The
  23285. second argument to signal is a pointer to a C function. The script programmer
  23286. cannot easily describe the action, which must be coded in the language of the
  23287. interpreter, as something like
  23288. signal(SIGABRT, ????);
  23289. You need to make the association between C code and an interpreter statement.
  23290. So first assume that the interpreter statement is stored in a character array
  23291. (called sigabrt_action here). Then call signal with a function, called
  23292. sigabrt_handler, that evaluates the interpreter statement.
  23293. void
  23294. sigabrt_handler() {
  23295. eval (sigabrt_action);
  23296. }
  23297. Here, eval evaluates the string containing the statement that the user
  23298. originally associated with this signal via trap. That's the basic idea. You
  23299. must still add a few things to sigabrt_handler. sigabrt_action must be set
  23300. before passing it as an argument. If not, you must perform a default action.
  23301.  
  23302.  
  23303. A Better Solution
  23304.  
  23305.  
  23306. To finish this implementation, you must write an X_handler and X_action for
  23307. each possible signal X, ending up with sigabrt_handler, sigfpe_handler,
  23308. sigill_handler, and so on. While standard C defines only six signals, some
  23309. implementations define dozens. Since the code for all signal handler is going
  23310. to be similar, perhaps we can do better.
  23311. We can. When a signal handler is executed, it is passed the signal number as
  23312. its first argument. You can replace all of those distinct functions with a
  23313. single generic signal handler:
  23314. /* define NSIG to be the number of signals
  23315. in your system */
  23316. char *actions[NSIG];
  23317. void
  23318. sig_handler(sig) {
  23319. eval (actions [sig] );
  23320. }
  23321. Listing 1 shows the complete implementation of the trap command.
  23322. It is common and convenient to define the signal 0 to be raised upon program
  23323. exit, even though the C standard doesn't define such a signal. [Nor does it
  23324. promise that the value 0 belongs to no other signal. - pjp] You can write:
  23325. trap "echo exiting" 0
  23326. so that the script will write exiting as its last act, regardless of how it
  23327. exits. (This is analogous to the onexit function in C.) Since actions [0]
  23328. already exists, supporting signal 0 costs very little. The interpreter must
  23329. simply call sig_handler (0) at termination.
  23330. Listing 2 is a rewritten signal handler that does the other things I mentioned
  23331. earlier. I've added a few useful features to sig_handler. The first is an
  23332. assertion that verifies that the delivered signal is defined. This protects
  23333. you against accessing outside the action array.
  23334. Next, you must check if actions[sig] is defined. If actions[sig] isn't
  23335. defined, sig_handler could not have even been called, except for the case
  23336. where sig == 0. The 0 case occurs because sighandler(0) is always called at
  23337. interpreter exit, whether or not a user action has been defined. Once
  23338. verified, signal is called to reinstall the signal handler for systems that
  23339. require this.
  23340. Lastly, the handler executes a longjmp, just in case you have defined a valid
  23341. environment in which to jump. For example, you might protect a call to func
  23342. using the code in Listing 3. While the trap's signal handler forces setjmp to
  23343. return 1 (thereby restarting func), other signal handlers could have setjmp
  23344. return other values, allowing func to be aborted. As an aside, it is odd that
  23345. the C standard does not allow longjmp to have setjmp return 0. There is no
  23346. good reason for it! [As a further aside, yes there is. The program calling
  23347. setjmp must be able to distinguish the intial call from a longjmp return. --
  23348. pjp]
  23349.  
  23350.  
  23351.  
  23352. The Final Version
  23353.  
  23354.  
  23355. Now, I'll present the final version of the procedures to implement the trap
  23356. command. In order to support a few features that I'll explain in a moment,
  23357. signal information will be stored in the following array of structs, one per
  23358. signal:
  23359. struct {
  23360. char *action;
  23361. void (*defaultX) ();
  23362. char *name;
  23363. } signals[NSIG];
  23364. action is the interpreter statement to execute when the signal arrives. If
  23365. none has been defined, action equals 0 and defaultX, a C function, is
  23366. executed. (I put the X at the end because default is a keyword!) name is the C
  23367. macro defining the signal.
  23368. Initializing this array is a little painful (Listing 4), but makes the rest of
  23369. the code easy (see Listing 4). First, the signal names are initialized. The
  23370. first six are defined by the C standard. The next one (signal 0) is my
  23371. convention for onexit that I mentioned earlier. After this are several
  23372. nonstandard (but common) signals. By using #ifdefs, the code is portable, yet
  23373. supports extensions if they are there. Feel free to augment this list of
  23374. signals. I've only provided a couple to give you the idea.
  23375. The last part of init_trap saves the default actions. Since signal does a
  23376. destructive read, you must call it twice, the second to rewrite the original
  23377. value. If you want to provide a default action, you should register it with
  23378. signal before ini_trap is run. Then, if the user ever sets the signal to
  23379. SIG_DFL, it will be set to your default, not the system's.
  23380. Listing 5 shows the final cmd_trap. I'll explain how it is to be used as I
  23381. step through the code.
  23382. First, notice that arguments are passed using the argc/argv convention. Even
  23383. though this isn't a main routine, it's a simple way of handling differing
  23384. arguments that are all of type char *. (Compare this to using stdargs.)
  23385. If you provide no signals as arguments, trap just lists the signals for which
  23386. it has actions defined by calling print_signal. See Listing 6. If you pass
  23387. signals as arguments with no action supplied, trap lists the actions for just
  23388. the named signals. signal_to_string is a trivial procedure that returns an
  23389. appropriate printable string describing the given signal.
  23390. The function string_to_signal converts signal names or numbers to the int form
  23391. required by signal. In Listing 7, string_to_signal first tries to interpret
  23392. the argument as a signal number, and then as a signal name. It even tries it
  23393. without the prefix SIG. If the last element of argv isn't a signal, the
  23394. program assumes it is an action. Fortunately, an action is not likely to look
  23395. exactly like a signal.
  23396. The remainder of cmd_trap loops through the signals, defining or printing them
  23397. as appropriate. Any signal names that are prefaced by an asterisk are reserved
  23398. to the interpreter and cannot be set by the user. As an example, in init_trap,
  23399. I reserved SIGFPE this way.
  23400. In order to set the signal, sig_trap discards the old action and saves the new
  23401. action, allocating and freeing space as necessary. The reason an entirely new
  23402. copy of the action is saved rather than just a pointer to it is that we must
  23403. assume the interpreter can throw away strings after a command returns. For
  23404. example, the action might have been stored in a variable that is overwritten
  23405. after the call to trap.
  23406. Lastly, the program calls signal. This system call recognizes two special
  23407. actions. If the action is SIG_IGN, the signal is subsequently ignored. If the
  23408. action is SIG_DFL, the signal subsequently gets the default treatment. In this
  23409. case, the program sets the action to 0 so that signals with not appear in
  23410. listings, just as when the program begins.
  23411. sig_handler requires a few syntactic changes to support the new signal
  23412. structure. Listing 8 shows the new version.
  23413.  
  23414.  
  23415. Conclusion
  23416.  
  23417.  
  23418. These routines provide a solid implementation of a complete and flexible
  23419. signal-trapping command for a typical command interpreter. You could add a
  23420. little more error checking. In particular, signal can return SIG_ERR upon
  23421. failure. I'll leave you with the task of integrating this into the code, and
  23422. thinking about when signal could possibly fail.
  23423. Thanks to Steve Clark and Sarah Wallace for offering helpful suggestions on
  23424. this column.
  23425.  
  23426. Listing 1
  23427. int
  23428. cmd_trap(action, list,count)
  23429. char *action; /* eval upon signal */
  23430. int *list; /* list of sigs */
  23431. int count; /* how many sigs */
  23432. {
  23433. int i;
  23434.  
  23435. for (i=0;i<count;i++,list++) {
  23436. actions[*list] = action;
  23437. signal(*list,sig_handler);
  23438. }
  23439. }
  23440.  
  23441. void
  23442. init_trap()
  23443. {
  23444. int i;
  23445.  
  23446. for (i=0;i<NSIG;i++) actions[i] = 0;
  23447. }
  23448.  
  23449.  
  23450. Listing 2
  23451. jmp_buf env;
  23452. int valid_env = 0;
  23453.  
  23454. void sig_handler(sig)
  23455.  
  23456. int sig;
  23457. {
  23458. assert(sig >= 0 && sig < NSIG);
  23459.  
  23460. if(!actions[sig]) {
  23461. /* always an error except when sig == 0 */
  23462. if (sig == 0) return;
  23463. fprintf(stderr,"unexpected signal (%d) delivered\n", sig);
  23464. } else {
  23465. signal(sig, sig_handler);
  23466. eval(actions[sig]);
  23467. }
  23468.  
  23469. if(valid_env) longjmp(env, 1);
  23470. }
  23471.  
  23472.  
  23473. Listing 3
  23474. if (1 >= setjmp(env)) {
  23475. valid_env = TRUE;
  23476. func();
  23477. }
  23478. valid_env = FALSE;
  23479.  
  23480.  
  23481. Listing 4
  23482. void
  23483. init_trap()
  23484. {
  23485. int i;
  23486.  
  23487. for (i=0;i<NSIG;i++) {
  23488. signals[i].name = 0;
  23489. }
  23490.  
  23491. /* defined by C standard */
  23492. signals[SIGABRT].name = "SIGABRTI";
  23493. signals[SIGFPE ].name = "*SIGFPE";
  23494. /* "*" means reserved to us - see below */
  23495. signals[SIGILL ].name = "SIGILL";
  23496. signals[SIGINT ].name = "SIGINT";
  23497. signals[SIGSEGV].name = "SIGSEGV";
  23498. signals[SIGTERM].name = "SIGTERM";
  23499.  
  23500. /* our own extension */
  23501. signals[0].name = "ONEXIT";
  23502.  
  23503. /* nonstandard but common */
  23504. #if defined(SIGHUP) /* hangup */
  23505. signals[SIGHUP ].name = "SIGHUP";
  23506. #endif
  23507.  
  23508. #if defined(SIGALRM) /* alarm clock */
  23509. signals[SIGALRM].name = "SIGALRM";
  23510. #endif
  23511.  
  23512. #if defined(SIGPWR) /* imminent power failure */
  23513. signals[SIGPWR ].name = "SIGPWR";
  23514. #endif
  23515.  
  23516.  
  23517. #if defined(SIGIO) /* input/output signal */
  23518. signals[SIGIO ].name = "SIGIO";
  23519. #endif
  23520.  
  23521. for (i=0;i<NSIG;i++) {
  23522. signals[i].action = 0;
  23523. signals[i].defaultX = signal(i,SIG_DFL);
  23524. signal(i,signals[i].defaultX);
  23525. }
  23526. }
  23527.  
  23528.  
  23529. Listing 5
  23530. #include <stdio.h>
  23531. #include <signal.h>
  23532. #include <assert.h>
  23533. #include <setjmp.h>
  23534. #include <string.h>
  23535. #include <stdlib.h>
  23536.  
  23537. #define streq(x,y) (0 == strcmp(x,y))
  23538.  
  23539. /* reserved to us if name begins with asterisk */
  23540. #define SIG_RESERVED(x) (signals[x].name[0] == '*')
  23541.  
  23542. void sig_handler();
  23543. void print_signal();
  23544.  
  23545. enum cmd_status {CMD_OK, CMD_ERROR};
  23546.  
  23547. enum cmd_status
  23548. cmd_trap(argc,argv)
  23549. int argc;
  23550. char **argv;
  23551. {
  23552.  
  23553. enum cmd_status rc = CMD_OK;
  23554. char *action = 0;
  23555. int len; /* length of action */
  23556. int i;
  23557.  
  23558. if (argc == 1) {
  23559. for (i=0;i<NSIG;i++) print_signal(i);
  23560. return(rc);
  23561. }
  23562.  
  23563. if (-1 == string_to_signaL(argv[argc-1])) {
  23564. action = argv[argc-1];
  23565. argc--;
  23566. }
  23567.  
  23568. for (i=1;i<argc;i++) {
  23569. int sig = string_to_signal(argv[i]);
  23570. if (sig < 0 sig >= NSIG) {
  23571. fprintf(stderr,"trap: invalid signal %s",argv[i]);
  23572. rc = CMD_ERROR;
  23573. break;
  23574. }
  23575.  
  23576.  
  23577. if(!action) {
  23578. print_signal(sig);
  23579. continue;
  23580. }
  23581.  
  23582. if (SIG_RESERVED(sig)) {
  23583. fprintf(stderr,"trap: cannot trap (%s)",argv[i]);
  23584. rc = CMD_ERROR;
  23585. break;
  23586. }
  23587.  
  23588. if (signals[sig].action) free(signals[sig].action);
  23589.  
  23590. if (streq(action,"SIG_DFL")) {
  23591. signal(sig,signals[sig].defaultX);
  23592. signals[sig].action = 0;
  23593. } else {
  23594. len = 1 + strlen(action);
  23595. if (0 == (signals[sig].action = malloc(len))) {
  23596. fprintf(stderr,"trap: malloc failed");
  23597. signal(sig,signals[sig].defaultX);
  23598. rc = CMD_ERROR;
  23599. break;
  23600.  
  23601. }
  23602. memcpy(signals[sig].action,action, len);
  23603. if (streq(action,"SIG_IGN")) {
  23604. signal(sig,SIG_IGN);
  23605. } else signal(sig,sig_handler);
  23606. }
  23607. }
  23608. return(rc);
  23609. }
  23610.  
  23611.  
  23612. Listing 6
  23613. char *
  23614. signal_to_string(sig)
  23615. int sig;
  23616. {
  23617. if (sig < 0 sig > NSIG) {
  23618. return("SIGNAL OUT OF RANGE");
  23619. } else if (!signals[sig].name) {
  23620. return("SIGNAL UNKNOWN");
  23621. } else return(signals[sig].name + SIG_RESERVED(sig));
  23622. }
  23623.  
  23624. void
  23625. print_signal(sig)
  23626. int sig;
  23627. {
  23628. if (signals[sig].action) printf("%s (%d): %s\n",
  23629. signal_to_string(sig),sig,signals[sig].action);
  23630. }
  23631.  
  23632.  
  23633. Listing 7
  23634. /* given signal index or name as string, */
  23635.  
  23636. /* returns signal index or -1 if bad arg */
  23637. string_to_signal(s)
  23638. char *s;
  23639. {
  23640. int sig;
  23641. char *name;
  23642.  
  23643. /* try interpreting as an integer */
  23644. if (1 == sscanf(s,"%d",&sig)) return(sig);
  23645.  
  23646. /* try interpreting as a string */
  23647. for (sig=0;sig<NSIG;sig++) {
  23648. name = signals[sig].name;
  23649. if (SIG_RESERVED(sig)) name++;
  23650. if (streq(s,name) streq(s,name+3))
  23651. return(sig);
  23652. }
  23653. return(-1);
  23654. }
  23655.  
  23656.  
  23657. Listing 8
  23658. jmp_buf env;
  23659. int valid_env = 0;
  23660.  
  23661. void
  23662. sig_handler(sig)
  23663. int sig;
  23664. {
  23665.  
  23666. assert(sig >= 0 && sig < NSIG);
  23667.  
  23668. if (!signals[sig].action) {
  23669. /* always an error except when sig == 0 */
  23670. if (sig == 0) return;
  23671. fprintf(stderr,"unexpected signal delivered - %s (%d)\n",
  23672. signal_to_string(sig),sig);
  23673. } else {
  23674. signal(sig, sig_handler);
  23675. eval(signals[sig].action);
  23676. }
  23677.  
  23678. if (valid_env) longjmp(env, 1);
  23679. }
  23680.  
  23681.  
  23682.  
  23683.  
  23684.  
  23685.  
  23686.  
  23687.  
  23688.  
  23689.  
  23690.  
  23691.  
  23692.  
  23693.  
  23694.  
  23695.  
  23696.  
  23697.  
  23698.  
  23699. On The Networks
  23700.  
  23701.  
  23702. New Moderator Needed
  23703.  
  23704.  
  23705.  
  23706.  
  23707. Sydney S. Weinstein
  23708.  
  23709.  
  23710. Sydney S. Weinstein, CDP, CCP is a consultant, columnist, author, and
  23711. president of Datacomp Systems, Inc., a consulting and contract programming
  23712. firm specializing in databases, data presentation and windowing, transaction
  23713. processing, networking, testing and test suites, and device management for
  23714. UNIX and MS-DOS. He can be contacted care of Datacomp Systems, Inc., 3837
  23715. Byron Road, Huntingdon Valley, PA 19006-2320 or via electronic mail on the
  23716. Internet/Usenet mailbox syd@DSI.COM (dsinc!syd for those who cannot do
  23717. Internet addressing).
  23718.  
  23719.  
  23720. Workload has hit another of the source group moderators on USENET network
  23721. news. Brandon Allbery declared that he is too busy to moderate
  23722. comp.sources.misc and asked in early October for volunteers to do the job.
  23723. Thus for the interim, comp.sources.misc has been very quiet.
  23724. The new moderator should be from a site directly connected to the Internet, so
  23725. that he can update the archive sites easily. An efficient moderator needs
  23726. about two to four hours per week just to maintain things. (I think Brandon
  23727. underestimated the time necessary since it really takes more time to do a good
  23728. job.) Some of Brandon's last postings are highlighted here.
  23729. For those using the MS-DOS shell posted by Ian Stewartson
  23730. <iarwqer@datlog.co.uk>, a major patch was issued as Volume 14, Issues 65 and
  23731. 66, bringing the shell to version 1.6.3. Many bugs were fixed and support for
  23732. POSIX P1003.2 commands has been started. POSIX is the standards body for an
  23733. operating system that looks somewhat like UNIX. P1003.2 specifies the commands
  23734. such an operating system should have, such as listing directories and copying
  23735. files.
  23736. The mail and news pretty-printer by Rich Burridge <rburridge@sun.com> has been
  23737. enhanced and reposted. This new version, mp-2.4.5, will print mail or news
  23738. messages using PostScript in a "pretty" format. It supports landscape mode and
  23739. will print multiple pages in a reduced size on a single sheet of paper. It
  23740. also can print normal ASCII files as well as a personal organizer format. For
  23741. those with time to spare, a TODO list is included. mp-2.4.5 is Volume 14,
  23742. Issues 67 and 68.
  23743. C++ is filtering through the nets. S. Manoharan <sam@lfcs.edinburgh.ac.uk> has
  23744. contributed a C++ class set for building event-based discrete event
  23745. simulations (Volume 14, Issue 69). Silo provides classes for Entity, Event,
  23746. Resource and Bin, and is based on M. H. MacDougall's book Simulating Computer
  23747. Systems: Techniques and Tools.
  23748. Wayne Davison <davison@dri.com> has invented a new context difference listing
  23749. format that produces output roughly 25 percent smaller than the standard
  23750. context diff output. Context diffs show changes made to a file by outputting
  23751. the old and new lines with several lines around them (context). diffs allow
  23752. automated patching programs (such as patch) to find where to make changes even
  23753. if line numbers have changed. This new format, called unidiff, includes the
  23754. context only once, showing the old and new lines within the same "hunk" of
  23755. output. unidiff, Volume 14, Issue 70, includes a patch to gnudiff 1.14 to
  23756. output unidiff's, a patch to Larry Wall's patch program (PL12) to accept
  23757. unidiff, and programs to translate to and from old style context diffs to
  23758. unidiff's format.
  23759. If you're using the old troff and an HP DeskJet printer, then you need
  23760. cat2desjet. Vasilis Prevelakis <vp@cui.unige.ch> contribution takes old troff
  23761. output (for the c/a/t phototypesetter) and converts it to a format suitable
  23762. for the HP Deskjet. Based on the cat2lj (c/a/t to HP LaserJet) program,
  23763. cat2desjet runs in two modes, a pure graphic bitmap mode and a soft font
  23764. download mode. The latter supports a lower resolution draft mode whose speed
  23765. warrants the loss in resolution for drafts (Volume 14 Issues 71-73).
  23766. Elwood Downey <downey@dimed.com> has updated his ephem (an interactive
  23767. astronomical ephemeris) program to version 4.21. New features include more
  23768. magnitude models, bug fixes, a Skydome watch format, and VMS support. The
  23769. large posting is Volume 14, Issues 76-81.
  23770. Tired of sorting out your files in an ls listing? Kent Landfield
  23771. <kent@sparky.imd.sterling.com> has contributed lc, which separates the files
  23772. into groups of like types and then displays the information in columns (Volume
  23773. 14, Issues 82 and 83). Note for Xenix users, lc is not the same as lc under
  23774. Xenix, which is just ls -C.
  23775. For those into AI, Donald Tveter <drt@chinet.chi.il.us> has contributed the
  23776. routines he used for the experiments in his AI textbook. The routines perform
  23777. back-propagation using a very efficient algorithm. Fast-backprop is Volume 14,
  23778. Issues 84-87.
  23779. Seeing your news build up and wondering in what groups they reside? Tired of
  23780. seeing du count the subdirectories in the parent directory? Then Chip
  23781. Rosenthal's <chip@chinacat.unicom.com> enhanced du (disk usage) is for you. A
  23782. simple useful tool that is Volume 14, Issue 88.
  23783. David Kirschbaum <kirsch@usasoc.soc.mil> contributed a portable unzip 3.1 for
  23784. Volume 14, Issue 102-106. Unzip from the INFO-ZIP project extracts files
  23785. stored in PKZIP format from UNIX, Minix and Atari. It works on both little and
  23786. big endian machines and on machines with different word lengths (except for
  23787. the Cray's). Unzip only extracts files. It cannot build zip files.
  23788. The system V enhanced getty, previewed in the first quarter of 1990, has been
  23789. released by Paul Sutcliffe, Jr. <paul@devon.lns.pa.us> as version 2.0. It
  23790. supports both standard (getty) and bi-directional (uugetty) modes, use of
  23791. runtime defaults file, modem chat scripts to set up the modems, and incoming
  23792. autobauding via the connect string. It's Volume 15, Issues 4-8.
  23793. If you aren't connected to Internet, you can't run NTP (network time protocol)
  23794. to automatically set your UNIX system to accurate time. However, you can
  23795. install a Heath GC1000 WWV radio clock and run gc1000 to set the UNIX time
  23796. from the clock. When run periodically from cron, gc1000 will synchronize the
  23797. system time with the clock, correcting for drift. gc1000 does not change the
  23798. time if the clock has lost the WWV signal, or if the time is off by more than
  23799. a certain number of minutes (to allow for errors in the time reception not to
  23800. effect the system). It also supports a debugging mode for checking the
  23801. interface. It's a relatively small program and is Volume 15, Issue 9.
  23802. Those sites running an archive and desiring a mail-based archive server should
  23803. obtain rnalib2 by Paolo Ventafridda <venta@i2ack.sublink.org>. An advanced
  23804. mail-server, rnalib2 allows remote users to ask for text or binary files via
  23805. ordinary e-mail. It can compress/uuencode/btoa/uusend/e-mail or uucp a file as
  23806. needed (Volume 15 Issues 10-13).
  23807. gnuplot, the command-line driven interactive function plotting utility, has
  23808. been upgraded via a very large patch. Besides bug fixes, it includes many new
  23809. output drivers including X11. Contributed by Russell Lang
  23810. <rjl@monu1.cc.monash.edu.au>, it is Volume 15, Issues 15-19.
  23811. If you've ever considered moving from SCCS to RCS (see my column in CUJ,
  23812. vol.7, no.4), sccs2rcs_kc from Kenneth H. Cox <kenstir@viewlogic.com> will do
  23813. the trick. It converts the SCCS s-file into the identical RCS history file
  23814. without altering or deleting the sccs file. Date, time, author, comments, and
  23815. branches are all preserved (Volume 15, Issue 22).
  23816. GNU-Emacs's calculator has been updated, and the update patch is more than
  23817. 1Mb. This 20-part patch file takes calc from 1.04 to 1.05. David Gillespie
  23818. <daveg@csvax.cs.caltech.edu> made it up, and if you have ftp access, follow
  23819. his instructions, ftp the full source. It must be easier than making sure a
  23820. 1Mb patch all worked. If not, it is Volume 15, Issues 28-47.
  23821. If you keep a file as a bunch of index cards and are looking for a way to
  23822. easily do the same on the computer, you need cardfile. Dave Lampe
  23823. <dplace!djl@pacbell.com> contributed cardfile as Volume 15, Issues 49-51. It
  23824. uses the metaphor of a stack of index cards with fields and subfields.
  23825. Searching and simple formatting are supported.
  23826. Last time I mentioned dmake, an enhanced make like utility at version 3.5,
  23827. there was a copyright problem in that version, and the author has corrected it
  23828. and released version 3.6, requesting that all copies of 3.5 be deleted. If you
  23829. have copies of 3.5, please comply. The new version has no loss in function.
  23830. Dennis Vadura <dvarudra@watdragon.waterloo.edu> has contributed the new
  23831. version for Volume 15, Issues 52-77. A patch to dmake 3.6, patch1, was posted
  23832. on Nov. 1, 1990 to comp.sources.bugs. This five-part patch fixes minor UNIX
  23833. problems and major problems with Microsoft C 6.0 and TCC 2.0. dmake is
  23834. different from other makes because it supports significantly enhanced macro
  23835. facilities, enhanced inference algorithms, support for file system transversal
  23836. both during inference and during the make, parallel makes on those
  23837. architectures that support it, attributed targets and text diversions. It is
  23838. portable to both UNIX and DOS and includes support for command.com and the MKS
  23839. korn shell under DOS.
  23840.  
  23841.  
  23842. Rich Is Still Slowly Posting Sources...
  23843.  
  23844.  
  23845. Slowly, but still there have been postings appearing in comp.sources.unix, but
  23846. they have been few and far between.
  23847. Chip Salzenberg's Deliver program was upgraded in a 10-part patch. Deliver
  23848. will automatically handle sophisticated algorithms for handling inbound
  23849. electronic mail. Deliver can save the electronic mail in different files, and
  23850. answer it for you automatically. New features include normal and error logs,
  23851. aborting of overly recursive delivery invocations and more configurability,
  23852. plus bug fixes. The original posting was at patchlevel 1, so this patch, patch
  23853. 2 is Volume 23, Issues 1-10.
  23854. If you are feeding network news a large number of sites, and running out of
  23855. cycles, newsxd from Chris Myers <chris@wugate.wustl.edu> will help you control
  23856. the load. newsxd is a configurable daemon that allows for a more controlled
  23857. execution of nntpxmits and sendbatches. It can vary when they execute by time
  23858. of day, type of service, and system load. Newsxd is Volume 23, Issues 11-13.
  23859. For a simple spreadsheet program that is free, try sc, now up to version 6.8.
  23860. Contributed to Volume 23, Issues 23-25 by Jeff Buhrt <sawmill!buhrt>, sc now
  23861. supports ASCII files via psc, plus cleans up many bugs. Patches to this have
  23862. appeared in comp.sources.bugs including files posted on Sep. 27, Oct. 29, and
  23863. Oct. 31. These patches take sc to version 6.10. Eventually these patches will
  23864. appear in comp.sources.unix.
  23865. Pseudo-terminals are a method of running a process without having a real
  23866. terminal connected all the time. Dan Bernstein <brnstnd@kramden.acf.nyu.edu>
  23867. has contributed pty. Pty is the sole interface between pseudo-terminals and
  23868. the rest of UNIX. It supports improved security, disconnect and reconnect,
  23869. full pty control and several utilities. It does not yet support POSIX ptys,
  23870. which differ slightly from the older BSD ptys, but port is planned. Pty is
  23871. Volume 23, Issues 31-36.
  23872. Paul Vixie <vixie@vixie.sf.ca.us> has contributed the cron, which he also
  23873. contributed to Berkeley for 4.4BSD. This version of cron supports the System V
  23874. style of multiple cron tables, one for each user running cron. (Cron is the
  23875. UNIX time-based scheduling program. It runs programs at specific regular
  23876. intervals, such as once a day at 3:00 am, or every five minutes, or the first
  23877. Monday of every month.) Vixie-cron is Volume 23, Issues 28-30.
  23878. The latest version of flex, a freely distributable replacement for lex, the
  23879. Unix lexical analyzer is Volume 23, Issues 37-46. Version 2.3 at patchlevel 6
  23880. supports UNIX, Atari, MS-DOS and VMS. It's contributed by Vern Paxson
  23881. <vern@cs.cornell.edu>.
  23882.  
  23883.  
  23884. Fast Update Cycles Getting Faster
  23885.  
  23886.  
  23887. The wonder of the "net" is that support is so fast, new versions appear before
  23888. I can even get the old versions mentioned in the column. The group
  23889. comp.sources.games is a wonderful example.
  23890. In my last column, I introduced vcraps, the full screen casino-style craps
  23891. game based on curses. Now Robert Steven Glickstein <bobg+@andrew.cmu.edu> has
  23892. updated it to version 2, which incorporates all the patches and more fixes. It
  23893. is Volume 11, Issues 48 and 49.
  23894. The othello-like game Reve, has been upgraded to version 1.1. It supports
  23895. SunView, XView, X11 and termcap modes. Reve offers nine levels of difficulty,
  23896. from beginner to tournament. The authors plan to enter the program in the
  23897. University of Waterloo Computer Science Club's Seventh Annual Computer Othello
  23898. Tournament. Rich Burridge <rburridge@sun.com> and Yves Gallot
  23899. <galloty@cernvax.cern.ch> submitted it for Volume 11, Issues 52-58 with patch
  23900. 1 in Volume 11, Issues 61-64.
  23901. Another multi-user game is Internet Go, which allows users to play go in real
  23902. time over a TCP/IP network. It requires a BSD style UNIX system and INET
  23903. sockets. Igo is contributed by Greg Hale <hale@scam.berkeley.edu> and Adrian
  23904. Mariano <agrian@milton.u.washington.edu> for Volume 11, Issues 66 and 67.
  23905. Ski, a skiing game from Mark Stevans <resumix!stevans@decwrl.dec.com> in
  23906. Volume 11, Issue 69, has been updated again, this time to version 6.0.. This
  23907. text-based skiing simulation offers an infinite slope, trees, ice, bar ground,
  23908. and the snowman. The goal is to get as far down the slope as you can.
  23909. Unfortunately, your jet-powered skis only go on backwards so you cannot see
  23910. where you are going, only where you have been. It has some strange options,
  23911. and apparently is still played enough, even after eight years, to warrant an
  23912. update.
  23913. Tired of loosing at nethack? A spoiler's file was contributed by Alan Light
  23914. <wheaton!alight>, Ken Roth <wheaton!kroth>, and Paul Waterman <wheaton!water>.
  23915. It was compiled by playing nethack, and from electronic mail and news groups.
  23916. Warning, this is a spoiler file, so if knowing the answer in advance ruins the
  23917. fun, don't get Volume 11, Issues 71-74.
  23918.  
  23919.  
  23920.  
  23921. Previews From alt.sources
  23922.  
  23923.  
  23924. Calendar programs still abound in alt.sources. New this time are a non
  23925. postscript version for use on line printers using 132x66 sized paper. Calendar
  23926. from Andrew Rogers <rogers@sud509.ed.ray.com> generates one month per page and
  23927. was posted Sept. 27, 1990. Pcal has also been updated to version 2.5 and now
  23928. supports having the left-most day of the week be user-selectable, some
  23929. reformatting of the calendar and cleanup of the resulting postscript output.
  23930. By making the day numbers smaller, nine lines of text are possible for each
  23931. day. The newest pcal was posted by Joseph A Brownlee <jbr@cbnews.att.com> on
  23932. Oct. 6, 1990.
  23933. If your old C compiler only supports eight-character variable names, then
  23934. unique-nms posted by Jean- Pierre Radley <jpradely!jpr> can help. It creates a
  23935. mapping of long names to short ones. Unique-nms was written by the late Fred
  23936. Buck. It was posted on Oct. 14, 1990.
  23937. A useful utility, cktar, will compare the contents of a tar archive against
  23938. the actual disk files. Useful to see what has changed without un-tar-ing the
  23939. entire archive into a parallel directory and doing a compare. It also checks
  23940. for file ownership and permissions. It is bytewise compare program, not a
  23941. source diff, so it also handles binary files. Cktar was posted by Warren
  23942. Tuckey <wht@n4hgf.mt-park.ga.us on Nov. 10, 1990.
  23943. If you need to read from compressed files, Graham Toal has provided a library
  23944. routine set to open, read, and close compressed files. It works on UNIX and
  23945. DOS and even supports 16-bit compress under DOS. Included is a version of
  23946. zcat.Zlib was posted on Nov. 15, 1990.
  23947. Still using CP/M? How about a CP/M emulator. It allows a UNIX system to
  23948. emulate an 8080 running CP/M. It was posted on Nov. 15, 1990 by D'Arcy J. M.
  23949. Cain <druid!darcy> in three parts.
  23950. Psroff, the old-troff (c/a/t version) to postscript converter was updated to
  23951. version 2.0, patchlevel 5 in a 16-part posting (complete release, not a patch)
  23952. by Chris Lewis <ecicrl@clewis>. It supports Postscript, HP Laserjet, and HP
  23953. Deskjet. Psroff was posted on Nov. 17, 1990.
  23954.  
  23955.  
  23956.  
  23957.  
  23958.  
  23959.  
  23960.  
  23961.  
  23962.  
  23963.  
  23964.  
  23965.  
  23966.  
  23967.  
  23968.  
  23969.  
  23970.  
  23971.  
  23972.  
  23973.  
  23974.  
  23975.  
  23976.  
  23977.  
  23978.  
  23979.  
  23980.  
  23981.  
  23982.  
  23983.  
  23984.  
  23985.  
  23986.  
  23987.  
  23988.  
  23989.  
  23990.  
  23991.  
  23992.  
  23993.  
  23994.  
  23995.  
  23996.  
  23997.  
  23998.  
  23999.  
  24000.  
  24001.  
  24002.  
  24003.  
  24004.  
  24005.  
  24006. Editor's Forum
  24007. Greetings from the Land of Oz. That's what many Australians call their
  24008. country, with their usual mixture of happy brashness and tongue-in-cheek
  24009. self-deprecation. They call themselves Aussies, pronounced OZ-ees, not AW-sees
  24010. -- which tells you why they claim Dorothy's dreamland as their own.
  24011. I have been here just over a week now. Already, I am growing comfortable with
  24012. driving on the left. I take it for granted that mine is the funny accent, not
  24013. everyone else's. (Moving north to Massachusetts a decade ago gave me a taste
  24014. of that experience.) I still marvel at swimming in the South Pacific when it
  24015. is snowing back home. I never quite adjust to seeing Orion upside down or
  24016. steering by the Southern Cross instead of the Little Dipper.
  24017. My computers arrived intact. It took but an hour to locate power cords to get
  24018. their batteries recharging. Within a day or two, I got the funny phone
  24019. connector that put my modem back in touch with the world. Two days later, I
  24020. had the cables I needed to drive the laser printers I'll be using here. My
  24021. good friend John O'Brien, of Whitesmiths, Australia, gets credit for all those
  24022. hardware successes.
  24023. Software has been more of a problem, but not insurmountable. My e-mail now
  24024. gets as far as John's computer across town. It is still a patchwork to get
  24025. mail those last few kilometers. I paid an earl's ransom for Bitstream fonts
  24026. that refuse to slide down a serial cable because they were installed with a
  24027. parallel interface in mind. Twice I have made frantic calls to the U.S. to
  24028. have someone read passages from manuals I thought I could leave behind.
  24029. It is still a miracle to me how quickly I reestablished diplomatic relations
  24030. with the other side of the world. Faxes, modems, second-day package delivery,
  24031. MS-DOS, Windows, and UNIX all work much the same here as back home. Many
  24032. things cost more, a very few cost less. Already, I am back in the business of
  24033. editing and writing.
  24034. Of course, I couldn't spend a year here without a lot of help back in Kansas.
  24035. Look at all those other names on the masthead. They have to work that much
  24036. harder because their editor is a bit further out of reach. I can look out my
  24037. office window in our rented house on Mackenzie Point and watch surfers dashing
  24038. into Tamarama Bay. Occasionally, that inspires a twinge of guilt that my
  24039. decision to come here has demanded so much help from others. (Only
  24040. occasionally, mind you.)
  24041. There's no place like home, to be sure. But it's also nice to go over the
  24042. rainbow at least once in your life.
  24043. P.J. Plauger
  24044. pjp@plauger.uunet.com
  24045.  
  24046.  
  24047.  
  24048.  
  24049.  
  24050.  
  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.  
  24093.  
  24094.  
  24095.  
  24096. New Releases
  24097.  
  24098.  
  24099. Updates
  24100.  
  24101.  
  24102.  
  24103.  
  24104. CUG155 B-Tree
  24105.  
  24106.  
  24107. Michimasa Honma (Japan) has updated CUG#155 Btree Library programs (developed
  24108. by Ray Swartz). The code conforms with ANSI C and is tested under MS-DOS using
  24109. Turbo C v2.0. For testing purposes, utilities that generate random numbers and
  24110. create a B-Tree are also added.
  24111.  
  24112.  
  24113. CUG328 WTWG
  24114.  
  24115.  
  24116. Although WTWG, window routines for text and graphics, was introduced as a
  24117. shareware package, David Blum (CA), the author of the package, has placed the
  24118. C source code in the public domain. The volume now includes the source code
  24119. for Turbo C v2.0 and Microsoft C v5.1.
  24120.  
  24121.  
  24122. New Releases
  24123.  
  24124.  
  24125.  
  24126.  
  24127. CUG335 Frankenstein Cross Assemblers
  24128.  
  24129.  
  24130. Mark Zenier (WA) has submitted a collection of cross-assemblers written in the
  24131. combination of Yacc and C. Inspired by Will Colley's cross- assemblers (CUG
  24132. #149, #219, #242, #267, #276), Zenier has created a series of cross-assemblers
  24133. for 8- and 16-bit microcomputers; RCA 1802-1805, Signetics/Phillips 2650,
  24134. Hitachi 6301-6303, 64180, Mos Technology/Rockwell 6502, Motorola 6805, 6809,
  24135. 68hc11-6801-6800, Texas Instruments tms7000, Intel 8041-8048, 8051, 8085,
  24136. 8096, Zilog Z8, Z80.
  24137. The assemblers provide hex listing, symbol table listing, debug, and processor
  24138. selection. The output source is similar to Colly's, with a few minor
  24139. differences: the end statement syntax and extensions (the support for
  24140. different character sets and the \ escapes in strings). The front end of each
  24141. assembler is implemented using Yacc/Bison, while the back end is written in C.
  24142. If you replace the front end, you will get a new cross-assembler. The package
  24143. includes Yacc and C source code, documentation, testing input and output
  24144. files, and makefile. The programs were developed and tested under UNIX/Xenix
  24145. and MS-DOS systems. Turbo C v1.5 was used for MS-DOS. Yacc or Bison (CUG #285)
  24146. is required to build an executable code.
  24147.  
  24148.  
  24149. CUG336 EGAPAL/EDIPAL
  24150.  
  24151.  
  24152. This volume includes EGA graphics applications and utilities contributed by
  24153. Scott Young (NH) and Marwan El-AUGI (FRANCE). Young has submitted a shareware
  24154. package, EGAPAL, which is a series of programs allowing users to create EGA
  24155. graphics images for the 640x350 and 16-color mode. EGAPAL includes a graphics
  24156. image editor program that lets you select a color from the palette, draw an
  24157. image on the screen, and save it. EGAPAL also includes a utility that converts
  24158. the graphics image into a header file to be included in your C programs, and a
  24159. library that loads a graphics image from disk of header files to the screen.
  24160. The package includes a documentation and sample program. Turbo C is required.
  24161. For registration, please contact the author (P.O. Box 1550 Section #8,
  24162. Portsmouth, NH 03802).
  24163. EI-AUGI has submitted a palette editor, EDIPAL, which allows the user to
  24164. change the EGA palette and save it. Saving the new palette is implemented by
  24165. not closing the graphics system, therefore the change is not permanent. (He is
  24166. working on this problem.)
  24167.  
  24168.  
  24169.  
  24170.  
  24171.  
  24172.  
  24173.  
  24174.  
  24175.  
  24176.  
  24177.  
  24178.  
  24179.  
  24180.  
  24181.  
  24182.  
  24183.  
  24184.  
  24185.  
  24186.  
  24187.  
  24188.  
  24189.  
  24190.  
  24191. New Products
  24192.  
  24193.  
  24194. Industry-Related News & Announcements
  24195.  
  24196.  
  24197.  
  24198.  
  24199. UNIX System Laboratories Offers New Products
  24200.  
  24201.  
  24202. UNIX System Laboratories is offerring the latest releases of its Open Look
  24203. graphical user interface (GUI), Xwin graphical windowing system and Alex
  24204. developer's tool software.
  24205. Open Look GUI for UNIX System V Release 4, and Xwin graphical windowing
  24206. system, USL's implementation of the X Window System ported to System V, are
  24207. available individually or together. Together, they are offered as Graphics
  24208. Services v4. Alex software, which is a language extension to X developer's
  24209. tool, is available separately.
  24210. Open Look GUI contains a customizable environment featuring icons, pushpins,
  24211. pull-down menus, point-and-click desktop and file manager, and other
  24212. utilities. Open Look GUI works with any implementation of the industry
  24213. standard Massachusetts Institute of Technology X Window System Intrinsics
  24214. Version X11R4.
  24215. This release of Open Look offers realistic three-dimenstional visuals that
  24216. provide enriched appearance and improved visual feedback to user input.It also
  24217. enables users who choose not to use a mouse to traverse the screen by rising
  24218. keyboard accelerators or navigational keys.Release 4 of Xwin includes support
  24219. for multiple servers, allowing users to run a server in each virtual terminal
  24220. or on multiple terminals connected to a single CPU.
  24221. Graphic Services v4, Open Look GUI, and Xwin graphical windowing system are
  24222. available now. Source licencing fees for Graphics Services v4 are $20,000 for
  24223. the initial CPU. Current customers can upgrade through April 1, 1991 at a 50
  24224. percent discount.
  24225. For more information, contact UNIX System Laboratories at (800) 828-UNIX.
  24226.  
  24227.  
  24228. MIPS Introduces International RISC/OS
  24229.  
  24230.  
  24231. MIPS Computer Systems has introduced an international version of its RISC/OS
  24232. UNIX-based operating system. In addition, MIPS introduced a Japanese country
  24233. kit, which provides users with a Japanese language interface for MIPS
  24234. workstations and servers.
  24235. RISC/OS 4.51 employs a layered design, making the system readily adaptable to
  24236. various languages and simplifies the development required to internationalize
  24237. applications. The lower layer provides functionality that is independent of
  24238. any one specific language or culture. The upper layers serve as country kits
  24239. that provide language and cultural-dependent interfaces.
  24240. MIPS implemented its first localized country kit for Japan, including messages
  24241. translated into Japanese for more than 130 UNIX commands. Country kits in
  24242. other languages will follow. The kit provides a Japanese version of the X
  24243. Window System terminal emulator, so Japanese characters can appear on display
  24244. screens in the X Window System environment. The kit also provides a Japanese
  24245. version of the EMACS text editor.
  24246. For more information, contact MIPS Computer Systems, 928 Arques Ave.,
  24247. Sunnyvale, CA 94086-3650, (408) 720-1700; FAX (408) 991-7777.
  24248.  
  24249.  
  24250. Multiprocessing Development Environment for 68000 Family
  24251.  
  24252.  
  24253. An integrated development environment for developers of multiprocessing
  24254. embedded systems based on the 68000 family of microprocessors is now available
  24255. from Intermetrics Microsystems Software an Precise Software Technologies. The
  24256. new environment incorporates the InterTools embedded systems development tools
  24257. with the Precise/MPX multiprocessing kernel.
  24258. Precise/MPX is a multiprocessing real-time kernel for industrial embedded
  24259. systems. It is an open kernel and can be used with an unlimited number of
  24260. different processors. MPX is based on a high-performance message passing
  24261. paradigm and supports multiple tasks on each processor.
  24262. The InterTools software development package features extensive C and
  24263. target-level optimizations and ANSI C language support. This package includes
  24264. programming utilities and XDB, and a multi-window source level debugger.
  24265. Prices for the integrated multiprocessing application development environment
  24266. start at $15,000 and will vary depending on host system and target platform
  24267. configuration. For more information, contact Intermetrics Microsystems
  24268. Software, 733 Concord Ave., Cambridge MA 02138-1002, (617) 661-0072; FAX (617)
  24269. 828-2843.
  24270.  
  24271.  
  24272. Qtool Offers Code Analysis System
  24273.  
  24274.  
  24275. CAS is an integrated, interactive tool system that will analyze, maintain, and
  24276. enhance ANSI C and C++ on DOS. CAS, which is available from Qtool, works with
  24277. any C compiler system. The system has four tools: a code browser, a text
  24278. browser, a command line query utility, and a translator.
  24279. The code browser helps programmers locate identifiers, defines, numbers, and
  24280. include in a project with query results placed in a scrollable list. The text
  24281. browser has built-in language capabilities and also allows hypertext-like
  24282. database searching. You can trace function calls or identifier references
  24283. across the project and also automatically trace include files. Both the text
  24284. browser and code browser permit easy invocation of external editors and other
  24285. tools. The command line query utility allows database queries from the command
  24286. line similar to interactive querries from within the code browser.
  24287. CAS is available for the PC compatible on DOS. The price is $100. For more
  24288. information, contact Qtool, 5814 SW Taylor, Portland, OR 97221, (503)
  24289. 297-3583.
  24290.  
  24291.  
  24292. Fast Floating Point Library
  24293.  
  24294.  
  24295. Triakis is now offering a library of C compatible functions called FFP (Fast
  24296. Floating Point), a floating-point arithmetic method based on the
  24297. sign-logarithm number system. The library operations, which can often rival a
  24298. math coprocessor in speed, can broaden the appeal of an application program
  24299. for users without special math hardware. The precision and exponent range of
  24300. the numbers equal and slightly exceed standard single precision.
  24301. Applications include general scientific computation, geometrical graphics,
  24302. signal processing, simulation, and non-linear systems. The library includes
  24303. transcendental functions, logarithm, exponential, and square root.
  24304. The library functions are compatible with Quick C and Turbo C. The product
  24305. sells for $59. A small royalty is charged when it is used in a commercial
  24306. product. For more information, contact Triakis, 1011 Duchess Rd., Bothell, WA
  24307. 98012, (206) 486-8282.
  24308.  
  24309.  
  24310. Application Development Tool For Motif
  24311.  
  24312.  
  24313. A Motif-based graphical user interface development tool is now available from
  24314. JYACC. The tool gives developers the flexibility of presenting their
  24315. applications forms or objects in either graphical or character-based modes
  24316. without recompiling. It also provides seamless integration to multiple
  24317. databases.
  24318.  
  24319. The tool provides support for basic Motif features, such as text, scrolling,
  24320. and push button widgets, full mouse and graphics support, and proportional
  24321. fonts.
  24322. For more information, contact JYACC, 116 John St., New York, NY 10038, (212)
  24323. 267-7722; FAX (212) 608-6753.
  24324.  
  24325.  
  24326. Symbolic Debugging For 6805 Chips
  24327.  
  24328.  
  24329. Byte Craft Limited has released v1.0 of its embedded systems development
  24330. productivity tool for debugging, integrating, and testing code. The E6805
  24331. Symbolic Host Support works as a software connector between the 6805 emulation
  24332. board and the C6805 Code Development System's C compiler, and it adds symbolic
  24333. debugging capabilities to the M68HC05 EVM and EVS boards.
  24334. The E6805 can step through source code at either instruction rate or source
  24335. line rate. Programs may be traced following the data flow or following the
  24336. program logic. Listing displays trace the activity of the program being
  24337. executed on the emulator or respond to user needs through browser commands.
  24338. The E6805 sells for $295 and requires 512K memory. For more information,
  24339. contact Byte Craft Limited, 421 King Street North, Waterloo, Ontario, Canada
  24340. N2J 4E4, (519) 888-6911; FAX (519) 746-6751.
  24341.  
  24342.  
  24343. Lynx Shipping Real-Time POSIX OS
  24344.  
  24345.  
  24346. Real-time UNIX applications developers can start developing portable
  24347. applications that comply with the emerging POSIX 1003.4 real-time standard
  24348. with the release of LynxOS 2.0 from Lynx Real-Time Systems.
  24349. LynxOS 2.0 also provides real-time Ada implementations based on lightweight
  24350. threads as described in POSIX 1003.48 (Draft 3). LynxOS 2.0 will sell for the
  24351. same price as previous versions, which vary according to hardware platforms
  24352. and optional features, such as X Windows, Motif, NFS, or ROMkit. A typical
  24353. development version for 80386 PC/AT compatibles, (including bootable LynxOS
  24354. kernel, shell, utilities, libraries, and object files to customize the LynxOS
  24355. kernel, C compiler and library for software development, device driver source
  24356. code, and user manuals costs $1,495.
  24357. LynxOS runs on the Intel 860i, 80386 SX and DX and 80486, Motorola 680X0 and
  24358. 88000, and MIPS R3000 and R6000 microprocessors. For more information, contact
  24359. Lynx Real-Time Systems, 16780 Lark Avenue, Los Gatos, CA 95030, (408)
  24360. 354-7770; FAX (408) 354-7085.
  24361.  
  24362.  
  24363. AGE Establishes Independent System Testing Lab
  24364.  
  24365.  
  24366. AGE has established an independent laboratory for X Window System testing and
  24367. evaluation.
  24368. The new service, called AGE Labs, provides the personnel and equipment
  24369. necessary to perform comprehensive X Window System product testing,
  24370. benchmarking, and analysis. This includes access to DECstation,
  24371. Hewlett-Packard 3XX and 8XX, IBM RS/6000, Sun 3, Sun SPARCstation,
  24372. 386/Interactive UNIX, 386/SCO UNIX, and Open DeskTop, and VAXstation
  24373. workstations; interfaces for Ethernet, IEEE 802.3, Token Ring, and serial
  24374. communication ports; Internet (TCP/IP), SL/IP, and DECnet networking
  24375. protocols, multi-user operating systems (UNIX and VMS); and X user
  24376. environments such as Motif, DECwindows, and OpenWindows.
  24377. Evaluation includes use of the Xlib Protocol Test Suite (T7), the MIT Volume
  24378. Stress Test, and the AGE Test Suite, Testing is also done using standard X
  24379. client programs and with popular commercially available X Window System
  24380. applications.
  24381. The multi-vendor, heterogeneous hardware and software environment ensures
  24382. thorough hardware and software testing. The AGE Labs service is available to
  24383. all vendors and is operated independently from the development teams at AGE.
  24384. AGE assures confidentiality of all products.
  24385. For more information, contact AGE, 8765 Aero Drive, Suite 226, San Diego, CA
  24386. 92123, (619) 565-7373; FAX (619) 565-7460.
  24387.  
  24388.  
  24389. Software Measurement Package For UNIX Updated
  24390.  
  24391.  
  24392. SET Laboratories has released v2.0 of UX-Metric line of software measurement
  24393. tools for Ada, C, and C++. UX-Metric computes measures of how difficult a
  24394. comptuer program will be for a programmer to understand, test, or modify. The
  24395. release adds support for additional metrics as well as a number of other
  24396. enhancements.
  24397. UX-Metric uses techniques known as software complexity metrics to measure the
  24398. characteristics of a program's source code, which are known to cause trouble
  24399. for programmers. In addition to support for the software science, cyclomatic
  24400. complexity and span of data reference measures, v2.0 also measures number of
  24401. blank lines, number of comments, and average variable name length.
  24402. UX-Metric v2.0 sells for $450. Registered users can upgrade for $50. The
  24403. company offers a 30-day money back guarantee. SunOS and System V/386 versions
  24404. of UNIX are currently supported. For more information, contact SET
  24405. Laboratories, P.O. Box 868, Mulino, OR 97042, (503) 829-7123.
  24406.  
  24407.  
  24408. Sage Updates Demo II
  24409.  
  24410.  
  24411. Sage Software is now shipping Dan Bricklin's Demo II v3.0. This version allows
  24412. users to overlay text on bitmapped images. It also adds mouse support, extends
  24413. keyboard support to AT-type enhanced keyboards, adds VGA video support,
  24414. provides an autosave feature, and adds memory swapping that lets Demo II
  24415. shrink to 7K on execution of external programs.
  24416. Demo II is a PC-based development tool for producing program prototypes,
  24417. demonstrations and tutorials. Demo II simulates the operation of any program
  24418. under any computing environment. It includes a run-time module with unlimited
  24419. distribution licensing so demonstrations produced can be freely sent to
  24420. prospective software users, providing demonstrations of a program's operation
  24421. without supplying any live code.
  24422. Demo II v3.0 is available in a single user version for $249. For more
  24423. information, contact Sage Software at (800) 547-4000; FAX (503) 645-4576.
  24424.  
  24425.  
  24426. Source Level Symbolic Debugger
  24427.  
  24428.  
  24429. Sierra Systems has introduced QuickFix, a high-end C language source-level
  24430. symbolic debugger for the 68000 family. The debugger's advanced
  24431. hardware/software design complements the company's previous product, the
  24432. Sierra C cross-development compiler for embedded applications.
  24433. The debugger offers a quick-connecting parallel ROM communication cable,
  24434. convenient multi-window display, and commands featuring a C-like syntax for
  24435. extremely intuitive operation.
  24436. QuickFix's high-speed performance can be attributed to the parallel ROM
  24437. communications interface. The debugger has six display windows: Source Window,
  24438. Register Window, Command Window, Data Display Window, Terminal Emulation
  24439. Window, and Help Window.
  24440. QuickFix command expressions follow C language syntax and semantics.
  24441. Expressions can incorporate any C operator, including casts and functions
  24442. calls. All local and global variables can be referenced by their symbolic
  24443. names, and the stack trace can be examined to determine the exact function
  24444. nesting. For repetitive operations, debugger commands can be grouped into
  24445. macros or placed into batch command files.
  24446. With QuickFix, programmers can observe, control, and modify embedded
  24447. applications as they execute on the target system. The debugger can set
  24448. breakpoints in either ROM or RAM, set watchpoints, display and modify register
  24449. and memory contents, and trace program execution. Conditional breakpoints and
  24450. code patching allow users to test bug fixes quickly and efficiently without
  24451. recompiling and reloading the target program.
  24452. The QuickFix debugger hosted on PC AT computers, including the target systems
  24453. monitor in source form and ROM communication cable, costs $1,500 when
  24454. purchased with the Sierra C Compiler. When purchased separately, QuickFix
  24455. costs $1,750. For more information, contact Sierra Systems, 6728 Evergreen
  24456. Ave., Oakland, CA 94611, (415) 339-8200; FAX (415) 339-3844.
  24457.  
  24458.  
  24459. C Programmer's Toolbox
  24460.  
  24461.  
  24462.  
  24463. The C Programmer's Toolbox v2.1 is now being shipped for Apple's Macintosh
  24464. Programmer's Workshop, PC/MS-DOS, and Sun's UNIX development environments. The
  24465. Toolbox, from MMC AD Systems, is a collection of 24 programming tools and aids
  24466. that enhance programmer productivity, while improving program performance and
  24467. quality. Major tools include CFlow, CLint, Cpp, CPrint, CXref, PMon and more.
  24468. This release adds support for Microsoft C 6.0 and Think C 4.000Ps. New options
  24469. include ANSI function prototype generation for any C source code, construction
  24470. of composite include files, new file/function interdependency reports, ne C
  24471. source code formatting options, and external specification of hidden include
  24472. files.
  24473. The Toolbox works with any C dialect, and works with any size program through
  24474. its virtual memory system and wildcard/indirect file processing facilities.
  24475. For more information, contact MMC AD Systems, Box 360845, Milpitas, CA 95036,
  24476. (415) 770-0858.
  24477.  
  24478.  
  24479. Kozo Signs SilverScreen Development Pact
  24480.  
  24481.  
  24482. Kozo Heikaku Engineering and Schroff Development have signed a software
  24483. development contract under which Schroff will implement Pixar's RenderMan and
  24484. the Phar Lap's DOS extender on SilverScreen.
  24485. SilverScreen is a 3-D CAD/Solid Modeling software system. It features 2-D and
  24486. 3-D Boolean operations, rendering, shading, mass properties, and associative
  24487. dimensioning. SilverScreen runs on DOS and UNIX (Silicon Graphics), For more
  24488. information, contact Schroff Development, P.O. Box 1334, Mission, KS 66222,
  24489. (913) 262-2664; FAX (913) 722-4936.
  24490.  
  24491.  
  24492. Cadre Unveils Teamwork
  24493.  
  24494.  
  24495. Cadre Technologies has released Teamwork 4.0 to supply computer-aided software
  24496. engineering (CASE) tools for C, Ada, and C++ applications development. These
  24497. CASE products address each phase of the software development process from
  24498. initial specifications to implementation and are complemented by advanced
  24499. testing capabilities throughout the entire life cycle.
  24500. New additions include dynamic modeling and real-time simulation, reverse
  24501. engineering C code, an Ada design-sensitive editor, expanded object-oriented
  24502. analysis support, heterogeneous network support, a document production
  24503. interface and a new release of PathMap.
  24504. Cadre Technologies has also introduced SaberLink, an operational interface
  24505. between Saber Software's Saber-C program development environment and Cadre's
  24506. Teamwork family. SaberLink allows users to invoke Saber-C commands from
  24507. Teamwork's structured design view.
  24508. For more information on either of these products, contact Cadre Technologies,
  24509. Providence Division, 222 Richmond St., Providence, RI 02903, (401) 351-5950;
  24510. FAX (401) 351-7380.
  24511.  
  24512.  
  24513. AccSys Expands Support
  24514.  
  24515.  
  24516. AccSys for Paradox has expanded its capabilities to include C and C++ support
  24517. for the Borland Turbo and Zortech compilers. Programmers can use v2.5 of
  24518. AccSys for Paradox to develop an application that runs under Microsoft's
  24519. Windows 3.0 and OS/2 using the Microsoft C compiler.
  24520. AccSys, from Copia International, is a seamless interface between C and
  24521. Paradox table files, and primary and secondary indexes and sorts database
  24522. files. Even though Paradox offers a versatile high-level language interface,
  24523. it does not provide the flexibility or the efficiency of C or C++. AccSys and
  24524. Paradox can be accessed either from your own application or from within
  24525. Paradox.
  24526. A complete set of working examples is included with AccSys for Paradox v2.50.
  24527. Source code can be purchased at an additional cost. There are no runtime
  24528. royalties, and the product is backed by a full 60-day return policy. Prices
  24529. for a single user copy is $395, with source code, $795. A multi-user copy is
  24530. $750, and $1,500 with source code.
  24531. For more information, contact Copia International, 1964 Richton Drive,
  24532. Wheaton, IL 60187, (708) 682-8898; FAX (708) 665-9841.
  24533.  
  24534.  
  24535. C-terp Employs DOS Extender Technology
  24536.  
  24537.  
  24538. Gimpel Software has release C-terp and C-terp 386 v.3.5. These C interpreters
  24539. offer integrated debugging and editing and employ Phar Lap DOS extender
  24540. technology to access all available extended memory.
  24541. C-terp features full K&R support with ANSI extensions, a full-screen,
  24542. built-in, reconfigurable editor, incredibly fast semi-compilation, complete
  24543. multiple module support, automatic make file, global search, preprocess
  24544. facility, system shell, trace, batch mode, and full-screen source-level
  24545. interactive debug facilities.
  24546. Under MS-DOS, C-terp is available for the Microsoft 5.x and 6.0 compiler for
  24547. $298. C-terp 386 is compiler-specific. In the Phar Lap environment, it is
  24548. available for Meta Ware's High C 386 and Watcom C 386. It sells for $398 for
  24549. the first copy and $300 for each additional copy. C-terp 386U, for UNIX System
  24550. V/386, is also available.
  24551. For more information, contact Gimpel Software, 3207 Hogarth Lane,
  24552. Collegeville, PA 19426, (215) 584-4261.
  24553.  
  24554.  
  24555.  
  24556.  
  24557.  
  24558.  
  24559.  
  24560.  
  24561.  
  24562.  
  24563.  
  24564.  
  24565.  
  24566.  
  24567.  
  24568.  
  24569.  
  24570.  
  24571.  
  24572.  
  24573.  
  24574.  
  24575.  
  24576.  
  24577.  
  24578. We Have Mail
  24579. Dear Bill,
  24580. I just read the December issue of the CUJ and thought it was quite good. It
  24581. had more useful information than most of the CUJ editions I've read.
  24582. Listing 1 in Rex's article is something I've always been amused with. I even
  24583. thought of a few more that are even less obvious:
  24584. a[i] == i[a]
  24585. == *(a+i)
  24586. == (a+i) [0]
  24587. == 0(a+i)
  24588.  
  24589. a[i] [j] == 0[a+i] [j]
  24590. == j [0[a+i]]
  24591. == 0[0[a+i] + j]
  24592. It seems pretty weird to me.
  24593. Second, there was a letter from Firdaus Irani asking about:
  24594. #include <stdio.h>
  24595. #include <stdlib.h>
  24596. #include <string.h>
  24597.  
  24598. int comp(unsigned char**, unsigned
  24599. char **);
  24600. unsigned char *list[] = { ... };
  24601.  
  24602. main(){
  24603. int x;
  24604.  
  24605. qsort(list, 5, sizeof(unsigned
  24606. char *), comp);
  24607. ...
  24608. }
  24609. I tried this with our compiler:
  24610. Cray Standard C, Release 2.0.1.2,
  24611. P/YMP Version
  24612. C Front-end Version 059 CMCS
  24613. Back-end Version 386
  24614. Compiler Generation Date = Nov 1 1990
  24615. Compilation Date and Time = Thu Nov \
  24616. 29, 1990
  24617. 09:34:04
  24618.  
  24619. *** *** SC151 [warning ] File = \
  24620. yy.c, line = 11:
  24621. The function call argument #4 is not \
  24622. assignment compatible.
  24623.  
  24624. TOTAL WARNINGS DETECTED IN yy.c: 1
  24625.  
  24626. Compilation Execution Time = 0.560350
  24627. seconds
  24628. which behaves the same as the Borland compiler (except it's a warning).
  24629. My reading of the standard is that this is correct. The function comp must be
  24630. assignment compatible with the fourth argument to qsort:
  24631. extern void qsort(void *_Base,
  24632. size_t _Nmemb, size_t _Size,
  24633. int (*_Compar)(const void *,
  24634. const void *));
  24635. which is the last line. If this were allowed to work as it stands then
  24636. int (*pf)(void *, void *) = comp;
  24637. must also work. I don't see that the rules for assignment compatible allow you
  24638. to look arbitrarily deep into the type that is pointed at and allow all
  24639. pointers to void to be compatible with any old pointer. I think an easier way
  24640. to state this is:
  24641.  
  24642. char **q;
  24643. void **p;
  24644.  
  24645. p = q; /* not assignment compatible */
  24646. When trying to determine assignment compatibility of pointers, you look at the
  24647. pointer and what it points at. Everything else must be compatible (as opposed
  24648. to assignment compatible).
  24649. This was a compromise we arrived at when trying to figure out what to do with
  24650. things like
  24651. int **p;
  24652. const int **q;
  24653. We decided that an implementation only needs to look one "type part" back for
  24654. assignment compatibility. After that they have to be strictly compatible.
  24655. That's my recollection anyway. Talk to you later.
  24656. Tom MacDonald
  24657. Cray Research
  24658. Thanks, Tom. (To our other readers -- Tom MacDonald is one of the more active
  24659. contributors to the standardization and evolution of the C language. He made
  24660. major contributions to the floating-point specifications in <float.h> and
  24661. <math.h>, among other thing. He also participates in the on-going work of the
  24662. Numerical C Extensions Group.)
  24663. The definition of the array subscript operator leads to all sorts of craziness
  24664. if you depart from the most conventional usage. None of the forms you and Rex
  24665. Jaeschke explore are necessarily "wrong," but they can sure mislead.
  24666. Your analysis of type compatibility among pointers to pointers is on the
  24667. money. I believe Tom Plum anguished quite a bit about the problems the C
  24668. standard causes with bsearch and qsort users. I don't think the rules we ended
  24669. up with are either incorrect or onerous, but they do make us old-line,
  24670. seat-of-the-pants C programmers think a bit harder. -- pjp
  24671. Dear Sirs
  24672. I was left in a most confused state after having read the review of Menuet by
  24673. Mr. Volkman in "Quick Takes" in the CUJ, Vol. 8 No. 12, pp. 101. Correct me if
  24674. I err but, in contrast to the conclusion reached by Mr. Volkman, is it not
  24675. desirable that a GUI not provide intertask comunication and other attributes
  24676. associated with an operating system?
  24677. Perturbed,
  24678. J. Ue Parrot
  24679. Chicago, IL
  24680. The distinction between a GUI and an operating system can be pretty academic.
  24681. If you want to support multiple window, cutting and pasting between windows,
  24682. and background processing, your display manager had better at least be on
  24683. intimate terms with the machinery that shuffles processes.
  24684. The functions of GUI and scheduler are distinct. Each is complex and difficult
  24685. to implement. Each has serious performance requirements. All that argues for
  24686. keeping the two functions distinct, as you point out. In real life, however,
  24687. designers have yet to do so without serious performance penalties. -- pjp
  24688. Dear C Users Journal,
  24689. I have never written to a magazine before and I usually avoid all arguments
  24690. defending one computer language against another. But after reading Rodney M.
  24691. Bates' letter in the January issue I feel impelled to express myself -- enough
  24692. is enough. Comparing the latest incarnation of C (or any other language) with
  24693. ISO Pascal is like comparing a 1990 Buick with a Model-T. Have any of you
  24694. rabid C (or Modula-2) types looked at a Turbo Pascal compiler lately?
  24695. Let's begin with "no Pascal that I know of has separate interface and
  24696. implementation modules." and the "popular UCSD Pascal...force(s) massive
  24697. recompilation." Turbo Pascal has supported private and public modules for
  24698. years now. In the masses of code that I am currently wading through I am
  24699. working with upwards of 50 different files in at least 3 different
  24700. directories. Using a command line switch tells the compiler whether I want to
  24701. "make" some of them or "build" all of them (sound familiar?). And it's fast --
  24702. I mean fast the way no C compiler is. And that conclusion is based upon my own
  24703. experiences using C on the same machine. (Yes, Virgina, I do program in C
  24704. also.)
  24705. The statement about "popular UCSD" only serves to emphasize that Mr. Bates has
  24706. not looked at a Pascal compiler for at least 5 years. (And in this business,
  24707. that reads 50 years.) UCSD Pascal's popularity has declined probably in direct
  24708. proportion to the popularity of the old Apple II, and in the microcomputer
  24709. world, Turbo Pascal is the de facto standard.
  24710. TP 6 supports units, separate compilation, pointers to functions, bit
  24711. manipulation, MS-DOS register types and interrupts, oh -- and of course
  24712. objects. I could go on and on. And it's fast -- fast -- fast with a smart
  24713. linker that ignores what you don't need to use. No inflated code here, thank
  24714. you.
  24715. In the same vein, years ago when I was taking a course in C, the teacher
  24716. proclaimed that "you can't do bit manipulation with Pascal." TP has supported
  24717. the operators shl (or <<), shr (>>), and xor (^) since day one. Okay, I can't
  24718. define a structure composed of bit variables in Pascal, but then, neither can
  24719. I manipulate sets in C.
  24720. The real point -- every language has its own strengths and weaknesses. "Real
  24721. programers" should be at least bilingual so that when the ideal development
  24722. environment becomes available they can take full advantage of it. What
  24723. environment you ask? -- the one where you can mix at least four languages with
  24724. ease. Like some assembler for communication work, C for pointers to huge
  24725. arrays, perhaps Prolog for a database and then Pascal for maintainability --
  24726. and sheer elegance.
  24727. Sincerely,
  24728. Barbara Argilo
  24729. Several dialects of Pascal make up for one or more of the deficiencies in the
  24730. language definition put forth by Wirth. Several implementations have excellent
  24731. development support. Some produce excellent code. The fundamental problem with
  24732. Pascal has always been a lack of standardization for many features essential
  24733. to serious development, such as separate compilation and manipulation of files
  24734. by name. Your code can thrive in one environment and not survive porting to
  24735. another.
  24736. You don't have to be a language bigot to observe that Pascal has diminished in
  24737. popularity over the same period that C has thrived. The bigotry appears only
  24738. when you contrive explanations for these trends. If you argue from emotional
  24739. bias instead of looking at verifiable facts, you indulge in a form of bigotry.
  24740. Programmers should certainly be comfortable in more than one language, if only
  24741. to appreciate the strengths and weaknesses of their favorite one(s). -- pjp
  24742. Dear C Users Journal,
  24743. There are a few minor fixes in Louis Baker's article (CUJ Jan. 1991) First,
  24744. the correct address is Zortech Inc., 4-C Gill Street, Woburn, MA 01801. The
  24745. correct phone numbers are (617) 937-0696, fax (617) 937-0793.
  24746. Concerning your comments on Zortech and Turbo C++ on page 125, if you run out
  24747. of memory, you are not necessarily out of luck. With version 2.1 and beyond,
  24748. you can use the Rational DOS Extender technology (-br compiler flag) which
  24749. puts the compiler into extended memory, freeing up that space for your
  24750. program.
  24751. On page 126, the Zortech v2.1 and above linker no longer mangles the names in
  24752. its error messages. It is a native code linker. Also, Zortech supports the
  24753. cdecl as well as the extern C keywords. The extern C keyword is ANSI standard.
  24754. Marc Brockman
  24755. Zortech, Inc.
  24756. 4-C Gill St.
  24757. Woburn, MA 01801
  24758. Thanks for the clarification. -- pjp
  24759. Dear Mr. Ward:
  24760. Since the first C Users Journal arrived at my home, I have eagerly awaited
  24761. every following issue. I used to subscribe to a plethora of magazines, but
  24762. since have reduced them to this one. Good job and keep it up!
  24763. Besides my work in MS-DOS and VAX/VMS, I have also worked on the THEOS-286V
  24764. operating system. I have yet to see any reference to it in this magazine. If
  24765. there are any CUG members who also use this operating system, I would love to
  24766. hear from them.
  24767. I also caught a reference to the BBS operated by R&D Publications. If it is
  24768. something useful to CUG members, how do I obtain access?
  24769. Thank you for your attention.
  24770. Aaron N. Cutshall
  24771. LZA Computer Resources
  24772. 4233 Casa Verde Dr.
  24773. Ft. Wayne, IN 46816
  24774. THEOS-286V is a new one to me. Anybody have any information on it? As for the
  24775. BBS, R&D hasn't put up any special customer services on it the last I heard.
  24776. Should that come to pass, you will read about it here first. -- pjp
  24777. Dear Mr. Ward:
  24778. In your January 1991 issue, Tom Plum provided some insight on C compiler
  24779. validation services being offered by a commercial company, the British
  24780. Standards Institute. I would like to add to the information he provided by
  24781. shedding some light on the certification process being used by our own
  24782. government.
  24783. Our government's National Institute of Standards and Technology (NIST),
  24784. formerly the National Bureau of Standards, provides the compiler certification
  24785. service for all US government agencies, such as the Departments of Defense,
  24786. Commerce, Energy, etc. This certification is required for any vendor who
  24787. wishes to sell to any US government agency, and is the only certification
  24788. required by law in this country. The government uses a standard called a
  24789. Federal Information Processing Standard (FIPS), which is usually the same as
  24790. the corresponding ANSI or IEEE standard from which the FIPS was adopted. In
  24791. the case of C, NIST is adopting a FIPS-C which is the same as the ANSI C
  24792. Standard, with some additional requirements.
  24793. NIST has been performing language certifications for years on other FIPS
  24794. languages such as Pascal, FORTRAN, and COBOL, and will likely begin C
  24795. certifications sometime this year. There are a number of points regarding C
  24796. certification that your readers should be aware of:
  24797. 1. Only NIST can perform such certification.
  24798. 2. The NIST will use only the ANSI C validation suite (ACVS) supplied by
  24799. Perennial as the certifying test system.
  24800.  
  24801. 3. Those wishing to obtain certification must be ACVS licensees.
  24802. The value of a NIST certified compiler should be apparent to anyone. I agree
  24803. with Tom Plum that compiler vendors should take steps to achieve
  24804. certification. However, since these certifications are not free, vendors
  24805. should make informed decisions regarding the relative value of certifications
  24806. and how to obtain them. It's not clear what value a BSI certification has, if
  24807. any, particularly here in the United States.
  24808. Sincerely,
  24809. Barry E. Hedquist
  24810. President
  24811. Perennial
  24812. 4699 Old Ironsides Dr., Suite 210
  24813. Santa Clara, CA 95054
  24814. The folks at BSI tell me that the U.S. government has certain obligations
  24815. under the treaty they endorsed as a member of the ISO. One of these is to
  24816. provide reciprocity with validation services offered by other member nations.
  24817. You can imagine what an economic impediment small companies would face if each
  24818. member nation could demand separate validation for products requiring
  24819. standards compliance.
  24820. I have even heard some Europeans argue that the shoe is on the other foot.
  24821. Because NIST insists on producing a FIPS standard that is not exactly the ANSI
  24822. standard, FIPS certification may not be acceptable to customers requiring ISO
  24823. C conformance. But the U.S. government is obliged to honor an ISO
  24824. certification from any member-authorized body.
  24825. As for me, I dunno. I am certainly no expert on the legalities involved. I
  24826. happily disclose that Tom Plum is more than a good friend. (Our wives are twin
  24827. sisters.) I observe that significant profits are at stake for the small
  24828. companies providing validation services around the world. Clout with major
  24829. vendors and purchasers of C compilers is an important issue. I also must
  24830. report more than a little unhappiness with the high-handed way NIST behaves in
  24831. the standards arenas that I care about. To me, they often appear coercive,
  24832. unresponsive to the community they supposedly serve, and more than a little
  24833. self-serving in their financial dealings. I emphasize, however, that these are
  24834. personal opinions.
  24835. Thank you for writing. -- pjp
  24836. Dear C Users Journal,
  24837. I have recently purchased Turbo C++. What percent of your articles are
  24838. applicable to Turbo C++? Perhaps an index of past issues may be helpful.
  24839. Thanks.
  24840. Gil Hoellerich
  24841. 2617 Country Way
  24842. Fayetteville, AR 7203
  24843. I use Turbo C++ myself for most of my development work. Mostly, I use it for
  24844. the ANSI C compiler hidden inside, not to mention the comfortable development
  24845. environment on my PCs. I can tell you that essentially all the C code that has
  24846. gone into recent issues of CUJ will work fine under Turbo C++. Code that
  24847. cannot is usually identified as a specific dialect, or making use of
  24848. nonstandard library functions. Quite a bit of the code we publish is actually
  24849. developed under Turbo C or Turbo C++.
  24850. The C++ stuff is just a bit more problematic. That language suffers more from
  24851. dialect variations than its older brother. Extensions to the Standard C
  24852. library, beyond the basic C++ I/O, vary the most. Nevertheless, our authors
  24853. tend not to push into the dark corners of C++ except when they are telling
  24854. cautionary tales.
  24855. An index sounds like a great idea. The next time Robert finds someone sitting
  24856. around the office looking bored, he can discuss the matter further. -- pjp
  24857. In fact, we have several initiatives under way. We should have some news "real
  24858. soon now." -- rlw
  24859. Dear Editor,
  24860. This example of implementing a state machine is a welcome article. However
  24861. when I tried the listings example, the following errors were found:
  24862. 1. Listing 1: MAX_FUNCS should be added as a #define with a value of 5. It is
  24863. referenced in listing 3.
  24864. 2. Listing 2: The Next_State values of S_PLAY under the states S_FAST_F,
  24865. S_REWlND, and S_RECORD are wrong. The next states remain the same as present
  24866. states.
  24867. 3. Listing 3: In the function driver, under the "find the state" comment, a
  24868. reference to s_table[i] is missing the underbar.
  24869. 4. The state diagram (and table) does not allow for the case where the tape is
  24870. left in the VCR and power turned off. When power is turned on, the tape must
  24871. be inserted before one can get to the READY state.
  24872. Suggestions for improvement:
  24873. A. Listing 1: The valuef 5 in defining the state table structure (*flist[S])
  24874. should now be changed to MAX_FUNCS.
  24875. B. Define a MIN_CHAN (of 2) for the VCR to cycle from 2 thru 13.
  24876. C. A follow-up article on "Alternative Organizations" mentioned by the author
  24877. is in order.
  24878. Enjoy the Journal -- keep up the good work!
  24879. Don L. Jackson
  24880. P.O. Box 681
  24881. Gilbert, AZ 85234
  24882. The author, Paul Fischer, replies:
  24883. Mr. Jackson,
  24884. Thanks for the comments. Sorry for any inconvenience errors 1-3 may have
  24885. caused. Regarding number 4, there are two solutions.
  24886. As I stated in the last paragraph of the section entitled "Example," a
  24887. function should be added that causes an automatic ejection of the tape in any
  24888. transition to the S_OFF state. I consider this function to be "internal device
  24889. operation" and did not list it. I do agree my decision not to list it causes
  24890. confusion on the operation of the VCR in that case.
  24891. Alternatively, some designers prefer to move to a "transition" state and
  24892. execute a function that checks the outstanding condition (in this case if
  24893. there is a tape present), and then generate an internal event to move to the
  24894. correct state and execute the appropriate functions. For this presentation I
  24895. was attempting to keep the diagram and state table as simple as possible. For
  24896. that reason I did not apply this alternative method.
  24897. Thanks again for your comments.
  24898. Paul Fischer
  24899. P.O. Box 6 81
  24900. Gilbert, AZ 85234
  24901. Dear Mr Plauger:
  24902. I read with interest your editorial in the C Users Journal of December 1990,
  24903. particularly with respect to your forthcoming sabbatical in Australia!
  24904. Many of my associates, not least myself, would be extremely interested in
  24905. meeting you during your stay here. Are you going to visit Melbourne, Victoria,
  24906. at all? While my company is particularly interested in Windows 3.0 and OS/2
  24907. A.P.I's, we run a bulletin board which has a large and devoted group of C
  24908. programmers. We have regular (monthly) meetings and would love to have you
  24909. along one evening if this were possible.
  24910. I look forward to meeting you in 1991. Please don't hesitate to call should
  24911. there be anyway in which myself or my associates can be of assistance to you
  24912. during your stay.
  24913. Yours faithfully,
  24914. Stephen Moignard
  24915. Windows Solutions
  24916. 1 Jamboree Ave.
  24917. Frankston South
  24918. Victoria, Australia 3199
  24919. Thanks for the invite. My family and I do plan to travel about Australia, and
  24920. I have already received a staggering number of offers to visit and speak. You
  24921. can reach me via the Computer Science Department at UNSW. -- pjp
  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.  
  24971.  
  24972.  
  24973.  
  24974.  
  24975.  
  24976.  
  24977.  
  24978.  
  24979.  
  24980.  
  24981.  
  24982.  
  24983.  
  24984.  
  24985.  
  24986.  
  24987.  
  24988.  
  24989.  
  24990. QNX Windows
  24991.  
  24992.  
  24993. Lan Barnes
  24994.  
  24995.  
  24996. Lan Barnes is a microcomputer systems programmer and database consultant
  24997. working in MS-DOS and QNX. He has written a full-screen QNX editor that he is
  24998. converting to QNX Windows in his spare time. Readers may contact him at 822
  24999. Guilford Ave., Suite 147, Baltimore, MD 21202.
  25000.  
  25001.  
  25002. In recent years the QNX operating system has become a favorite of systems
  25003. designers. QNX is a networked OS (or NOS) for microcomputers capable of
  25004. combining real-time performance with a user interface that can support
  25005. "friendly" applications. The system has been around for awhile -- Quantum
  25006. Software Systems, QNX's vendor, has been shipping one form or another for ten
  25007. years. Nevertheless, QNX is less dated than many younger operating systems.
  25008. Its underlying modular, message-passing architecture still stands as a fresh
  25009. and powerful alternative to conventional OS and NOS designs.
  25010. The friendliness of QNX's user interface, however, can be challenged. The
  25011. traditional QNX shell interface can best be described as "UNIX without the
  25012. frills." While programmers and network administrators tend to appreciate QNX's
  25013. spare elegance, new QNX users -- even the more technical users typical of a
  25014. real-time environment -- have always been subjected to a steep learning curve.
  25015. In today's micro culture of graphical user interfaces, QNX developers felt an
  25016. obvious need for a GUI for QNX.
  25017. Such a GUI was announced and released at the QNX Developers Conference in
  25018. Ottawa last fall. I provide here a high-level overview of that GUI, called QNX
  25019. Windows.
  25020.  
  25021.  
  25022. Open Look Standard
  25023.  
  25024.  
  25025. QNX Windows is based on the Open Look standard supported by, among others,
  25026. AT&T and Sun Microsystems. Open Look is a pleasing standard, depending heavily
  25027. on familiar visual references to provide intuitive ease of use. For example,
  25028. you scroll by using a slide bar. You make both exclusive and nonexclusive
  25029. choices using buttons that invert in shadowing when you toggle them, so they
  25030. appear depressed.
  25031. Small, standardized icons provide features in an intuitive manner. Menus can
  25032. be made "sticky," for example, so that they will remain in view after a
  25033. selection, by clicking on an icon of a pushpin in the menu's corner.
  25034. Similarly, a selection that leads to a submenu has a wedge-shaped arrowhead
  25035. beside it, while a terminal selection does not.
  25036. Open Look's ease of operation does not come at the expense of control
  25037. subtlety. Consider the example of the ubiquitous slide bar. This icon, which
  25038. looks and acts like a stereo control, can interpret a single mouse click to
  25039. scroll:
  25040. to the beginning
  25041. to the end
  25042. up a page
  25043. down a page
  25044. up a percent of the total
  25045. down a percent of the total
  25046. You can also use the mouse to drag the scroll to a specific place.
  25047.  
  25048.  
  25049. Necessary Resources
  25050.  
  25051.  
  25052. I confess that I found Open Look as expressed in QNX Windows to be easy and
  25053. intuitive to pick up. For me, this a profound concession, since I have been
  25054. described as a "mouse-hostile anti- windows curmudgeon" by my GUI-enthusiast
  25055. friends. But the proof of a GUI is in the programming. More specifically, a
  25056. GUI's real value is a function of the performance it provides compared with
  25057. the resources it consumes, including that most valuable resource, programmer's
  25058. time.
  25059. In hardware resources, QNX Windows requires a minimum of an 80286 machine with
  25060. 2 MB of memory and either EGA or VGA. Unlike other GUIs, this minimum
  25061. requirement is realistic, and gives satisfactory performance. I tested the
  25062. package on my home machine, a 286 that cruises along at a sedate 9.5 mHz with
  25063. one wait state and hard drive seek times in the mid-50s (counting in msec.).
  25064. This combination performed quick screen painting and smooth scrolling, even
  25065. with a background process running.
  25066. Quantum claims that QNX Windows will outperform such GUIs as Open Desktop on
  25067. machines with half the clock speed and a quarter of the memory. I can't refute
  25068. that. I know, by comparison, that Microsoft Windows 286 limps along on my
  25069. machine to the point of being less than useful.
  25070.  
  25071.  
  25072. Programming Interface
  25073.  
  25074.  
  25075. For a C programmer, any new system must be judged in terms of its function
  25076. library and the programmer's model that lies behind it. The Open Look
  25077. programmer's model begins with the definitions of page, pane, and window, the
  25078. logical elements from which a QNX Windows application is built.
  25079. To build an application in QNX Windows, the programmer starts by defining a
  25080. "page." At the lowest level, a page represents a buffer in memory into which
  25081. the program builds objects that will display on the page. Each page may have
  25082. from one to four "panes," which may be thought of as subwindows. Panes serve
  25083. as natural subgroupings for elements on the window's picture. Tying these
  25084. together is the visible "window," which may be thought of as a view into the
  25085. page beneath it.
  25086. QNX Windows is strongly object-oriented. Every element that may be put on a
  25087. page represents a highly smart object. For example, objects such as buttons or
  25088. menu selections remember their color, shape, and location. They also remember
  25089. whether they highlight or change colors when selected.
  25090. In fact, a QNX Windows object knows everything about its screen display. QNX
  25091. Windows handles that display without needing direction from the programmer's
  25092. application. Thus, if you click on a button, it will automatically perform
  25093. whatever its visual task is -- become depressed, change color, or pick up a
  25094. check mark, for example. The button will also send a message to the
  25095. application that it has been selected.
  25096. The underlying protocol that makes all this possible is client/server. The QNX
  25097. Windows process runs on the display node as a windows server. (It must have a
  25098. graphics terminal.). Client processes queue requests for windows services.
  25099. This design husbands resources at the same time that it enhances performance.
  25100. Only one instance of the windows code is necessary per display node, and the
  25101. display is always painted by the local processor, even if the requesting
  25102. process is running on a distant node.
  25103. Performance can be further enhanced if applications programmers follow a "draw
  25104. first, then show" discipline. Changes to the window should be written into the
  25105. picture buffer with draw function calls until the new window is complete. Then
  25106. the changes for the completed window are sent to the screen. This gives a
  25107. pleasing, instantaneous effect on the video display.
  25108.  
  25109.  
  25110. The Function Library
  25111.  
  25112.  
  25113. QNX Windows' function library, the other half of its API, comprises
  25114. approximately 200 function calls divided into 20 functional groups. These
  25115. range from low-level functions that draw arcs, points, Bezier curves, and the
  25116. like, to high-level calls that draw complete menus, meters, or slidebars in a
  25117. single function call.
  25118. The authors of the library avoided chaos by following a rational naming
  25119. convention very much along modern C lines. Each function name begins with a
  25120. functional-group word, such as Draw, Picture, Set, or Event. Following that is
  25121. the name of the target object or action, as in DrawButton, PictureOpen,
  25122. SetColor, or EventWaiting. As with all good naming conventions, the programmer
  25123. is able to predict the name and existence of a function very soon after
  25124. starting to use QNX Windows. I have put aside my grumbling about the "creeping
  25125. Pascalization" of C, and have oiled my rusty shift key.
  25126.  
  25127.  
  25128. Object Orientation
  25129.  
  25130.  
  25131.  
  25132. Using the QNX Windows library is facilitated by the design paradigm that all
  25133. screen constructs, such as buttons or menus, are high level objects that store
  25134. a complete set of characteristics for that object. Each windows process has a
  25135. context -- a set of color, fill, and line defaults that will apply to each
  25136. draw operation as it occurs. These characteristics are copied through to the
  25137. drawn object, thus fixing its characteristics even if the context is changed
  25138. later.
  25139. Each object, be it a menu or dialog box, has an optional "tag," a string
  25140. identifier of up to 255 characters that may be used as a handle in subsequent
  25141. manipulations of that object. Although objects can also be located by their
  25142. type or their coordinates on the picture, the use of tags goes a long way
  25143. toward preserving the sanity of the QNX Windows programmer.
  25144.  
  25145.  
  25146. High-Level Objects
  25147.  
  25148.  
  25149. Because the Open Look standard defines a continuum of user interfaces, the
  25150. designers of QNX Windows had the opportunity to develop many complex objects,
  25151. such as menus and dialogs, into single function calls. That opportunity was
  25152. not neglected.
  25153. Menus are a special case of dialogs. Menus follow the Open Look conventions of
  25154. having a wedge shaped submenu mark and being selectable through the right
  25155. mouse button. You can display a menu by calling the function Menu, which needs
  25156. only five arguments:
  25157. a label
  25158. a title
  25159. the number of display columns
  25160. a list of menu items separated by semicolons
  25161. a string of option characters
  25162. You don't need to write any mouse-handling code for the menu, or indeed for
  25163. any part of a QNX Windows application. The server handles the mouse
  25164. consistently at all times. It returns nothing to the application but
  25165. selections made by mouse click.
  25166. A program frequently needs file-selection menus. A second function call,
  25167. FileMenu, creates a menu with two special arguments -- a directory and a
  25168. wild-card skeleton. The menu can present files of a specific type from a
  25169. specific directory. Both arguments are string pointers, so the application can
  25170. put the directory and skeleton under user control.
  25171. Another standardized QNX Windows dialog is a "notice." A notice displays a
  25172. message such as "Printer has jammed," with a clear button labeled "OK" (as if
  25173. a jammed printer is ever OK). A help text system allows contextual help to be
  25174. stored in simple text files and called up by an interactive browser. These
  25175. help text files are all stored in a common directory. An application could
  25176. even allow a user to modify the program's help text.
  25177. QNX is so closely associated with the world of mission-critical and real-time
  25178. systems. It is heartening to see that the QNX Windows library provides two
  25179. functions called DrawNumber and DrawReal. These present numerical data to
  25180. screen objects in a variety of formats, including dials, meters, bars, and
  25181. gauges with or without sliders, end buttons, and ticks. It is a tribute to the
  25182. server approach that these formidable coding chores can be reduced to function
  25183. calls each with five parameters. They can also be present for all processes
  25184. with a single instance of code in the server.
  25185.  
  25186.  
  25187. Programming Paradigm
  25188.  
  25189.  
  25190. A QNX Windows application does surprisingly little setup code to get the
  25191. system rolling. Listing 1 shows the code necessary to produce a "Hello world"
  25192. message box. This illustrates the underlying "draw, then show" philosophy in
  25193. QNX Windows. The first three lines set up the picture in memory. The fourth
  25194. line uses the picture tag test to show the picture in a window. Line five
  25195. replaces the standard QNX call to receive. As a message passing OS, QNX is
  25196. geared to using asynchronous, event-driven algorithms. No QNX code fragment
  25197. would be complete without some line that waits on a message (or sends one).
  25198. The code in Listing 1 cannot discriminate among messages. That is acceptable
  25199. perhaps if we simply want to clear the "Hello world" after any mouse click.
  25200. Usually, however, a piece of QNX Windows code will have an event loop similar
  25201. to the one in Listing 2.
  25202. Listing 3 is the complete code for a puzzle game shipped with QNX Windows. The
  25203. puzzle is familiar to most of us as a small flat box of numbered tiles that
  25204. can be slid past each other. The box has 15 tiles in a 4 X 4 enclosure,
  25205. leaving one space for maneuvering the tiles. I don't like these puzzles,
  25206. probably because I'm not any good at solving them, but I do note that the
  25207. source for this windows application has only 300 lines of C code.
  25208.  
  25209.  
  25210. Summary
  25211.  
  25212.  
  25213. The QNX Windows GUI combines the features of both a high-level and low-level C
  25214. windowing library with a client/server window driver design. The resulting GUI
  25215. provides a powerful development environmen. It does not sacrifice the
  25216. performance that has made QNX a favorite OS among those involved in
  25217. mission-critical or real-time software.
  25218. The QNX Windows developers toolkit can, I suspect, speed application
  25219. development for current QNX users. Perhaps its handsome presentation,
  25220. following the Open Look standard, will lure new QNX users into the fold.
  25221. Figure 1
  25222.  
  25223. Listing 1
  25224. EVENT_MSG msg; /* allocate space for a message */
  25225.  
  25226. Picture("test", NULL); /* create a picture to write to */
  25227. DrawAt(200, 100); /* set the coordinates */
  25228. DrawText("Hello, world", 0, 0, 0, NULL, NULL); /* draw it */
  25229. Window("test", "T", "C", NULL); /* show it */
  25230. GetEvent(0, &msg, sizeof(EVENT_MSG)); /* wait for an event */
  25231.  
  25232.  
  25233. Listing 2
  25234. EVENT_MSG event;
  25235. /* allocate space for an
  25236. event message */
  25237. draw_screen();
  25238. /* put up your window */
  25239. for(;;)
  25240. { /* receive the message
  25241. from QNX Windows */
  25242. GetEvent(0, &event,
  25243. sizeof(EVENT_MSG));
  25244. /* extract the message type
  25245. and act appropriately */
  25246.  
  25247. switch(Event(&event))
  25248. {
  25249. case CLICK:
  25250. /* somebody clicked
  25251. the mouse */
  25252. do_something();
  25253. continue;
  25254. case QUIT:
  25255. /* the user wants to quit */
  25256. case TERMINATED:
  25257. /* the server is being
  25258. shut down */
  25259. shutdown();
  25260. exit(0);
  25261. }
  25262. }
  25263.  
  25264.  
  25265. Listing 3
  25266. /*
  25267. * puzzle.c: This QNX Windows program creates a
  25268. * moving tile puzzle, with 15 numbered tiles in a
  25269. * 4 X 4 enclosure.
  25270. *
  25271. * It is reproduced here by permission of Quantum
  25272. * Software Systems, Ltd of Kanata, Canada.
  25273. */
  25274.  
  25275. #include <stdio.h>
  25276. #include <windows.h>
  25277.  
  25278. #define RECT_HEIGHT 250
  25279. #define RECT_WIDTH 360
  25280. #define PANE_BORDER 140
  25281.  
  25282. #define PANE_HEIGHT ((rows)*RECT_HEIGHT + 120)
  25283. #define PANE_WIDTH ((rows)*RECT_WIDTH + 120)
  25284. #define CASE(str) (((str)[0] << 8) Â½ (str)[1])
  25285.  
  25286. EVENT_MSG msg;
  25287.  
  25288. int rows = 4, max_num;
  25289. int b[20][20], old[20][20];
  25290. int mv;
  25291.  
  25292. main()
  25293. {
  25294. int go = YES, wid, pid;
  25295.  
  25296. /* connect to QWindows */
  25297. if ( !GraphicsOpen (NULL))
  25298. exit ( -1 );
  25299.  
  25300. SetName("Puzzle", NULL); /* Optional */
  25301.  
  25302. pid = PictureOpen ( "pict", NULL, NULL, LIGHT_GRAY,
  25303. NULL, NULL, NULL );
  25304. PictureHighlightOptions( NULL, 'N', 0, 0 );
  25305.  
  25306.  
  25307. init();
  25308. display();
  25309.  
  25310. wid = WindowOpen ("Puzzle", PANE_HEIGHT, PANE_WIDTH,
  25311. "MT480-r;s", NULL, NULL, pid);
  25312. /*
  25313. * Put control button in top frame.
  25314. */
  25315.  
  25316. WindowBarCurrent( TOP, NULL );
  25317. SetButton( NULL, WHITE, NULL );
  25318. DrawAt( 300, 120 );
  25319. AttachDialog("Options", NULL,
  25320. "Scramble;Size½@(Easy½03;The Famous 4 x 4½04;Hard½
  25321. 05;Very Hard½06;You've got to be
  25322. Kidding½ 10)^R",
  25323. NULL, "MD", NULL, NULL);
  25324. DrawButton( "Options", NULL, "Ns;D", "op");
  25325. Draw();
  25326. WindowBarCurrent( NULL, NULL );
  25327.  
  25328. load_icon( "/windows/apps/puzzle" );
  25329. DrawStart(NULL, 0);
  25330.  
  25331. while ( go )
  25332. {
  25333. GetEvent ( 0, &msg, sizeof(msg));
  25334.  
  25335. switch ( Event ( &msg ) )
  25336. {
  25337. case QUIT:
  25338. WindowClose( 0, NULL );
  25339. go = NO;
  25340. break;
  25341.  
  25342. case TERMINATED:
  25343. go = NO;
  25344. break;
  25345.  
  25346. case CLICK:
  25347. if (msg.hdr.code == 'S') {
  25348. /* game piece selected */
  25349. move(atoi(msg.hdr.key) - 1);
  25350. mv++;
  25351. win ();
  25352. }
  25353.  
  25354. break;
  25355.  
  25356. case DIALOG:
  25357. switch(CASE(msg.hdr.key)) {
  25358. case '03': case '04': case '05': case '06':
  25359. case '07': case '10':
  25360. rows = atoi(msg.hdr.key);
  25361. case 'Sc':
  25362. new_game();
  25363. break;
  25364. }
  25365. }
  25366.  
  25367. }
  25368. GraphicsClose();
  25369. exit (0);
  25370. }
  25371.  
  25372. init() {
  25373. int i, j, r, loop;
  25374.  
  25375. max_num = rows*rows;
  25376.  
  25377. for (i = 0; i < rows; i++)
  25378. for (j = 0; j < rows; j++) {
  25379. b[i][j] = ( i * rows + j);
  25380. old[i][j] = -1;
  25381. }
  25382.  
  25383. i = j = rows - 1;
  25384. srand(get_ticks());
  25385. r = rand() % 200 + 500;
  25386.  
  25387. for (loop = 0; loop < 5000; ++loop) {
  25388. switch (rand() % 4) {
  25389. case 0:
  25390. if (i != 0) {
  25391. b[i][j] = b[i - 1][j];
  25392. b[i - 1][j] = max_num;
  25393. --i;
  25394. }
  25395. break;
  25396.  
  25397. case 1:
  25398. if (i != rows - 1) {
  25399. b[i][j] = b[i + 1][j];
  25400. b[i + 1][j] = max_num;
  25401. i++;
  25402. }
  25403. break;
  25404.  
  25405. case 2:
  25406. if (j != 0) {
  25407. b[i][j] = b[i][j - 1];
  25408. b[i][j - 1] = max_num;
  25409. --j;
  25410. }
  25411. break;
  25412.  
  25413. case 3:
  25414. if (j != rows - 1) {
  25415. b[i] [j] = b[i] [j + 1];
  25416. b[i] [j + 1] = max_num;
  25417. j++;
  25418. }
  25419. break;
  25420. }
  25421. }
  25422. }
  25423.  
  25424. display() {
  25425. int i, j;
  25426.  
  25427.  
  25428. for (i = 0; i < rows; i++)
  25429. if (memcmp(b[i], old[i], rows)) {
  25430. for (j = 0; j < rows; j++)
  25431. if (b[i][j] != max_num)
  25432. draw_number(i, j, b[i][j] + 1);
  25433. memcpy(old[i], b[i], rows);
  25434. }
  25435. }
  25436.  
  25437. draw_number( row, col, number)
  25438. int row, col, number;
  25439. {
  25440. char tbuf[4];
  25441.  
  25442. tsprintf(tbuf, "%2d", number);
  25443.  
  25444. SetColor ( "T", BLACK );
  25445.  
  25446. DrawAt (PANE_BORDER + (row*RECT_HEIGHT) + CHARH/2,
  25447. PANE_BORDER + (col*RECT_WIDTH ));
  25448.  
  25449. DrawText( tbuf, 0, 0, 0, "Sem", tbuf);
  25450.  
  25451. Draw ();
  25452. }
  25453.  
  25454. move_number( row, col, number)
  25455. int row, col, number;
  25456. {
  25457. char buffer[7];
  25458.  
  25459. tsprintf(buffer, "%2d", number);
  25460. ShiftTo( buffer, PANE_BORDER + (row*RECT_HEIGHT) + CHARH/2,
  25461. PANE_BORDER + (col*RECT_WIDTH ));
  25462. }
  25463.  
  25464.  
  25465. move(m)
  25466. int m;
  25467. {
  25468. int i1, j1, i, j, i2, j2, c;
  25469.  
  25470. i1 = j1 = -1;
  25471.  
  25472. for (i = 0; i1 == -1 && i < rows; i++)
  25473. for (j = 0; j < rows; j++)
  25474. if (b[i][j] == m) {
  25475. i1 = i;
  25476. j1 = j;
  25477. break;
  25478. }
  25479.  
  25480. if (i1 != -1) {
  25481. i2 = j2 = -1;
  25482. for (i = 0; i < rows; i++)
  25483.  
  25484. if (b[i][j1] == max_num) {
  25485. i2 = i;
  25486.  
  25487. j2 = j1;
  25488. break;
  25489. }
  25490.  
  25491. if (i2 == -1)
  25492. for (j = 0; j < rows; j++)
  25493. if (b[i1][j] == max_num) {
  25494. i2 = i1;
  25495. j2 = j;
  25496. break;
  25497. }
  25498.  
  25499. if (i2 == -1)
  25500. return;
  25501. }
  25502. else
  25503. return;
  25504.  
  25505. /* Hold picture for smoother updates */
  25506. PictureHold();
  25507.  
  25508. if (i1 == i2)
  25509. if (j1 < j2)
  25510. for (j = j2 - 1; j >= j1; --j) {
  25511. b[i1][j + 1] = b[i1][j];
  25512. move_number(i1, j+1, b[i1][j]+1);
  25513. }
  25514. else
  25515. for (j = j2; j < j1; j++) {
  25516. b[i1][j = b[i1][j + 1];
  25517. move_number(i1, j, b[i1][j+1]+1);
  25518. }
  25519.  
  25520. else
  25521. if (i1 < i2)
  25522. for (i = i2 - 1; i >= i1; --i) {
  25523. b[i + 1][j1] = b[i][j1];
  25524. move_number(i+1, j1, b[i][j1]+1);
  25525. }
  25526. else
  25527. for (i = i2; i < i1; i++) {
  25528. b[i][j1] = b[i + 1][j1];
  25529. move_number(i, j1, b[i+1][j1]+1);
  25530. }
  25531. b[i1][j1] = max_num;
  25532.  
  25533. /* update current picture */
  25534. PictureContinue();
  25535. }
  25536.  
  25537. win() {
  25538. int c = 0, i, j;
  25539. char buf[50];
  25540.  
  25541. for (i = 0; i < rows; i++)
  25542. for (j = 0; j < rows; j++)
  25543. if (b[i][j] < c)
  25544. return( 0 );
  25545. else
  25546.  
  25547. c = (int) b[i][j];
  25548.  
  25549. tsprintf(buf, "You got it in %d moves!", mv);
  25550. Notice( NULL, "You Win", NULL, "W", buf);
  25551.  
  25552. return( 1 );
  25553. }
  25554.  
  25555. new_game() {
  25556. RECT_AREA area;
  25557.  
  25558. WindowInfo(NULL,&area,NULL,NULL,NULL,NULL);
  25559.  
  25560. /* erase all elements in the picture */
  25561. WindowHold();
  25562. Erase( ALL );
  25563.  
  25564. area.height = PANE_HEIGHT;
  25565. area.width = PANE_WIDTH;
  25566.  
  25567. init();
  25568. display();
  25569. mv = 0;
  25570.  
  25571. WindowChange( &area, NULL, KEEP, NULL, "!" );
  25572. WindowContinue();
  25573. }
  25574.  
  25575.  
  25576.  
  25577.  
  25578.  
  25579.  
  25580.  
  25581.  
  25582.  
  25583.  
  25584.  
  25585.  
  25586.  
  25587.  
  25588.  
  25589.  
  25590.  
  25591.  
  25592.  
  25593.  
  25594.  
  25595.  
  25596.  
  25597.  
  25598.  
  25599.  
  25600.  
  25601.  
  25602.  
  25603.  
  25604.  
  25605.  
  25606.  
  25607.  
  25608.  
  25609.  
  25610. Of Mice And Menus
  25611.  
  25612.  
  25613. Keith Bugg
  25614.  
  25615.  
  25616. Keith Bugg is currently a senior software engineer with Analysas Corp. His ten
  25617. years experience as a programmer/analyst includes five years in C, all on the
  25618. DEC VAX or the PC. Readers may contact him at 122 E. Morningside Drive, Oak
  25619. Ridge, TN 37830, (615) 482-9515.
  25620.  
  25621.  
  25622. Including a mouse in your application enhances its ease of use and
  25623. friendliness. Novice users can find the large keyboard intimidating. Using a
  25624. mouse to navigate a program can reduce this apprehension. Also, using a mouse
  25625. lowers the learning curve -- the simplicity of "point and click" reinforces
  25626. the user's intuition.
  25627. The programmer benefits from the improved data integrity a mouse supplies. In
  25628. a classic menu-driven system, the program must read the user's keystroke(s),
  25629. validate results, and report on them. In a mouse-driven system, the user can
  25630. only select a valid option. If the user clicks the mouse on a vacant area of
  25631. the screen, the program simply ignores the clicks.
  25632. I describe here the design and implementation of a C program that allows a
  25633. mouse to select options in a menu-driven application. The sample program
  25634. demonstrates many of the technical issues, and can serve as a template in your
  25635. collection of tools. I developed the code for an IBM PC running MS-DOS. The
  25636. program requires a Microsoft mouse (or compatible), its associated driver, and
  25637. the Microsoft mouse library, MOUSE.LIB. I used the Microsoft C compiler, but
  25638. the code should work with other compilers with a minimum of changes. Whatever
  25639. compiler you choose, you must buy MOUSE.LIB. It is available from Microsoft as
  25640. part of the Microsoft Mouse Programmer's Guide, and costs about $25.
  25641. A program using the Microsoft mouse can be compiled in any of the memory
  25642. models, and then linked with MOUSE.LIB, as in this example: 
  25643. cl/AM /c myprogram.c
  25644. (creates a medium model but does not link)
  25645. link myprogram.obj,MOUSE.LIB
  25646. (creates executeable of 'myprogram')
  25647. The Microsoft compiler switch /AM determines the memory model, in this case
  25648. medium. Use /AS for small, /AC for compact, and /AL for large and huge.
  25649. The mouse library contains all the routines needed to manipulate the mouse.
  25650. Utilities include reading the cursor, writing the cursor, and determining the
  25651. status of mouse buttons. The Microsoft mouse performs 23 functions.
  25652. The call to mouse uses four parameters which are declared as integers and
  25653. passed by reference, as in mouse (&m1, &m2, &m3, &m4). (Each argument of type
  25654. pointer to int.) The first parameter indicates the command to be performed.
  25655. The others provide additional information, such as cursor coordinates or
  25656. button selection. Table 1 summarizes the calls most widely used.
  25657. Mouse coordinates differ from screen coordinates by a factor of eight when in
  25658. text mode, because each character is an 8-by-8 pixel group. The upper
  25659. left-hand corner of the screen is the origin. It has coordinates (0, 0). The
  25660. lower right-hand corner of the screen has coordinates (632, 192), assuming the
  25661. usual 25 lines of 80 characters each. 
  25662. BIOS calls solve most of the technical design issues that affect including a
  25663. mouse in a program. A mouse-driven program should begin by checking the target
  25664. platform for the existence of a mouse and its driver. Your program can then
  25665. adapt automatically to machines without a mouse. Incidentally, you might want
  25666. menu options to be selected via the keyboard whether your target platform has
  25667. a mouse or not. The sample program does not include this mode of operation,
  25668. but you can easily include it. Programming for the arrow keys, the function
  25669. keys, and other special keys (such as Home and End) is fairly straightforward.
  25670.  
  25671. The next issue concerns manipulating the cursors. You must control both the
  25672. default text cursor and the mouse cursor. Use BIOS calls to control the text
  25673. cursor. You can also use BIOS calls to access the video display. The sample
  25674. program displays the example menu using BIOS calls. Use an array of structures
  25675. to define the menu. Each element holds the text of the menu option plus the
  25676. screen and mouse coordinates. With this approach, you can make changes easily.
  25677. When using a mouse in your applications, you should disable the control-break
  25678. interrupt, and restore it upon termination. If you don't, and the user types a
  25679. control-break, the program could return control to DOS with the text cursor
  25680. off and the mouse cursor still on. This situation could confuse and frustrate
  25681. the user. Remember that an important reason to using a mouse is to create a
  25682. level of comfort and to reduce complexity. You access the control break
  25683. interrupt by calling signal. See the Microsoft C documentation for a
  25684. description of this function. 
  25685. Although it is beyond the scope of this article, bear in mind that a
  25686. well-designed application would save and restore the target platform's
  25687. configuration. This includes the graphics mode and screen color, for example.
  25688. The focus of this article is more on the fundamental requirements. At that
  25689. level, little difference exists between a mouse-driven program and a
  25690. traditional program. Both give the user information, wait for a response, and
  25691. take action. 
  25692. The sample program executes an infinite loop waiting for an event to occur. It
  25693. ignores meaningless events such as clicking the mouse button on a vacant area
  25694. of the menu. It processes valid ones such as backlighting a menu option when
  25695. the mouse cursor is moved into it. Knowing the mouse's characteristics is
  25696. essential for organizing the logic flow of an application.
  25697. Interfacing a mouse to an application brings a versatile and elegant dimension
  25698. to the user-interface. All well-engineered software emphasizes what to do over
  25699. how to do it. Mouse-driven programs are no exception. You can use the sample
  25700. program as a template for other programs. Here are some suggestions: a pop-up
  25701. calculator, a control panel in a process-control environment, a cut-and-paste
  25702. buffer for a text editor, or a help screen with hypertext capabilities.
  25703. References
  25704. Microsoft Mouse Programmer's Reference Guide by Microsoft Press.
  25705. C Power Users Guide by Herb Schildt 
  25706. Table 1
  25707. m1 Description
  25708. ------------------------------------------------------------------------------
  25709. 0 Resets mouse, number of buttons returned in m2; status returned in m1
  25710.  (0 indicates mouse not installed, -1 if OK)
  25711.  
  25712. 1 Enable mouse cursor
  25713.  
  25714. 2 Disable mouse cursor
  25715.  
  25716. 3 Returns button status and cursor coordinates (m3 = horizontal,
  25717.  m4 = vertical
  25718.  
  25719. 4 Set mouse cursor (m3= horizontal, m4 = vertical) screen address
  25720.  
  25721. 5 Returns button press information (set m2 = 0 for left button, 1 for right.
  25722.  Number of presses are returned in m2; m3 and m4 have right cursor
  25723.  position)
  25724.  
  25725. 6 Same as (5), but gets information when button is RELEASED
  25726.  
  25727. 7 Limits range of cursor's horizontal movement
  25728.  
  25729. 8 Limits range of cursor's vertical movement
  25730.  
  25731. Listing 1 (mouse.c)
  25732. /*
  25733.  
  25734. NAME = MOUSE.C
  25735. This program demonstrates the fundamentals of using a mouse to
  25736. select options in a menu-driven application. Uses two "dummy"
  25737. functions: genledg() and payroll().
  25738. Note: Function chkdrv() taken from page 7-16 of "Microsoft
  25739. Mouse Programmer's Reference Guide"
  25740. */
  25741.  
  25742. #include <stdio.h>
  25743. #include <dos.h> /* required for BIOS calls, etc. */
  25744. #include <string.h>
  25745. #include <signal.h> /* disable & enable control break */
  25746.  
  25747. #define RED_BACK 74 /* display using a red background */
  25748.  
  25749. extern void pascal far mouse(int*, int*, int*, int*); /*declare mouse*/
  25750.  
  25751. void txtcursor(char cur), cls(), outstr(), genledg(), payroll(), heading();
  25752. void chkdrv(); /* checks for existence of mouse driver */
  25753.  
  25754. int i, handler(); /* handler() part of control-break */
  25755. int m1, m2, m3, m4; /* mouse parameters */
  25756. struct menu /* structure for our sample menu */
  25757. {
  25758.  
  25759. char text[20]; /* text of options */
  25760. int x; /* horiz. screen coordinate */
  25761. int y; /* vert. screen coordinate */
  25762. int mh,mh2,mv; /* mouse horiz/vert. coordinates*/
  25763. }options[3]; /* 3 options in our sample prog.*/
  25764.  
  25765. main()
  25766. {
  25767.  
  25768. int choice, loop;
  25769.  
  25770. /* disable Control-Break interrupt */
  25771.  
  25772. if(signal(SIGINT,handler) == (int(*) ())-1)
  25773. {
  25774. cls();
  25775. printf("Fatal error - could not disable Control-Break\n");
  25776. abort();
  25777. }
  25778.  
  25779. /* check for mouse hardware & driver */
  25780. m1=O;
  25781. mouse(&m1,&m2,&m3,&m4); /* check hardware here */
  25782. if(m1 != -1)
  25783. {
  25784.  
  25785. cls();
  25786. printf("Fatal error - no mouse found..\n");
  25787. abort();
  25788. }
  25789. chkdrv(); /* check for mouse driver */
  25790.  
  25791. /* "Populate" our structure with data */
  25792.  
  25793.  
  25794. strcpy(options[0].text, "General Ledger" );
  25795. options[0].x = 6;
  25796. options[0].y = 30;
  25797. options[0].mh = 240; /* mh & mh2 define the horiz. */
  25798. options[0].mh2= 344; /* limits of option's "hot spot"*/
  25799. options[0].mv = 48; /* define vertical limit here */
  25800.  
  25801. strcpy(options[1].text, "Payroll" );
  25802. options[1].x = 8;
  25803. options[1].y = 30;
  25804. options[1].mh = 240;
  25805. options[1].mh2 = 288;
  25806. options[1].mv = 64;
  25807.  
  25808. strcpy(options[2].text,"Exit");
  25809. options[2].x = 10;
  25810. options[2].y = 30;
  25811. options[2].mh = 240;
  25812. options[2].mh2= 264;
  25813. options[2].mv = 80;
  25814.  
  25815. heading(); /* display menu, etc. */
  25816. m1=m2=m3=m4=i=0; /* initialize variables */
  25817. mouse(&m1,&m2,&m3,&m4); /* init mouse */
  25818. txtcursor(0); /* turn off text cursor */
  25819. m1=1; /* show cursor */
  25820. mouse(&m1,&m2,&m3,&m4);
  25821.  
  25822. /* now set up infinite loop which processes the
  25823. user's requests */
  25824. loop=1;
  25825. while(loop)
  25826. {
  25827.  
  25828. m1=3; /* read mouse cursor position */
  25829. m2=m3=m4=0;
  25830. mouse(&m1,&m2,&m3,&m4);
  25831.  
  25832. for(i=0; i < 3; ++i) /* is cursor in 1 of 3 options? */
  25833. {
  25834. if((m3 >=options[i].mh && m3 <= options[i].mh2) &&
  25835. (m4 == options[i].mv))
  25836. {
  25837. outstr(options[i].x,options[i].y, options[i].text,RED_BACK);
  25838. break;
  25839. }
  25840. else
  25841. outstr(options[i].x,options[i].y, options[i].text,10);
  25842. }
  25843. m1=6; /* read left button */
  25844. m2=m3=m4=0;
  25845. mouse(&m1,&m2,&m3,&m4);
  25846. if(m2) /* user pressed left button */
  25847. {
  25848. choice= -1; /* reset from last choice made */
  25849. for(i=0; i < 3; i++)
  25850. {
  25851. if((m3 >= options[i].mh && m3 <= options[i].mh2)
  25852. && (m4 == options[i].mv))
  25853.  
  25854. choice = i;
  25855. }
  25856. switch(choice)
  25857. {
  25858. case 0: /* selected GEN. LEDGER */
  25859. genledg();
  25860. break;
  25861. case 1: /* selected PAYROLL */
  25862. payroll();
  25863. break;
  25864. case 2: /* selected EXIT */
  25865. loop=0; /* ends infinite loop */
  25866. break;
  25867. default: /* meaningless "click" */
  25868. break;
  25869. }
  25870. m1=m2=m3=m4=i=0;
  25871. mouse(&m1,&m2,&n3,&m4); /* init mouse */
  25872. m1=1; /* show cursor */
  25873. mouse (&m1,&m2,&m3,&m4);
  25874. }
  25875. }
  25876. /* perform housekeeping chores before exiting */
  25877.  
  25878. m1=2; /* hide mouse cursor */
  25879. mouse(&m1,&m2,&m3,&m4);
  25880. txtcursor(1); /* restore text cursor */
  25881. cls(); /* clear screen */
  25882. signal(SIGINT, SIG_IGN); /* enable control-break */
  25883. exit(0); /* return to DOS */
  25884. } /* END MAIN PROGRAM */
  25885.  
  25886. void txtcursor(char cur) /* controls text cursor */
  25887. {
  25888. union REGS r; /* prepare registers,etc */
  25889. r.h.ah = 1;
  25890. if(cur) /* turn text cursor on */
  25891.  
  25892. {
  25893. r.h.cl = 7;
  25894. r.h.ch = 6;
  25895. }
  25896. else
  25897. {
  25898. r.h.ch = 113; /* set bits 5,6 */
  25899. r.h.cl = 0; /* set lower bits */
  25900. }
  25901. int86(0x10, &r, &r); /* execute the interrupt */
  25902.  
  25903. }
  25904.  
  25905. void cls() /* clears the screen */
  25906. {
  25907. union REGS r;
  25908. r.h.ah = 6; /* screen scroll code */
  25909. r.h.al = 0; /* clear screen code */
  25910. r.h.ch = 0; /* start row */
  25911. r.h.cl = 0; /* start column */
  25912. r.h.dh = 24; /* end row */
  25913.  
  25914. r.h.dl = 79; /* end column */
  25915. r.h.bh = 7;
  25916. int86(0x10, &r, &r); /* execute interrupt */
  25917. }
  25918. void outstr(x,y,s, color) /* writes a string to video RAM */
  25919. char *s; /* string to write */
  25920. char color; /* color of string */
  25921. int x,y; /* where to start writing*/
  25922. {
  25923. char far *vid_mem; /* video memory pointer */
  25924. vid_mem= (char far *) 0xB8000000; /* start of video memory */
  25925. vid_mem=vid_mem+((x * 160) + (y * 2)); /* where to start output */
  25926. while(*s) /* loop thru string... */
  25927. {
  25928.  
  25929. *vid_mem=*s++; /* move string into video memory*/
  25930. ++vid_mem; /* incr. to next byte=attribute */
  25931. *vid_mem=color; /* and set the color there */
  25932. ++vid_mem; /* incr. for next character in */
  25933. } /* the string "s" .... */
  25934. }
  25935.  
  25936. void chkdrv() /* checks for presence of mouse driver */
  25937. {
  25938. union REGS inregs, outregs;
  25939. struct SREGS segregs;
  25940. long address;
  25941. unsigned char first_byte;
  25942.  
  25943. inregs.x.ax = 0x3533;
  25944. intdosx(&inregs, &outregs, &segregs);
  25945.  
  25946. address=(((long) segregs.es) << 16)+(long) outregs.x.bx;
  25947. first_byte= * (long far *) address;
  25948.  
  25949. if((address==0) Â½Â½ (first_byte==0xcf))
  25950. {
  25951. cls();
  25952. printf("System has a mouse, but no mouse driver !\n");
  25953. printf("Install file MOUSE.SYS and re-start this program.");
  25954. exit(0);
  25955. }
  25956. }
  25957. void genledg() /* "dummy" function */
  25958. {
  25959. m1=2; /* hide cursor */
  25960. mouse(&m1,&m2,&m3,&m4);
  25961. cls();
  25962. printf("Perform Option 1, 'General Ledger'\nNow press 'ENTER'...");
  25963. getchar();
  25964. heading();
  25965. }
  25966.  
  25967. void payroll() /* "dummy" function */
  25968.  
  25969. {
  25970. m1=2; /* hide cursor */
  25971. mouse(&m1,&m2,&m3,&m4);
  25972. cls();
  25973.  
  25974. printf("Perform Option 2, 'Payroll'\nNow press 'ENTER'...");
  25975. getchar();
  25976. heading();
  25977. }
  25978. void heading() /* menu heading */
  25979. {
  25980. cls(); /* clear screen */
  25981. outstr(3,25,"Sample Mouse/Menu Program",14);
  25982. for(i=0; i < 3; i++)
  25983. outstr(options[i].x,options[i].y,options[i].text,10);
  25984. }
  25985.  
  25986. int handler() /* control-break function */
  25987. {
  25988. signal(SIGINT, SIG_IGN);
  25989. }
  25990.  
  25991.  
  25992.  
  25993.  
  25994.  
  25995.  
  25996.  
  25997.  
  25998.  
  25999.  
  26000.  
  26001.  
  26002.  
  26003.  
  26004.  
  26005.  
  26006.  
  26007.  
  26008.  
  26009.  
  26010.  
  26011.  
  26012.  
  26013.  
  26014.  
  26015.  
  26016.  
  26017.  
  26018.  
  26019.  
  26020.  
  26021.  
  26022.  
  26023.  
  26024.  
  26025.  
  26026.  
  26027.  
  26028.  
  26029.  
  26030.  
  26031.  
  26032.  
  26033.  
  26034.  
  26035.  
  26036.  
  26037. Point-And-Shoot Menus
  26038.  
  26039.  
  26040. John Matsche
  26041.  
  26042.  
  26043. John J. Matsche has a B.S and an M.B.A. from the University of Central
  26044. Florida. His experience includes ten years of systems analysis on various
  26045. platforms. His language experience includes C++, Pascual, Assembler, Algol,
  26046. Cobol, and Basic. You can contact John at 4662 Millhaven Rd., Martinez, GA
  26047. 30907.
  26048.  
  26049.  
  26050. In the beginning, programming a menu screen was easy. You presented a list of
  26051. program options to the user. The user would select an option by entering the
  26052. corresponding number and pressing the enter key. It was a simple programming
  26053. task, usually requiring nothing more than a looping series of write statements
  26054. followed by a read, then some sort of a branching statement.
  26055. When I bought my first copy of Lotus 1-2-3, I realized the inadequacies of
  26056. this algorithm. The point-and-shoot menus that run along the top of my
  26057. spreadsheet do more than provide a raw list of program options. These menus
  26058. also describe each option. Once selected, it is easy to maneuver down the
  26059. hierarchy, and even easier to back out. In short, the point-and-shoot menu
  26060. system is more intuitive. More important, at that stage in my career, the
  26061. point and shoot system looked more polished and professional.
  26062. The Lotus menu system has many variations. Basically, it consists of a list of
  26063. words or short narrative that describes an option. The selected option, is
  26064. highlighted in some fashion, usually by display in reverse video. When the
  26065. user presses the right arrow key, the next option becomes the selected option.
  26066. The left arrow key selects the previous option. The user points to an option
  26067. by pressing the arrow keys, then invokes the option by pressing the enter key
  26068. -- hence the name "point and shoot." If the user presses the escape key, the
  26069. menu backs out. No option is selected and control returns to some previous
  26070. state.
  26071. I set out to develop an algorithm that would implement point-and-shoot menus
  26072. as efficiently as possible. At the same time, I wanted it to be generic enough
  26073. to work with any application program. C is an ideal programming language for
  26074. this type of project. It is low-level enough to provide the speed necessary
  26075. for quick screen displays. It is compact enough to have negligible impact on
  26076. an application programs's size. For this project, I used Borland's Turbo C++
  26077. compiler running on an IBM-compatible 386 using MS-DOS v4.01.
  26078.  
  26079.  
  26080. Implementation
  26081.  
  26082.  
  26083. I make use of three video modes that I call normal, reverse, and highlight.
  26084. These terms are left over from the days of monochrome displays, but are also
  26085. applicable to color systems. Simply choose your own favorite color scheme for
  26086. each mode. In this implementation, reverse video implies that both background
  26087. and foreground colors are reversed from normal. Highlight video uses the
  26088. intense foreground color of the normal video mode.
  26089. The program shown in Listing 1 contains the complete implementation. I added a
  26090. main function to demonstrate the execution of the menu system. By removing
  26091. main, you make the program a compilable module that can easily be linked into
  26092. any application program. To complete the conversion from program to module,
  26093. you should create a separate header file containing a prototype of the menu
  26094. function. The remaining functions should stay relatively hidden from the
  26095. application.
  26096. The #defines at the top of the listing provide logical names for the various
  26097. keys that menu recognizes. Some of the keys can be read directly by the getch
  26098. function. For example, the enter key will always generate an ASCII 13 (hex
  26099. 0x0d). Other keys, such as the function keys and the arrows, do not generate a
  26100. single character code when pressed. To recognize these keys, you must look at
  26101. something called a scan code.
  26102. From a software point of view, think of a scan code as an extended return
  26103. value for special keys. Whenever a special key such as a function or arrow key
  26104. is pressed, getch returns an ASCII NUL (hex 0x00). The next call to getch
  26105. returns the scan code for the pressed key. Note that this does not mean you
  26106. must press the special key twice. It simply means that whenever you press a
  26107. special key two characters are loaded into the keyboard buffer instead of just
  26108. one. In fact, if you were to use getch as a pause mechanism, and you didn't
  26109. test for scan codes, you might inadvertently pick up the scan code later on in
  26110. another getch call.
  26111. As previously mentioned, main is a demonstration function that is not a part
  26112. of the menu logic. It illustrates how easy the menu is to call. By looking at
  26113. main, you see that menu requires only two parameters. The first points to the
  26114. first element of an array of pointers to the menu items to be displayed. Note
  26115. that in this implementation, each menu item begins with a different letter.
  26116. This restriction allows you to select a menu item by pressing this letter on
  26117. the keyboard. The array is terminated with a null pointer. You don't need to
  26118. tell menu how many items are being passed to it. The second parameter to menu
  26119. is a Boolean value (1 means true, 0 means false) that specifies whether or not
  26120. a horizontal format is to be used.
  26121. The rest of the setup sets the desired colors and formats the screen. The
  26122. primary loop in main displays which item the user chose and positions the
  26123. cursor for the next menu display. The loop terminates when the user presses Q
  26124. for quit.
  26125. Five functions carry out the menu's actual implementation: show, findletter,
  26126. init, keypress, and menu. show displays one menu item on the screen.
  26127. findletter searches the list of items for the letter pressed by the user. init
  26128. sets up screen locations for displaying items, keypress handles all keyboard
  26129. input, and menu controls the show.
  26130. menu initializes screen colors and locations, displays all of the items, gets
  26131. user input, and loops until the user wants to exit. To initialize screen
  26132. locations, call the function init. The gettextinfo function, included with
  26133. Turbo C++, is called to provide menu with screen information as stored in the
  26134. text_info type structure called info. This structure allows you to derive the
  26135. current colors and cursor position for reference. Next, menu turns off the
  26136. cursor to present a cleaner looking menu and calls show repeatedly to display
  26137. each menu item.
  26138. The main loop of menu displays the currently selected menu item in reverse
  26139. video, then waits for the user to press a key. Once a key is pressed, the
  26140. function tests for a scan code. menu then displays the current item in normal
  26141. video, effectively de-selecting it from the user's point of view. Finally, the
  26142. function calls keypress to act on the selected key. The value returned by
  26143. keypress determines whether or not the loop is repeated.
  26144. The last task menu must complete before exiting is to turn the cursor back on
  26145. and return the user's selection to the calling function.
  26146. Screen locations, as stated earlier, are set up by init and used by show.
  26147. Setting up the locations depends on which format, vertical or horizontal, is
  26148. specified. For vertical formats, screen location is simply based on the
  26149. starting row plus the current index. For horizontal formats, the position is
  26150. calculated from the starting column location plus the length of the previous
  26151. menu items. Because the horizontal format involves more calculation, it is
  26152. only done once in init, then it is stored in an array.
  26153. show adds the starting row and column information, contained in the x and y
  26154. variables, to an offset based on the chosen format (horizontal or vertical).
  26155. It sets the video mode to highlight by setting the high bit of the foreground
  26156. color attribute. It then displays the first character, returns video mode to
  26157. normal, and displays the rest of the menu item.
  26158. keypress has been set up separately to isolate the logic necessary to handle
  26159. the user's input, so you can easily add new key handling ability to the
  26160. function's repertoire. keypress is responsible for calculating which menu item
  26161. will be made current based on which key the user pressed. It does this using
  26162. an index value (variable i) that can vary from zero to the maximum number of
  26163. items less one. The function returns --1 as an index value when the user has
  26164. pressed the enter key. menu then knows to quit and return whichever option was
  26165. previously current to the calling function. Note that keypress returns an
  26166. index value instead of the menu item letter. keypress was implemented to
  26167. return an index value so show will easily know where to display the next menu
  26168. item.
  26169. The last function to discuss is findletter. It is called from keypress
  26170. whenever the user presses a key that is not specifically handled. findletter
  26171. loops through the menu items looking for a match between the key pressed and
  26172. the first character of the menu item. If found, it changes the current menu
  26173. item index and passes it to the caller as a return value.
  26174. Collectively, these functions provide a simple and clean looking
  26175. point-and-shoot menu system that is flexible enough to handle most
  26176. applications' needs. At the same time, it is simple enough not to bog down the
  26177. programmer in detail. There are, obviously, many enhancements that can be made
  26178. to this system.
  26179.  
  26180.  
  26181. Enhancements
  26182.  
  26183.  
  26184. The basic point-and-shoot menu theme has many variations. For example, you can
  26185. display a more descriptive explanation of each menu item on a separate line
  26186. when it is selected. To do this you might want to pass to menu another array
  26187. of pointers (call this the description array) which correspond on a
  26188. one-for-one basis to the original array (list of menu items). If the first
  26189. menu item is "Load," then the first element of the description array might be
  26190. "Load a file from disk." show would display this description on some
  26191. predesignated line. Many applications reserve the bottom line of the screen
  26192. display for this purpose.
  26193. You could enhance this implementation by creating a more sophisticated
  26194. approach to color selection. Even though the menu example described in this
  26195. article is a stand-alone module, it would normally be incorporated as part of
  26196. an overall module to handle screen formatting. Several other utility functions
  26197. for windowing and formatting would be included that also require a way to
  26198. specify color selection. One popular way to designate color selection is to
  26199. define a color palette, implemented as an array of allowable color
  26200. combinations for foreground and background. You might select normal,
  26201. highlight, and reverse colors with a call like color(3,2,5) in which each
  26202. parameter represents a specific index within the color palette.
  26203. This particular implementation of point-and-shoot menus relies on the user to
  26204. provide a Quit option as one of the menu items. Many applications recognize
  26205. the escape key as the key to press to back out of a menu without selecting
  26206. anything. To add this capability to the example, modify keypress to return a
  26207. special value for escape to menu. A value of --2 should be used since --1
  26208. designates the enter key. (Remember that keypress returns an index value, not
  26209. a character.) In turn, menu returns some prearranged value such as ASCII 27,
  26210. so the calling application can recognize a back-out request and act
  26211. accordingly.
  26212. Many applications these days are demanding mouse support. The actual
  26213. implementation would depend on the mouse support library that you use. A
  26214. detailed discussion of mouse support is beyond the scope of this article, but
  26215. its impact on the menu system is easy to describe.
  26216. Adding mouse support would involve replacing the getch logic used for
  26217. detecting characters and scan codes with a separate function. This function
  26218. would return the same character value or scan code based on the user's
  26219. selection. The function would probably detect what the user selected using
  26220. some sort of loop that continuously tests the keyboard and mouse devices for
  26221. input. You need modify no other function.
  26222. This example of point-and-shoot menus was implemented in ANSI C to enhance its
  26223. portability. Note that it's not strictly ANSI. For example, gettextinfo is
  26224. available only for IBM-compatible systems. However, other systems do have
  26225. other functions for performing similar tasks.
  26226. Implementing a point-and-shoot menu system with the object-oriented flavor of
  26227. Turbo C++ has many advantages also. One of the greatest advantages is the
  26228. ability of a function to inherit behavior. For example, if you want to enhance
  26229. the behavior of function keypress (called method keypress in OOP) to recognize
  26230. the escape key, you create a new function (method) that contains only the
  26231. escape key handling logic. Everything else is already provided.
  26232.  
  26233.  
  26234. Summary
  26235.  
  26236.  
  26237. You can approach programming problems or ideas in a variety of ways. I've
  26238. attempted to describe one generalized approach and several possible
  26239. enhancements that I've had great success with in my own application programs.
  26240. The overall design goal of this menu system is to isolate into separate
  26241. functions, as much as possible, discreet parts of the process such as keyboard
  26242. handling, location calculations, and screen displays. This modular approach
  26243. facilitates making future enhancements without completely rewriting the
  26244. process every time.
  26245.  
  26246. Listing 1 (cujmenu.c)
  26247. /************************************************************
  26248. An implementation of the point and shoot menu system.
  26249. The main function is a simple demonstration routine.
  26250.  
  26251. */
  26252.  
  26253. #include <stdio.h>
  26254. #include <conio.h>
  26255.  
  26256. #define CRET 0x0d /* key definitions */
  26257. #define TAB 0x09
  26258. #define BACKTAB 0x0f /* key scan codes */
  26259. #define UPARROW 0x48
  26260. #define DNARROW 0x50
  26261. #define LTARROW 0x4b
  26262. #define RTARROW 0x4d
  26263. #define PAGEUP 0x49
  26264. #define PAGEDN 0x51
  26265. #define HOMEKEY 0x47
  26266. #define ENDKEY 0x4f
  26267. #define CTRLEND 0x75
  26268.  
  26269. /************************************************************
  26270. display one line of menu at the proper location. If
  26271. an X offset is specified then this must be horizontal
  26272. formatted menu.
  26273. */
  26274. void show (int x,int y,int xoff,int yoff,char *p,int fore)
  26275. {
  26276. if (xoff) /* if an X offset exist */
  26277. x += xoff;
  26278. else
  26279. y += yoff;
  26280.  
  26281. gotoxy(x,y);
  26282. textcolor(fore8); /* use intense color */
  26283. putch(*p++); /* print the first char */
  26284. textcolor(fore); /* back to normal color */
  26285. cputs(p); /* print the rest of it */
  26286. }
  26287.  
  26288. /************************************************************
  26289. search the p list for a beginning character in the
  26290. menu list that matches c.
  26291. */
  26292. int findletter (char c, char *p[], int i)
  26293. {
  26294. int j = 0;
  26295.  
  26296. while (*p[j] != NULL) /* while not at end */
  26297. if (c == *p[j]) /* if we find a match */
  26298. { i = j; break; } /* record and quit */
  26299. else
  26300. j++;
  26301.  
  26302. return(i); /* return the new idx */
  26303. }
  26304.  
  26305. /************************************************************
  26306. initialize certain location variables to allow us
  26307. to write at proper screen location.
  26308. */
  26309. int init (char *p[], int horz, int *xoff)
  26310.  
  26311. {
  26312. int max = 0, x = 0;
  26313.  
  26314. if (horz) /* if horizontal format */
  26315. {
  26316. while (p[max] != NULL) /* while not at end */
  26317. {
  26318. *xoff++ = x; /* calc X offsets */
  26319. x += strlen(p[max++]); /* add length of item */
  26320. }
  26321. }
  26322.  
  26323. else /* else zero it out */
  26324. {
  26325. while (p[max++] != NULL) *xoff++ = 0;
  26326. max--;
  26327. }
  26328.  
  26329. return(max); /* number of items found */
  26330. }
  26331.  
  26332. /************************************************************
  26333. set the index according to user's response. returns
  26334. the new index value or -1 if the user pressed enter.
  26335. */
  26336. int keypress (char *p[], char c, int i, int max)
  26337. {
  26338. int done = 0;
  26339.  
  26340. switch(c) /* switch on user input */
  26341. {
  26342. case UPARROW:
  26343. case LTARROW:
  26344. case BACKTAB: i--; break;
  26345. case DNARROW:
  26346. case RTARROW:
  26347. case TAB: i++; break;
  26348. case CRET: done=1; break;
  26349. default: i = findletter(c,p,i);
  26350. }
  26351.  
  26352. if (done) i = -1; else /* if done, ret -1 */
  26353. if (i < 0) i = max-1; else /* else do range check */
  26354. if (i == max) i = 0;
  26355.  
  26356. return(i); /* ret new index */
  26357. }
  26358.  
  26359. /************************************************************
  26360. display a point and shoot menu. Return the first
  26361. character of the menu option selected.
  26362. */
  26363. char menu (char *p[], int horz)
  26364. #define MAX_ELEMENTS 12
  26365. {
  26366. struct text_info info; /* holds text screen info */
  26367. int i, j, x = 0, y, fore, back, max, savei;
  26368. int xoff[MAX_ELEMENTS]; /* for use w/ horiz format */
  26369. char c;
  26370.  
  26371.  
  26372. max = init(p,horz,xoff); /* calc screen positions */
  26373. gettextinfo(&info); /* get current screen attr */
  26374. fore = info.attribute & 0x0f;/* extract foreground color */
  26375. back = info.attribute >> 4; /* extract background color */
  26376. x = info.curx; /* establish reference location */
  26377. y = info.cury;
  26378. _setcursortype(_NOCURSOR); /* turn cursor off */
  26379. for (i=0; i<max; i++) /* display all menu items */
  26380. show(x,y,xoff[i],i,p[i],fore);
  26381. i = 0;
  26382.  
  26383. do
  26384. {
  26385. textcolor(back); /* set reverse video mode */
  26386. textbackground(fore);
  26387. show(x,y,xoff[i],i,p[i],back); /* display selected item*/
  26388. if ((c = toupper(getch())) == 0)/* if 0 is returned */
  26389. c = getch(); /* then get scan code */
  26390. textcolor(fore); /* set normal video mode */
  26391. textbackground(back);
  26392. show(x,y,xoff[i],i,p[i],fore); /* redisplay item */
  26393. savei = i; /* save prev sel item */
  26394. }
  26395.  
  26396. while ((i = keypress(p,c,i,max)) != -1); /* quit when user selects */
  26397.  
  26398. _setcursortype(_NORMALCURSOR); /* turn cursor back on */
  26399. return(*p[savei]); /* return selection */
  26400. }
  26401.  
  26402. /************************************************************
  26403. demonstration main function to show use of the point
  26404. and shoot menu.
  26405. */
  26406. void main (void)
  26407. {
  26408. char *p[] = {"First ","Second ","Third",
  26409. "Quit ",NULL}, c;
  26410. int format = 0;
  26411.  
  26412. textcolor(LIGHTGRAY);
  26413. textbackground(BLUE);
  26414. clrscr();
  26415. cputs("Display menu in horizontal format? (y/n) >");
  26416. if (toupper(getch()) == 'Y')format = 1;
  26417. gotoxy(10,10);
  26418. while ((c = menu(p,format)) != 'Q')
  26419. {
  26420. gotoxy(10,20);
  26421. cprintf("The selected option is: %c\n\r",c);
  26422. gotoxy(10,10);
  26423. }
  26424. }
  26425.  
  26426.  
  26427.  
  26428.  
  26429.  
  26430.  
  26431.  
  26432.  
  26433.  
  26434.  
  26435.  
  26436.  
  26437.  
  26438.  
  26439.  
  26440.  
  26441.  
  26442.  
  26443.  
  26444.  
  26445.  
  26446.  
  26447.  
  26448.  
  26449.  
  26450.  
  26451.  
  26452.  
  26453.  
  26454.  
  26455.  
  26456.  
  26457.  
  26458.  
  26459.  
  26460.  
  26461.  
  26462.  
  26463.  
  26464.  
  26465.  
  26466.  
  26467.  
  26468.  
  26469.  
  26470.  
  26471.  
  26472.  
  26473.  
  26474.  
  26475.  
  26476.  
  26477.  
  26478.  
  26479.  
  26480.  
  26481.  
  26482.  
  26483.  
  26484.  
  26485.  
  26486.  
  26487.  
  26488.  
  26489.  
  26490.  
  26491.  
  26492.  
  26493.  
  26494. Serial Communications With Turbo C
  26495.  
  26496.  
  26497. Greg Chursenoff
  26498.  
  26499.  
  26500. This article is not available in electronic form.
  26501.  
  26502.  
  26503.  
  26504.  
  26505.  
  26506.  
  26507.  
  26508.  
  26509.  
  26510.  
  26511.  
  26512.  
  26513.  
  26514.  
  26515.  
  26516.  
  26517.  
  26518.  
  26519.  
  26520.  
  26521.  
  26522.  
  26523.  
  26524.  
  26525.  
  26526.  
  26527.  
  26528.  
  26529.  
  26530.  
  26531.  
  26532.  
  26533.  
  26534.  
  26535.  
  26536.  
  26537.  
  26538.  
  26539.  
  26540.  
  26541.  
  26542.  
  26543.  
  26544.  
  26545.  
  26546.  
  26547.  
  26548.  
  26549.  
  26550.  
  26551.  
  26552.  
  26553.  
  26554.  
  26555. Some Small C++ Classes
  26556.  
  26557.  
  26558. Joe Schell
  26559.  
  26560.  
  26561. Joe Schell received his B.A. in mathematics from the University of Colorado.
  26562. He has been programming 15 years as a hobby, five of those years in C. Joe
  26563. specializes in writing C/C+ + libraries. You can contact him at P.O. Box 7039,
  26564. Boulder, CO 80306.
  26565.  
  26566.  
  26567. I have read a number of books and articles on C++. All of them have code in
  26568. them, but none of them seem to have any small classes that are useful. I
  26569. define a small class as one that is solely contained in the header file.
  26570. Additionally the methods (the functions of the class) should be simple and
  26571. will probably compile as in-line code. 
  26572. Here are a few small classes that should be useful. I will describe four
  26573. classes: boolean, byte, word, and check_heap. The first handles true and false
  26574. values. byte and word handle 8-bit and 16-bit values that are found in memory.
  26575. check_heap is a debugging tool that determines if constructors and destructors
  26576. are handling memory allocation correctly.
  26577.  
  26578.  
  26579. boolean Class
  26580.  
  26581.  
  26582. I modeled the boolean class on the boolean type in Pascal. The implementation
  26583. was easy, and the code should be mostly self-explanatory. I did have some
  26584. doubts over some implementation details. Initially I was going to make the ++
  26585. force the boolean to true and the -- -- operator force it to false. But that
  26586. didn't add anything since the programmer could simply set the Boolean value if
  26587. he wants to force it to a specific value. So, instead these two operators
  26588. reverse the value of the Boolean.
  26589. In Pascal, the true and false values are constant. The const keyword does the
  26590. same thing for the C+ + values. The only methods that can be called for a
  26591. const boolean are operator int( ), operator ~( ), and make_string. Those
  26592. methods are defined as constant by the use of const in front of the function
  26593. definition and because they don't modify the Boolean value.
  26594. Initially, the make_string method was actually implemented as the conversion
  26595. operator for char*: operator char*( ). Although this method seems intuitive,
  26596. C++ v2.0 does not have a precedence order for picking an int conversion over a
  26597. char* conversion in conditional statements.
  26598. Most of my code in the last two years has used Booleans in conditionals --
  26599. although I have never printed true/false. So the choice of the conversion
  26600. operator was easy. It is interesting to look at the line false = true; in
  26601. testbool.cpp. Because true is a constant, that line should produce an error
  26602. message. In Turbo C++, it produces a warning message. A similar line using a
  26603. const int does produce an error message. The code for the boolean class is in
  26604. boolean.hpp. The test routine testbool.cpp will check it. 
  26605.  
  26606.  
  26607. check_heap Class
  26608.  
  26609.  
  26610. I developed the next class while I was working on some constructors and
  26611. destructors that allocated and deallocated memory. I needed to test that the
  26612. heap was being handled correctly after the destructors were called. Because I
  26613. had several different simultaneous tests, a class was the perfect way to
  26614. handle it. Look at the file testheap.cpp for several examples
  26615. The check_heap variable is declared at the beginning of a suspected problem
  26616. area. The method test is called any number of times after that. test will send
  26617. an error message to cerr if the heap is different than when an instance of the
  26618. class is declared. I found this useful even in places where I knew that the
  26619. heap was supposed to be different. When using this class, keep in mind that it
  26620. is often useful to pass a class instance to a function as the following line
  26621. does in testheap.cpp.
  26622. test_value(t);
  26623. Don't forget that main creates a temporary value for t. It is this temporary
  26624. value that actually gets passed to test_value. Because the destructor for the
  26625. temporary doesn't get called until the end of main, an error message is
  26626. produced. (Even after two years of using C++ and the check_heap class, I still
  26627. forget this detail all the time.) The best way to avoid the problem is to
  26628. insert another function call level into the code. You can use test_test_value.
  26629. The temporary value is created inside this function and destroyed before it
  26630. returns.
  26631. If there is a problem when test is called, the function will print out the
  26632. difference from when it started and the current position. I seldom find that
  26633. number of any real use, but it has helped me occasionally when I had no idea
  26634. where the problem was. I just start poking at random items until the
  26635. difference changes by a little bit. Then I know I am in the right area.
  26636. Since that difference can be useful, I spent a lot of time making sure that
  26637. the number it printed made some kind of sense. PC compatibles use a segmented
  26638. memory structure, so the code must adjust for different memory models. The
  26639. macro CHECK_HEAP_diff_ makes this adjustment. It is possible that test will
  26640. not be inlined even though it looks as though it should be. However, it is
  26641. certainly not worth the effort of keeping it in a separate source file. I
  26642. prefer to keep it all in the header file. The code for check_heap is in
  26643. chkheap.hpp.
  26644.  
  26645.  
  26646. byte And word Classes
  26647.  
  26648.  
  26649. The last two classes are byte and word. These classes could almost be
  26650. implemented as typedefs, except that they check for valid values. byte is used
  26651. just like an unsigned char.word is used just like an unsigned int. If a larger
  26652. value is assigned to either one, then an error message is generated at
  26653. runtime. In the test code testbyte.cpp, byte is used like a pointer to char,
  26654. and word is used like a pointer to int. For example:
  26655. d = (byte*)test_byte;
  26656. ...
  26657. z = (word*)(&test_word);
  26658. This is permissible only when the byte class is the same size as a char and
  26659. the word class is the same size as an int. In particular, the byte class will
  26660. not work on a 286 or 386 when word alignment is used. To check the size I have
  26661. included the two #if directives that are at the end of the byte.hpp file. They
  26662. will produce a compile-time error if the sizes do not match. I would probably
  26663. not use these as pointers to ints or chars though. They are intended to point
  26664. to various locations directly in memory, as when you access data for the
  26665. serial ports or the keyboard directly. Note that on PC compatibles, these can
  26666. also be used as far pointers in the following manner:
  26667. byte far *b =
  26668. far_address_of_byte;
  26669.  
  26670.  
  26671. Some Final Comments
  26672.  
  26673.  
  26674. I find the file form.h to be useful. It does not contain any classes, and it
  26675. is only needed for the function form. I haven't found any way to use the Turbo
  26676. C++ form function and still use iostream.h, so I made my own function.
  26677. I use the following naming scheme for my files. The extension hpp is used for
  26678. include files which have classes in them. The extensions h and hh are used as
  26679. they are in C. The extension cpp indicates a C++ source file, containing
  26680. either classes or final applications such as the testing routines. The c
  26681. extension is used for C code only. I make this distinction because I write in
  26682. C and C++, and I need to keep the two separate. In addition, when I am looking
  26683. through code I want to be able to distinguish between include files with
  26684. classes and those without classes. These distinctions make life a little
  26685. easier for me.
  26686.  
  26687. Listing 1 (boolean.hpp)
  26688. /*********************************************************************/
  26689. /* Boolean class. Copyright by Joe Schell 1989. */
  26690. /*********************************************************************/
  26691.  
  26692. #ifndef CLASS_boolean
  26693.  
  26694. #define CLASS_boolean
  26695.  
  26696. const int TRUE = 1;
  26697. const int FALSE = 0;
  26698.  
  26699. class boolean
  26700. {
  26701. int b; // boolean type.
  26702. void value(const int i) { b = (i) ? TRUE : FALSE; }
  26703.  
  26704. public:
  26705. boolean() { b = FALSE; }
  26706. boolean(const int i) { vaLue(i); }
  26707. boolean(const boolean &i) { b = i.b; }
  26708.  
  26709. operator int() const { return b; }
  26710. operator ~() const { return b ? FALSE : TRUE; }
  26711. boolean &operator=(const int &i) { value(i); return *this; }
  26712. boolean &operator++() { b = b ? FALSE : TRUE; return *this; }
  26713. boolean &operator--() { return (*this)++; }
  26714.  
  26715. char *make_string() const { return b ? "true" : "false"; }
  26716.  
  26717. }; // End of boolean class.
  26718.  
  26719. const boolean true(TRUE), false(FALSE);
  26720.  
  26721. #endif
  26722.  
  26723.  
  26724. Listing 2 (testbool.cpp)
  26725. /********************************************************************/
  26726. /* Test the boolean class. */
  26727. /********************************************************************/
  26728.  
  26729. #include <iostream.h>
  26730. #include <boolean.hpp>
  26731.  
  26732. char *test(int i) {return i ? "okay.\n" : "not okay.\n"; }
  26733.  
  26734. main()
  26735. {
  26736.  
  26737. boolean b1,b2;
  26738.  
  26739. cout << "Testing boolean class\n";
  26740. cout << "Constructed value and int() is " << test(b1 == FALSE);
  26741. cout << "Comparison is " << test(b1 == b2);
  26742. b1 = 1;
  26743. cout << "Operator() is " << test(b1 == true);
  26744. b1 = ~b2;
  26745. cout << "Operator~() is " << test(b1 == true);
  26746. b1++;
  26747. cout << "Operator++() is " << test(b1 == false);
  26748. b1--;
  26749. cout << "Operator--() is " << test(b1 == true);
  26750. cout << "Make_string() is " << bl.make_string() << "ly okay.\n";
  26751.  
  26752. true = false; // This only produces warning message.
  26753.  
  26754. }
  26755.  
  26756.  
  26757. Listing 3 (testheap.cpp)
  26758. /********************************************************************/
  26759. /* Test the cheak_heap class. */
  26760. /********************************************************************/
  26761.  
  26762. #include <iostream.h>
  26763. #include <chkheap.hpp>
  26764.  
  26765. struct test_class_bad // Class that does not deallocate.
  26766. {
  26767. char *p;
  26768. test_class_bad() { p = new char; }
  26769. ~test_class_bad() { /* p is not deleted. */ }
  26770. private:
  26771. test_class_bad(test_class_bad&);
  26772. };
  26773.  
  26774. struct test_class_good // Class that does deallocate.
  26775. {
  26776. char *p;
  26777. test_class_good() { p = new char; }
  26778. test_class_good(test_class_good &t) { p = new char; *p = *(t.p); }
  26779. ~test_class_good() { delete p; }
  26780. };
  26781.  
  26782. char *test_easy(const int); // Prototypes for test functions.
  26783. char *test_class(const int);
  26784. void test_value(test_class_good);
  26785. void test_test_value(test_class_good &t) { test_value(t); }
  26786.  
  26787. main()
  26788. {
  26789. test_class_good t;
  26790. char *p;
  26791.  
  26792. cout << "Testing check_heap class. Should have three okay errors\n";
  26793. check_heap check;
  26794.  
  26795. p = test_easy(1);
  26796. check.test("Test_easy(1) error: ");
  26797.  
  26798. p = test_easy(0);
  26799. check.test("This error is okay: ");
  26800. delete p; // Clean up memory and fix for next check.test().
  26801.  
  26802. p = test_class(1);
  26803. check.test("Test_class(1) error: ");
  26804.  
  26805. p = test_class(0);
  26806. check.test("This error is okay: ");
  26807. delete p; // Clean up memory.
  26808.  
  26809. check.start(); // Get ready for next call to check.test().
  26810.  
  26811. // Next line demonstrates compiler creating temp value.
  26812. test_value(t);
  26813.  
  26814. check.testnew("This error is okay: ");
  26815. test_test_value(t);
  26816. check.test("Test_test_value(t) error: ");
  26817. }
  26818.  
  26819. char *test_class(const int i)
  26820. {
  26821. char *r;
  26822.  
  26823. if (i)
  26824. {
  26825. test_class_good t;
  26826. r = 0;
  26827. }
  26828. else{
  26829. test_class_bad t;
  26830. r = t.p;
  26831. }
  26832. return r;
  26833. }
  26834.  
  26835. char *test_easy(const int i)
  26836. {
  26837. char *p = new char;
  26838. if (i) { delete p; p = 0; }
  26839. return p;
  26840. }
  26841.  
  26842. void test_value(test_class_good t)
  26843. { /* Ignore warning about t not being used. */ }
  26844.  
  26845.  
  26846. Listing 4 (chkheap.hpp)
  26847. /*******************************************************************/
  26848. /* Check class allocation errors. Copyright by Joe Schell 1989. */
  26849. /*******************************************************************/
  26850.  
  26851. #ifndef CLASS_check_heap
  26852. #define CLASS_check_heap
  26853.  
  26854. #include <iostream.h>
  26855. #include <stddef.h> // Used for ptrdiff_t definition.
  26856.  
  26857. // CHECK_HEAP_diff_: Used get around segmented memory on IBMs.
  26858. #if defined(_TURBOC_) \
  26859. && (defined(_LARGE_) defined(_HUGE_) defined(_COMPACT_))
  26860. #define CHECK_HEAP_diff_ char huge *
  26861. #else
  26862. #define CHECK_HEAP_diff_ char*
  26863. #endif
  26864.  
  26865.  
  26866. class check_heap
  26867. {
  26868. public:
  26869. void start() { begin = new char; delete begin; }
  26870. check_heap() { start(); }
  26871.  
  26872. void test(const char *s=0) // Do a test.
  26873.  
  26874. {
  26875. end = new char;
  26876. if (begin != end)
  26877. cerr << s
  26878. << "Heap error: entry/exit difference = "
  26879. << diff() << ".\n";
  26880. delete end;
  26881. }
  26882.  
  26883. void testnew(const char *s=0) // Do a test and reset.
  26884. { test(s); start(); }
  26885.  
  26886. private:
  26887. char *begin, *end; // Beginning and end of allocation.
  26888.  
  26889. ptrdiff_t diff() const
  26890. { return (ptrdiff_t)
  26891. ((CHECK_HEAP_diff_)end - (CHECK_HEAP_diff_)begin);}
  26892.  
  26893. }; // End of check_heap class.
  26894.  
  26895. #endif
  26896.  
  26897.  
  26898. Listing 5 (testbyte.cpp)
  26899. /*****************************************************************/
  26900. /* Test byte and word classes. Copyright Joe Schell 1989. */
  26901. /*****************************************************************/
  26902.  
  26903. #include <byte.hpp>
  26904.  
  26905. #define comp(c,i) (((c) == int(i)) ? "okay.\n" : "not okay.\n")
  26906.  
  26907. void test_init(int c) {cout << " Initialization is " << comp(c,3);}
  26908. void test_inc(int c) { cout << " Increment is" << comp(c,4); }
  26909. void test_dec(int c) { cout << " Decrement is" << comp(c,3); }
  26910. void test_eql(int c) { cout << " Equal for int is " << comp(c,3);}
  26911.  
  26912.  
  26913. main()
  26914. {
  26915. cout << "Testing byte and word class.\n";
  26916.  
  26917. byte b, c=3, *d;
  26918. char *test_byte = "abc";
  26919.  
  26920. cout << "Byte:( should be 03, result=" << c.make_string() << ")\n";
  26921. test_init(c);
  26922. c++; test_inc(c);
  26923. c--; test_dec(c);
  26924. b=c; test_eql(b);
  26925. c=4;
  26926. cout << " Setting equal to integer is " << comp(c,4);
  26927.  
  26928. b=c;
  26929. c++;
  26930. cout << " Comparison of bytes is"
  26931. << ((b!=c) ? "okay." : "not okay.") << "\n";
  26932. d = (byte*)test_byte;
  26933.  
  26934. cout << " Pointing is" << comp(*d,*test_byte);
  26935. d++;
  26936. cout << " Incrementing pointer is " << comp(*d,*(test_byte+1));
  26937.  
  26938.  
  26939. word x, y=3, *z;
  26940. int test_word=8;
  26941. cout << "\nWord:( should be 0003, result="
  26942. << y.make_string() << ")\n";
  26943. test_init(y);
  26944. y++; test_inc(y);
  26945. y--; test_dec(y);
  26946. x=y; test_eql(x);
  26947. y++;
  26948. cout <<" Comparison of words is"
  26949. << ((x!=y) ? "okay." :"not okay.") << "\n";
  26950. z = (word*)(&test_word);
  26951. cout << " Pointer to word is" << comp(*z,test_word);
  26952. (*z)++;
  26953. cout << " Dereference and increment is " << comp(*z,9);
  26954.  
  26955. // The next two lines should cause 'Illegal values' when
  26956. // not commented.
  26957. // b=UCHAR_MAX + 1;
  26958. // x=(long)UINT_MAX + 1;
  26959.  
  26960. cout << "\nTest is finished.\n";
  26961. }
  26962.  
  26963.  
  26964. Listing 6 (byte.hpp)
  26965. /*************************************************************/
  26966. /* Byte classes. Copyright by Joe Schell 1989. */
  26967. /*************************************************************/
  26968.  
  26969. #ifndef CLASS_byte
  26970. #define CLASS_byte
  26971. #include <limits.h> // Maximum values UCHAR_MAX and UINT_MAX.
  26972. #include <stdlib.h> // prototype exit() and EXIT_FAILURE.
  26973. #include <iostream.h>
  26974. #include <form.h>
  26975. /*--------------------------------------------------------------------*/
  26976. /* byte Handle a byte. */
  26977. /*--------------------------------------------------------------------*/
  26978. class byte
  26979. {
  26980. public:
  26981. byte() { c = 0; }
  26982. byte(int &i) { c = value(i); }
  26983. operator int() const { return c; }
  26984. byte operator=(int &i) { c = value(i); return *this; }
  26985. byte operator++() { c++; return *this; }
  26986. byte operator--() { c--; return *this; }
  26987. char *make_string() { return form("%2.2X", int(c));}
  26988.  
  26989. private:
  26990. unsigned char c; // A byte.
  26991. unsigned char value(int &i)
  26992. {
  26993.  
  26994. if (i > UCHAR_MAX)
  26995. {
  26996. cerr << "\nByte class: Illegal value-" << i << "\n";
  26997. exit(EXIT_FAILURE);
  26998. }
  26999. return (unsigned char)i;
  27000. }
  27001. }; // End of byte class.
  27002. /*---------------------------------------------------------------------*/
  27003. /* word Handle a word */
  27004. /*---------------------------------------------------------------------*/
  27005. class word
  27006. {
  27007. public:
  27008. word() { i = 0; }
  27009. word(long &x) { i = value(x); }
  27010. operator long() const { return (long)i; }
  27011. word operator++() { i++; return *this; }
  27012. word operator--() { i--; return *this; }
  27013. char *make_string() { return form("%4.4X", i); }
  27014. private:
  27015. unsigned int i; // An int.
  27016. unsigned int value(long &x)
  27017. {
  27018. if (x > UINT_MAX)
  27019. {
  27020. cerr << "\nWord class: Illegal value-" << x << "\n";
  27021. exit(EXIT_FAILURE);
  27022. }
  27023. return (unsigned int)x;
  27024. }
  27025. }; // End of word class.
  27026.  
  27027. #if sizeof(unsigned char) != sizeof(byte)
  27028. #error Byte class cannot be used as pointer to memory.
  27029. #endif
  27030. #if sizeof(unsigned int) != sizeof(word)
  27031. #error Word class cannot be used as pointer to memory.
  27032. #endif
  27033.  
  27034. #endif // #ifndef CLASS_byte
  27035.  
  27036.  
  27037. Listing 7(form.h)
  27038. /******************************************************************/
  27039. /* Formatted output functions. Note: the return value */
  27040. /* is only good until a form function is used again. */
  27041. /******************************************************************/
  27042. #ifndef INCLUDE_form
  27043. #define INCLUDE_form
  27044.  
  27045. #ifndef_STDIO_H
  27046. #include <stdio.h>
  27047. #endif
  27048.  
  27049. static char _form_s[256];
  27050.  
  27051.  
  27052. static char *dec(long i,int w=0)
  27053.  
  27054. { sprintf(_form_s, "%*ld", w, i);
  27055. return _form_s;
  27056. }
  27057.  
  27058. static char *hex(int i,int w=0)
  27059. { sprintf(_form_s, "%0*X", (w ? w : sizeof(int)), i);
  27060. return _form_s;
  27061. }
  27062.  
  27063. static char *hex(long i, int w=0)
  27064. { sprintf(_form_s, "%0*X", (w ? w : sizeof(long)), i);
  27065. return _form_s;
  27066. }
  27067. static char *chr(int i,int w=0)
  27068. { sprintf(_form_s, "%*c", w, i);
  27069. return_form_s;
  27070. }
  27071.  
  27072. static char *form(char *f,...)
  27073. { vsprintf(_form_s, f, ...); return _form_s; }
  27074.  
  27075. #endif
  27076.  
  27077.  
  27078.  
  27079.  
  27080.  
  27081.  
  27082.  
  27083.  
  27084.  
  27085.  
  27086.  
  27087.  
  27088.  
  27089.  
  27090.  
  27091.  
  27092.  
  27093.  
  27094.  
  27095.  
  27096.  
  27097.  
  27098.  
  27099.  
  27100.  
  27101.  
  27102.  
  27103.  
  27104.  
  27105.  
  27106.  
  27107.  
  27108.  
  27109.  
  27110.  
  27111.  
  27112.  
  27113.  
  27114.  
  27115.  
  27116.  
  27117. Standard C
  27118.  
  27119.  
  27120. Implementing <locale.h>
  27121.  
  27122.  
  27123.  
  27124.  
  27125. P.J. Plauger
  27126.  
  27127.  
  27128. P.J. Plauger is senior editor of The C Users Journal. He is secretary of the
  27129. ANSI C standards committee, X3J11, and convenor of the ISO C standards
  27130. committee, WG14. His latest book is Standard C, which he co-authored with Jim
  27131. Brodie. You can reach him at pjp@plauger.uunet.
  27132.  
  27133.  
  27134.  
  27135.  
  27136. Introduction
  27137.  
  27138.  
  27139. Last month, I introduced the header <locale.h> and described its brief
  27140. history. I showed how to adapt a program to the default locale, and how to
  27141. partially revert behavior to the "C" locale when necessary. Now it's time to
  27142. look at some implementation details.
  27143. The easiest part is the function localeconv. All it must do is return a
  27144. pointer to a structure describing (parts of) the current locale. That
  27145. structure has type struct lconv, which is defined in <locale.h>. Here are the
  27146. easy parts of the implementation. Listing 1 shows the file locale.h. Listing 2
  27147. shows localeco.c. (The name is chopped to eight letters because of file naming
  27148. restrictions on MS-DOS and other systems.) Packed in with localeconv are the
  27149. structures holding the current and "C" locales.
  27150. I have defined additional fields in struct lconv, over and above those
  27151. specified by the C Standard. One field points at the name of the locale.
  27152. Another links these structures together into a list. (Initially, the "C"
  27153. locale is the only entry on the list.) The remaining fields contain
  27154. information that changes with a change in locale. The Standard C library I
  27155. have written defines several such fields. Here, I show only the ones that I've
  27156. discussed in earlier columns. You can see what is involved in controlling the
  27157. tables used by the functions in <ctype. h>.
  27158.  
  27159.  
  27160. What the Standard Says
  27161.  
  27162.  
  27163. The setlocale function introduces many more implementation isses than
  27164. localeconv. I showed what the Standard says about localeconv last month. Here
  27165. is what it has to say about setlocale:
  27166.  
  27167.  
  27168. 4.4.1 Locale Control
  27169.  
  27170.  
  27171.  
  27172.  
  27173. 4.4.1.1 The setlocale Function
  27174.  
  27175.  
  27176.  
  27177.  
  27178. Synopsis
  27179.  
  27180.  
  27181. #include <locale.h>
  27182. char *setlocale(int category,
  27183. const char *locale);
  27184.  
  27185.  
  27186. Description
  27187.  
  27188.  
  27189. The setlocale function selects the appropriate portion of the program's locale
  27190. as specified by the category and locale arguments. setlocale can change or
  27191. query the program's entire current locale or portions thereof. The value
  27192. LC_ALL for category names the program's entire locale; the other values for
  27193. category name only a portion of the program's locale. LC_COLLATE affects the
  27194. behavior of the strcoll and strxfrm functions. LC_CTYPE affects the behavior
  27195. of the character handling functions101 and the multibyte functions.
  27196. LC_MONETARY affects the monetary formatting information returned by
  27197. localeconv. LC_NUMERIC affects the decimal-point character for the formatted
  27198. input/output functions and the string conversion functions, as well as the
  27199. non-monetary formatting information returned by the localeconv function.
  27200. LC_TIME affects the behavior of the strftime function.
  27201. A value of "C"for locale specifies the minimal environment for C translation;
  27202. a value of " " for locale specifies the implementation-defined native
  27203. environment. Other implementation-defined strings may be passed as the second
  27204. argument to setlocale.
  27205. At program startup, the equivalent of
  27206. setlocale(LC_ALL, "C");
  27207. is executed.
  27208. The implementation shall behave as if no library function calls setlocale.
  27209.  
  27210.  
  27211.  
  27212. Returns
  27213.  
  27214.  
  27215. If a pointer to a string is given for locale and the selection can be honored,
  27216. the setlocale function returns a pointer to the string associated with the
  27217. specified category for the new locale. If the selection cannot be honored,
  27218. setlocale returns a null pointer and the program's locale is not changed.
  27219. A null pointer for locale causes setlocale to return a pointer to the string
  27220. associated with the category for the program's current locale; the program's
  27221. locale is not changed.102
  27222. The pointer to string returned by setlocale is such that a subsequent call
  27223. with that string value and its associated category will restore that part of
  27224. the program's locale. The string pointed to shall not be modified by the
  27225. program, but it may be overwritten by a subsequent call to setlocale.
  27226. Forward references: formatted input/output functions (4.9.6), the multibyte
  27227. character functions (4.10.7), the multibyte string functions (4.10.8), string
  27228. conversion functions (4.10.1), the strcoll function (4.11.4.3), the strftime
  27229. function (4.12.3.5), the strxfrm function (4.11.4.5).
  27230. Footnotes:
  27231. 101. The only functions in 4.3 whose behavior is not affected by the current
  27232. locale are isdigit and isxdigit.
  27233. 102. The implementation must arrange to encode in a string the various
  27234. categories due to a heterogeneous locale when category has the value LC_ALL.
  27235. [end of excerpt] 
  27236.  
  27237.  
  27238. Implementing setlocale
  27239.  
  27240.  
  27241. setlocale clearly has a number of tasks to perform. It must determine what
  27242. locales to switch to, based on the category and name you specify when you call
  27243. the function. It must find locales already in memory, or read in newly
  27244. specified locales from a file. (I describe the general case, of course. A
  27245. minimal implementation can recognize only the "C" and " " locales, which can
  27246. be the same.) And it must return a name that it can later use to restore the
  27247. current locale.
  27248. The last task is one of the hardest because you can construct a mixed locale,
  27249. containing categories from various locales. For example, you can write:
  27250. #include <locale.h>
  27251. .....
  27252. char *s1, s2;
  27253.  
  27254. setlocale(LC_ALL, "");
  27255. s1 = setlocale(LC_CTYPE, "C");
  27256. if ((s2 = malloc(strlen(s1) + 1)))
  27257. strcpy(s2, s1);
  27258. The first call switches to the native locale, which is some locale preferred
  27259. by the local operating environment. The second call reverts one category to
  27260. the "C" locale. You must make a copy of the string pointed to by s1 because
  27261. intervening calls to setlocale might alter it. If you later make the call
  27262. setlocale(LC_ALL, s2);
  27263. the locale reverts to its earlier mixed state.
  27264. setlocale must contrive a name that it can later use to reconstruct an
  27265. arbitrary mix of categories. The C Standard doesn't say how to do this, or
  27266. what the name looks like. It only says that an implementation must do it.
  27267. The scheme I settled on was to paste qualifiers on a locale name if it
  27268. contains mixed categories. Say, for example, that the base locale is "USA".
  27269. That gives you American date formats, the English alphabet, and so on. But an
  27270. application adapts the monetary category to the special conventions of
  27271. accounting -- a locale exists called "acct". The name that characterizes this
  27272. mixed locale is "USA;monetary:acct".
  27273. I use semicolons to separate components of the mixed locale name. Within a
  27274. component, a colon separates a category name from its locale name. The base
  27275. locale has no category name qualifier. When setlocale constructs a name, it
  27276. adds components only for categories that differ from the base locale.
  27277. Perhaps now you can understand some of the complexity of setlocale. Listing 3
  27278. shows the source file setlocal.c. Much of its logic is concerned with parsing
  27279. a name to determine which locale to use for each category. Another big chunk
  27280. of logic builds a name that setlocale can later digest. Everything else is
  27281. small potatoes by comparison.
  27282. To determine the native locale, I inspect the environment variable LOCALE.
  27283. That strikes me as a reasonable channel for determining what locale to favor.
  27284. It's akin to using the environment variable TZ to determine what time zone
  27285. you're in. The environment variable is inspected at most once during program
  27286. execution.
  27287. You will also see code that copies information into the "C" locale on the
  27288. first call to the function. I adopted that ruse to avoid a nasty snowball
  27289. effect. It's easy enough to pile all the various locale-dependent tables into
  27290. one structure. Do so, however, and you get the whole snowball regardless of
  27291. how little of it you use. I felt it was better to have setlocale do a bit more
  27292. work to avoid this problem. You don't want to drag in 10Kb of code when you
  27293. use only isspace from the library.
  27294. I offloaded some of the work to an internal function called _Getloc. Listing 4
  27295. shows the file xgetloc.c, or at least most of it. This function determines
  27296. whether a locale exists in memory. If a locale does not exist in memory,
  27297. _Getloc should go looking for it. I have stubbed that code out for this
  27298. presentation, because it takes a whole column just to describe how you can
  27299. make your own locale files and read them in at runtime.
  27300. Listing 5 shows the file xsetloc.c. It contains the function _Setloc, which
  27301. actually copies new information into the current locale. It also copies
  27302. information out to the various bits of static data affected by changes in the
  27303. locale. A call to setlocale drags in all this stuff. I don't know how to avoid
  27304. this particular snowball. At least you can avoid it if you leave locales
  27305. alone.
  27306. I have tested this code at least superficially. It should not contain major
  27307. errors. Be wary of small gaffes, however.
  27308.  
  27309.  
  27310. Conclusion
  27311.  
  27312.  
  27313. What I have presented here is just the basic machinery you need to support
  27314. locales. It is enough to let you build additional locales directly into the
  27315. library. Just add static declarations of type struct lconv and initialize them
  27316. as you see fit. Be sure to change_Clocale._Next to point at the list you add.
  27317. The real fun of locales is defining an open-ended set. To do that, you must be
  27318. able to specify a locale without altering C code. I have developed
  27319. considerable additional machinery that lets you do so. Next month, I will show
  27320. you the code that reads locale files.
  27321.  
  27322. Listing 1 (locale.h)
  27323. /* locale.h standard header */
  27324. #ifndef _LOCALE
  27325. #define _LOCALE
  27326. /* macros */
  27327. #define NULL_NULL
  27328. /* locale codes */
  27329. #define LC_ALL 0
  27330. #define LC_COLLATE 1
  27331. #define LC_CTYPE 2
  27332. #define LC_MONETARY 3
  27333. #define LC_NUMERIC 4
  27334.  
  27335. #define LC_TIME 5
  27336. /* ADD YOURS HERE */
  27337. #define _NCAT 6 /* one more than last */
  27338. /* type definitions */
  27339. struct lconv {
  27340. struct lconv *_Next;
  27341. const char *_Name;
  27342. /* controlled by LC_CTYPE */
  27343. const short *_Ctype;
  27344. const short *_Tolower;
  27345. const short *_Toupper;
  27346. /* controlled by LC_MONETARY */
  27347. char *currency_symbol;
  27348. char *int_curr_symbol;
  27349. char *mon_decimal_point;
  27350. char *mon_grouping;
  27351. char *mon _thousands_sep;
  27352. char *negative_sign;
  27353. char *positive_sign;
  27354. char frac_digits;
  27355. char int_frac_digits;
  27356. char n_cs_precedes;
  27357. char n_sep_by_space;
  27358. char n_sign_posn;
  27359. char p_cs_precedes;
  27360. char p_sep_by_space;
  27361. char p_sign_posn;
  27362. /* controlled by LC_NUMERIC */
  27363. char *decimal_point;
  27364. char *grouping;
  27365. char *thousands_sep;
  27366. };
  27367. /* declarations */
  27368. struct lconv *localeconv(void);
  27369. char *setlocale(int, const char *);
  27370. struct lconv *_Getloc(const char *, const char *);
  27371. struct lconv *_Setloc(int, struct lconv *);
  27372. extern struct 1conv _Clocale, _Locale;
  27373. /* macro overrides */
  27374. #define localeconv() (&_Locale)
  27375. #endif
  27376.  
  27377.  
  27378. Listing 2 (localeco.c)
  27379. /* localeconv function */
  27380. #include <limits.h>
  27381. #include <locale.h>
  27382.  
  27383. /* static data for "C" and current locales */
  27384. static char null[] = "";
  27385. struct lconv _Clocale = {
  27386. NULL, "C",
  27387. /* LC_CTYPE */
  27388. NULL, NULL, NULL,
  27389. /* LC_MONETARY */
  27390. null, /* currency_symbol */
  27391. null, /* int_curr_symbol */
  27392. null, /* mon_decimal_point */
  27393. null, /* mon_grouping */
  27394.  
  27395. null, /* mon_thousands_sep */
  27396. null, /* negative_sign */
  27397. null, /* positive_sign */
  27398. CHAR_MAX, /* frac_digits */
  27399. CHAR_MAX, /* int_frac_digits */
  27400. CHAR_MAX, /* n_cs_precedes */
  27401. CHAR_MAX, /* n_sep_by_space */
  27402. CHAR_MAX, /* n_sign_posn */
  27403. CHAR_MAX, /* p_cs_precedes */
  27404. CHAR_MAX, /* p_sep_by_space */
  27405. CHAR_MAX, /* p_sign_posn */
  27406. /* LC_NUMERIC */
  27407. ".", /* decimal_point */
  27408. null, /* grouping */
  27409. null, /* thousands_sep */
  27410. struct lconv _Locale = {
  27411. NULL, "C",
  27412. /* LC_CTYPE */
  27413. NULL, NULL, NULL,
  27414. /* LC_MONETARY */
  27415. null, /* currency_symbol */
  27416. null, /* int_curr_symbol */
  27417. null, /* mon_decimal_point */
  27418. null, /* mon_grouping */
  27419. null, /* mon_thousands_sep */
  27420. null, /* negative_sign */
  27421. null, /* positive_sign */
  27422. CHAR_MAX, /* frac_digits */
  27423. CHAR_MAX, /* int_frac_digits */
  27424. CHAR_MAX, /* n_cs_precedes */
  27425. CHAR_MAX, /* n_sep_by_space */
  27426. CHAR_MAX, /* n_sign_posn */
  27427. CHAR_MAX, /* p_cs_precedes */
  27428. CHAR_MAX, /* p_sep_by_space */
  27429. CHAR_MAX, /* p_sign_posn */
  27430. /* LC_NUMERIC */
  27431. ".", /* decimal_point */
  27432. null, /* grouping */
  27433. null, /* thousands_sep */
  27434.  
  27435. /* get pointer to current locale */
  27436. #undef localeconv
  27437. struct lconv *localeconv(void)
  27438. {
  27439. return (&_Locale);
  27440. }
  27441.  
  27442.  
  27443. Listing 3 (setlocal.c)
  27444. /* setlocale function */
  27445. #include <ctype.h>
  27446. #include <locale.h>
  27447. #include <stdlib.h>
  27448. #include <string.h>
  27449.  
  27450. #if _NCAT != 6
  27451. #error wrong number of categories
  27452. #endif
  27453. /* static data */
  27454.  
  27455. static char *defname = NULL;/* name of "" locale */
  27456. static int namalloc = 0;/* _Locale. _Name allocated
  27457. */
  27458. static const char * const nmcats[_NCAT] = {
  27459. NULL, "collate:", "ctype:", "monetary:",
  27460. "numeric:", "time:"};
  27461. static struct lconv *pcats[_NCAT] = {NULL};
  27462.  
  27463. /* set new locale */
  27464. #undef setlocale
  27465. char *setlocale(int cat, const char *lname)
  27466. {
  27467. if (cat < 0 _NCAT <= cat)
  27468. return (NULL); /* bad category */
  27469. if (lname == NULL)
  27470. return ((char *)_Locale._Name);
  27471. if (lname[0] == '\0')
  27472. { /* find name of default locale */
  27473. char *sl, *s2;
  27474.  
  27475. if (defname)
  27476. lname = defname;
  27477. else if ((sl = getenv("LOCALE")) != NULL
  27478. && (s2 = malloc(strlen(sl) + 1)) != NULL)
  27479. lname = defname = strcpy(s2, sl);
  27480. else
  27481. lname = "C";
  27482. }
  27483. if (_Clocale._Ctype == NULL)
  27484. { /* flesh out "C" locale */
  27485. size_t i;
  27486.  
  27487. for (i = 0; i < _NCAT; ++i)
  27488. pcats[i] = & _Clocale;
  27489. _Clocale._Ctype = _Ctype;
  27490. _Clocale._Tolower = _Tolower;
  27491. _Clocale._Toupper = _Toupper;
  27492. }
  27493. { /* set categories */
  27494. struct lconv *p;
  27495. int changed = 0;
  27496.  
  27497. if (cat != LC_ALL)
  27498. { /* set a single category */
  27499. if ((p = _Getloc(nmcats[cat], lname)) == NULL)
  27500. return (NULL);
  27501. if (p != pcats[cat])
  27502. pcats[cat] = _Setloc(cat, p), changed = 1;
  27503. }
  27504. else
  27505. { /* set all categories */
  27506. size_t i;
  27507.  
  27508. for (i = 0; ++i < _NCAT; )
  27509. { /* set a category */
  27510. if ((p = _Getloc(nmcats[i], lname)) == NULL)
  27511. { /* revert all on any failure */
  27512. setlocale(LC_ALL,_Locale._Name);
  27513. return (NULL);
  27514.  
  27515. }
  27516. if (p != pcats[i])
  27517. pcats[i] = _Setloc(i, p), changed = 1;
  27518. }
  27519. if ((p = _Getloc("", lname)) != NULL)
  27520. pcats[0] = p; /* set only if LC_ALL
  27521. component */
  27522. }
  27523. if (changed)
  27524. { /* rebuild _Locale._Name */
  27525. char *s;
  27526. size_t i, n;
  27527. size_t len = strlen(pcats[0]->_Name);
  27528.  
  27529. for (i = 0, n = 0; ++i < _NCAT; )
  27530. if (pcats[i] != pcats[0])
  27531. { /* count a changed subcategory */
  27532. len += strlen(nmcats[i])
  27533. + strlen(pcats[i]->_Name) + 1;
  27534. ++n;
  27535. }
  27536. if (in == 1)
  27537. { /* uniform locale */
  27538. if (namalloc)
  27539. free((void *)_Locale._Name);
  27540. Locale._Name = pcats[1]->_Name;
  27541. namalloc = 0;
  27542. }
  27543. else if ((s = malloc(len + 1)) == NULL)
  27544. { /* may be rash to try to roll back */
  27545. setlocale(LC_ALL, _Locale._Name);
  27546. return (NULL);
  27547. }
  27548. else
  27549. { /* build complex name */
  27550. if (namalloc)
  27551. free((void *)_Locale._Name);
  27552. _Locale. _Name = s;
  27553. namalloc = 1;
  27554. s += strlen(strcpy(s, pcats[0]->_Name));
  27555. for (i = 0; ++i < _NCAT; )
  27556. if (pcats[i] != pcats[0])
  27557. { /* add a component */
  27558. *s = ';';
  27559. s += strlen(strcpy(s, nmcats[i]));
  27560. s += strlen(strcpy(s, pcats[i]->_Name));
  27561. }
  27562. }
  27563. }
  27564. }
  27565. return ((char *)_Locale._Name);
  27566. }
  27567.  
  27568.  
  27569. Listing 4 (xgetloc.c)
  27570. /*_Getloc function */
  27571. #include <stdio.h>
  27572. #include <string.h>
  27573.  
  27574.  
  27575. /* get locale pointer, given category and name */
  27576. struct lconv *_Getloc(const char *nmcat, const char *lname)
  27577. {
  27578. const char *ns, *s;
  27579. size_t nl;
  27580. struct lconv *p;
  27581.  
  27582. { /* find category component of name */
  27583. size_t n;
  27584.  
  27585. for (ns = NULL, s = lname; = s += n + 1)
  27586. { /* look for exact match or LC_ALL */
  27587. if (s[n = strcspn(s, ":;")] == '\0' s[n] == ';')
  27588. { /* memorize first LC_ALL */
  27589. if (ns == NULL)
  27590. ns = s, nl = n;
  27591. if (s[n] == '\0')
  27592. break;
  27593. }
  27594. else if (memcmp(nmcat, s, ++n) == 0)
  27595. { /* found exact category match */
  27596. ns = s + n, nl = strcspn(ns, ";");
  27597. break;
  27598. }
  27599. else if (s[n += strcspn(s + n, ";")] == '\0')
  27600. break;
  27601. }
  27602. if (ns == NULL)
  27603. return (NULL); /* invalid name */
  27604. }
  27605. for (p = &_Clocale; p; p = p->_Next)
  27606. if (memcmp(p->_Name, ns, nl) == 0
  27607. && p->_Name[nl] == '\0')
  27608. return (p);
  27609. /* try here to read in locale from file */
  27610. return (NULL);
  27611. }
  27612.  
  27613.  
  27614. Listing 5 (xsetloc.c)
  27615. /* _Setloc function */
  27616. #include <ctype.h>
  27617. #include <limits.h>
  27618. #include <locale.h>
  27619. #include <stdlib.h>
  27620. #include <string.h>
  27621.  
  27622. /* set category for locale */
  27623. struct lconv *_Setloc(int cat, struct lconv *p)
  27624. {
  27625. switch (cat)
  27626. { /* set a category */
  27627. case LC_COLLATE:
  27628. break;
  27629. case LC_CTYPE:
  27630. _Ctype = p->_Ctype;
  27631. _Tolower = p->_Tolower;
  27632. _Toupper = p->_Toupper;
  27633. break;
  27634.  
  27635. case LC_MONETARY:
  27636. _Locale.currency_symbol = p->currency_symbol;
  27637. _Locale.int_curr_symbol = p->int_curr_symbol;
  27638. _Locale.mon_decimal_point = p->mon_decimal_point;
  27639. _Locale.mon_grouping = p->mon_grouping;
  27640. _Locale.mon_thousands_sep = p->mon_thousands_sep;
  27641. _Locale.negative_sign = p->negative_sign;
  27642. _Locale.positive_sign = p->positive_sign;
  27643. _Locale.frac_digits = p->frac_digits;
  27644. _Locale.int_frac_digits = p->int_frac_digits;
  27645. _Locale.n_cs_precedes = p->n_cs_precedes;
  27646. _Locale.n_sep_by_space = p->n_sep_by_space;
  27647. _Locale.n_sign_posn = p->n_sign_posn;
  27648. _Locale.p_cs_precedes = p->p_cs_precedes;
  27649. _Locale.p_sep_by_space = p->p_sep_by_space;
  27650. _Locale.p_sign_posn = p->p_sign_posn;
  27651. break;
  27652. case LC_NUMERIC:
  27653. _Locale.decimal_point = p->decimal_point[0] != '\0'
  27654. ? p->decimal_point : ".";
  27655. _Locale.grouping = p->grouping;
  27656. _Locale.thousands_sep = p->thousands_sep;
  27657. break;
  27658. case LC_TIME:
  27659. break;
  27660. }
  27661. return (p);
  27662. }
  27663.  
  27664.  
  27665.  
  27666.  
  27667.  
  27668.  
  27669.  
  27670.  
  27671.  
  27672.  
  27673.  
  27674.  
  27675.  
  27676.  
  27677.  
  27678.  
  27679.  
  27680.  
  27681.  
  27682.  
  27683.  
  27684.  
  27685.  
  27686.  
  27687.  
  27688.  
  27689.  
  27690.  
  27691.  
  27692.  
  27693.  
  27694.  
  27695.  
  27696.  
  27697.  
  27698. Doctor C's Pointers(R)
  27699.  
  27700.  
  27701. Data Structures, Part I
  27702.  
  27703.  
  27704.  
  27705.  
  27706. Rex Jaeschke
  27707.  
  27708.  
  27709. Rex Jaeschke is an independent computer consultant, author and seminar leader.
  27710. He participates in both ANSI and ISO C Standards meetings and is the editor of
  27711. The Journal of C Language Translation, a quarterly publication aimed at
  27712. implementors of C language translation tools. Readers are encouraged to submit
  27713. column topics and suggestions to Rex at 2051 Swans Neck Way, Reston, VA 22091
  27714. or via UUCP at uunet!aussie!rex or aussie!rex@uunet.uu.net.
  27715.  
  27716.  
  27717. This month I embark on a new series on data structures. Probably the single
  27718. biggest strength of C is its wealth of basic and derived data types --
  27719. everything from simple scalars and one-dimensional arrays to arrays of
  27720. pointers to functions returning pointers to arrays of objects.
  27721.  
  27722.  
  27723. Introduction
  27724.  
  27725.  
  27726. C is an expensive language to learn. In theory, it should only be pursued
  27727. vigorously if the benefits significantly outweigh the costs. Unless you can
  27728. master the extensive data typing capabilities C provides, you will not be able
  27729. to completely understand or exploit the language. If you don't think you need
  27730. many the data capabilities C provides, you should either use a much safer
  27731. language with less power or learn more about the capabilities C has so you can
  27732. see how you might use them.
  27733. When most people come to a new language, they think in terms of the
  27734. capabilities of their old languages for quite a while. If their old language
  27735. didn't support a given capability (for example, pointers to functions), they
  27736. may not even know about that capability, or have any real idea why they would
  27737. want to use it. So the more educated you can become about C's data structure
  27738. capabilities, the better off you'll be. I hope this series will help.
  27739. I plan to cover the following topics:
  27740. simple arrays and array operations (such as insertion, deletion, and
  27741. searching)
  27742. arrays of pointers versus multi-dimensional arrays
  27743. the use of malloc and friends to extend arrays
  27744. stacks, queues, deques, and linked lists (single, double, circular, and
  27745. lattice)
  27746. recursion
  27747. pointers to functions and tables thereof
  27748. trees
  27749. This is an ambitious agenda, and no doubt there will be digressions along the
  27750. way. However, I will try to adhere to something along these lines.
  27751. If you wish to read a little on your own, I recommend Donald E. Knuth's The
  27752. Art of Computer Programming, Volume 1: Fundamental Algorithms. Addison-Wesley
  27753. ISBN 0-201-03809-9. Chapter 2 (Information Structures) runs some 230 pages and
  27754. is full of useful information on data structures. Although much of this was
  27755. written more than 20 years ago, it is still quite relevant.
  27756.  
  27757.  
  27758. Simple Array Manipulations
  27759.  
  27760.  
  27761. The following, rather large, program defines an array of integers. It allows
  27762. the user to manipulate the array either as a whole or by its individual
  27763. elements. (I'll call them nodes.) The complete set of operations are:
  27764. Add a new node to the end
  27765. Change a user-selected node
  27766. Display a user-selected node
  27767. Insert a new node between nodes or before the first node
  27768. Remove a user-selected node
  27769. Search for first occurrence of a node value
  27770. Search for last occurrence of a node value
  27771. Report the number of nodes
  27772. Sort the nodes in ascending order
  27773. Show all entries in the table
  27774. The array is defined with only four nodes. However, by changing the macro
  27775. MAX_NODES you can make the number anything you choose. (I chose four so I can
  27776. easily fill the array and test the error checking for full arrays, etc.)
  27777. I use scanf to get user-supplied input. If you want robust error recovery on
  27778. invalid input, scanf is difficult (if not impossible) to use. That is a
  27779. separate issue, not directly relevant to the main theme. So I have chosen to
  27780. ignore input validation for the most part. If you want, you can easily supply
  27781. input that will break the program.
  27782. The commands are defined using macros, so you can change the characters used.
  27783. You could also obtain input using a mouse-driven menu. Again, that is
  27784. peripheral to the main topic.
  27785. Throughout the series, I will adapt this program to use different data
  27786. structures behind the scenes. For example, in this first part I use a simple
  27787. automatic one-dimensional array. Next, I use one allocated at runtime via
  27788. malloc. Eventually, in a future installment, I will use a dynamically
  27789. allocated linked list.
  27790. Listing 1 shows a program that allocates memory for an array ary:
  27791. int ary[MAX_NODES];
  27792. It permits the user to perform various operations on the whole array or on
  27793. specific elements (or nodes) within it. They may, for example
  27794. add, remove, or change a node
  27795. display or sort the whole array
  27796.  
  27797. The program is designed as an introductory example of using data structures
  27798. with C.
  27799. I will not discuss the code much because it is clearly laid out and the
  27800. identifier names are self-explanatory. I tested the program by creating a text
  27801. file of commands mixed with various expected and unexpected inputs. I then fed
  27802. that to the program using command-line redirection. This is not only
  27803. convenient for testing, but allows for a crude batch facility to process large
  27804. amounts of production data acquired through other means.
  27805. You might take issue with my use of goto in the cases SEARCH_FNODE and
  27806. SEARCH_LNODE. I find many programmers of the structured programming faith (of
  27807. which I am one) taking their dislike for goto to the extreme. If you think
  27808. goto should never be used, I suggest you think how to build a machine that
  27809. does not have branch or jump instructions. Even Pascal, supposedly the
  27810. ultimate structured language, has a goto.
  27811. The problem with goto is that programmers inevitably write code that branches
  27812. to irresponsible places -- that's why C's break and continue are so nice. The
  27813. compiler works out where to go instead. In any event, I believe my goto
  27814. solution as presented in Listing 1 is more elegant and efficient than any
  27815. structured solution I've seen for the problem. My rule is, "Use goto only when
  27816. absolutely necessary and then only in a forward direction in a local scope."
  27817. End of sermon on goto.
  27818.  
  27819.  
  27820. Dynamically Allocated Arrays
  27821.  
  27822.  
  27823. The first version of the program defined ary as having automatic storage
  27824. duration. This was an arbitrary choice -- it could just as easily have been
  27825. static for my purposes.
  27826. Standard C provides a family of routines to allocate memory at runtime. I will
  27827. refer to this as dynamic memory allocation. (See my column "The Memory
  27828. Management Library" in CUJ Vol. 8, No. 1.) With careful design, you can easily
  27829. change from using an automatic or static object to a dynamic one. The only
  27830. changes you need to make to the program are as follows:
  27831. #include <stdlib.h>
  27832.  
  27833. main()
  27834. {
  27835. int *ary;/* ary is now a pointer, NOT an array */
  27836. ...
  27837. ary = malloc(MAX_NODES * sizeof(int));
  27838. if (ary == NULL) {
  27839. printf ("Cannot allocate memory for ary\n");
  27840. exit(2);
  27841. }
  27842. You need the header stdlib.h to declare the memory allocation functions. Since
  27843. the array does not exist at compile-time, ary is now simply a pointer to an
  27844. int, not an array of int. By allocating the same amount of memory as before,
  27845. but now at runtime, you can make ary point to the first element in the
  27846. allocated array. Where ary was previously the name of an array and was
  27847. converted to &ary[0] in all the right places, it is now the address of the
  27848. first int in the allocated space. In short, ary can be used in exactly the
  27849. same way as before. (This is possible since C permits a pointer expression,
  27850. such as ary, to be arbitrarily subscripted to one level. Because a[i] is
  27851. equivalent to *(a + i), the two subscripted forms are completely
  27852. interchangeable.)
  27853.  
  27854.  
  27855. Variable Size Arrays
  27856.  
  27857.  
  27858. Using malloc is a step in the right direction, but the array is still limited
  27859. to a fixed number of nodes. You can fix this by replacing the constant
  27860. MAX_NODES with a variable max_nodes and using realloc to extend the array if
  27861. it fills. Again, with the proper initial design, the changes needed are small
  27862. and localized. For example:
  27863. int *ptr;
  27864. int max_nodes = 1;
  27865.  
  27866. ary = malloc(max_nodes * sizeof(int));
  27867. if (ary == NULL) {
  27868. printf ("Cannot allocate initial memory"
  27869. "for ary\n");
  27870. exit(2);
  27871. }
  27872. By initializing max_nodes to 1, you start out allocating an array of only one
  27873. element. Of course, if your application always used at least 20, for example,
  27874. this initializer should be changed. There's no point extending the array by
  27875. one node 20 times if you know up front what you're going to do.
  27876. The only other changes are to the cases ADD_NODE and INSERT_NODE. Instead of
  27877. complaining when the table is full, these operations can now extend the array
  27878. by one node, as follows:
  27879. if (nodes_in_use == max_nodes) {
  27880. ptr = realloc(ary,
  27881. (max_nodes + 1) * sizeof(int));
  27882. if (ptr == NULL) {
  27883. printf("Cannot allocate new
  27884. node\n");
  27885. break;
  27886. }
  27887. ary = ptr;
  27888. ++max_nodes;
  27889. }
  27890. Now you have a version that is limited only by the amount of memory available
  27891. at runtime. Note though that realloc might not be able to allocate memory
  27892. contiguously, in which case it has to go the more expensive route of
  27893. allocating a new space, copying the old array contents to it, and freeing the
  27894. old copy. realloc has another limitation. Say, for example, there are 1,000
  27895. bytes of memory available to begin with. Once the allocated array uses about
  27896. 500 of these, there is insufficient memory left for realloc to make a copy
  27897. before it frees the original. While only half of available memory is used,
  27898. realloc might not be able to use the other half.
  27899. realloc lets you extend the array by one node with a very small amount of
  27900. code, but it is probably not efficient over the long run. You might want to
  27901. extend the array by a cluster of nodes (say 5 to 10 at a time). Or you might
  27902. chose to use a linked list instead to avoid the need for realloc to copy any
  27903. of the existing array.
  27904. In my realloc version, I chose not to shrink the array when a node was
  27905. deleted. This was an arbitrary choice. You could argue that it would be good
  27906. housekeeping to do so. You could also argue that by not freeing deleted nodes
  27907. you can maintain a cache of available nodes when the next addition or
  27908. insertion is needed.
  27909.  
  27910.  
  27911. Final Comments
  27912.  
  27913.  
  27914.  
  27915. All examples in this article work and are useful, but they probably have
  27916. inefficiencies. For example, inserting and deleting nodes requires all
  27917. subsequent nodes to be shuffled toward or away from the end, respectively. As
  27918. the node count increases, these operations become increasingly expensive. You
  27919. can avoid this expense by using linked lists. You will see in future
  27920. installments that such lists have their own unique problems.
  27921.  
  27922. Listing 1
  27923. #include <stdio.h>
  27924. #include <ctype.h>
  27925. #include <limits.h>
  27926.  
  27927. #define MAX_NODES 4 /* max number of nodes in array */
  27928.  
  27929. #define ADD_NODE 'A'
  27930. #define CHANGE_NODE 'C'
  27931. #define DISPLAY_NODE 'D'
  27932. #define EXIT 'E'
  27933. #define SEARCH_FNODE 'F'
  27934. #define HELP '?'
  27935. #define INSERT_NODE 'I'
  27936. #define SEARCH_LNODE 'L'
  27937. #define COUNT_NODES 'N'
  27938. #define REMOVE_NODE 'R'
  27939. #define SORT_NODES 'S'
  27940. #define DUMP_TABLE 'T'
  27941.  
  27942. /* ------------------------------------------------------------------- */
  27943.  
  27944. main()
  27945. {
  27946. int ary[OB]MAX_NODES[CB];
  27947.  
  27948. int nodes_in_use = 0;
  27949. int index;
  27950. int temp, i, j;
  27951. int inchar = 'x'; /* force initial prompt */
  27952.  
  27953. while (1) {
  27954. if (inchar != ' ')
  27955. printf("\nEnter Action Code (%c for help): ", HELP);
  27956. switch(toupper(inchar = getchar())) {
  27957.  
  27958. /* ------------------------------------------------------------------- */
  27959.  
  27960. case EXIT:
  27961. goto end; /* break won't do so use goto */
  27962.  
  27963. /* ------------------------------------------------------------------- */
  27964.  
  27965. default:
  27966. printf("\n Invalid command. Please try again\n");
  27967. break;
  27968.  
  27969. /* ------------------------------------------------------------------- */
  27970.  
  27971. case HELP:
  27972. printf("\n The action codes are:\n");
  27973. printf("\t%c - produces this help message\n", HELP);
  27974. printf("\t%c - Add a new node to the end\n", ADD_NODE);
  27975. printf("\t%c - Change a user-selected node\n", CHANGE_NODE);
  27976. printf("\t%c - Display a user-selected node\n", DISPLAY_NODE);
  27977. printf("\t%c - Exit this program\n", EXIT);
  27978. printf("\t%c - Search for first occurrence\n", SEARCH_FNODE);
  27979.  
  27980. printf("\t%c - Insert a new node\n", INSERT_NODE);
  27981. printf("\t%c - Search for last occurrence\n", SEARCH_LNODE);
  27982. printf("\t%c - Report the number of nodes\n", COUNT_NODES);
  27983. printf("\t%c - Remove a user-selected node\n", REMOVE_NODE);
  27984. printf("\t%c - Sort the nodes in ascending order\n", SORT_NODES);
  27985. printf("\t%c - Show all entries in the table\n", DUMP_TABLE);
  27986. break;
  27987.  
  27988. /* ------------------------------------------------------------------- */
  27989.  
  27990. case '\n': /* ignore white space on input */
  27991. case ' ' :
  27992. case '\t':
  27993. case '\v':
  27994. case '\f':
  27995. inchar = ' '; /* indicate white space input */
  27996. break;
  27997.  
  27998. /* ------------------------------------------------------------------- */
  27999.  
  28000. case ADD_NODE:
  28001. if (nodes_in_use == MAX_NODES) {
  28002. printf("\n Table is full\n");
  28003. break;
  28004. }
  28005.  
  28006. printf("\n Enter new node's value: ");
  28007. scanf("%3d", &ary[nodes_in_use]);
  28008. ++nodes_in_use;
  28009. printf("\n Node added");
  28010. break;
  28011.  
  28012. /* ------------------------------------------------------------------- */
  28013.  
  28014. case DUMP_TABLE:
  28015. if (nodes_in_use == 0) {
  28016. printf("\n Table contains no nodes\n");
  28017. break;
  28018. }
  28019.  
  28020. printf("\n Table nodes are as follows:\n");
  28021. for (index = 0; index < nodes_in_use; ++index)
  28022. printf("\tNode %2d =>%3d\n", index ary[index]);
  28023.  
  28024. break;
  28025.  
  28026. /* ------------------------------------------------------------------- */
  28027.  
  28028. case COUNT_NODES:
  28029. printf("\tThere are %2d nodes in the table\n", nodes_in_use);
  28030. break;
  28031.  
  28032. /* ------------------------------------------------------------------- */
  28033.  
  28034. case DISPLAY_NODE:
  28035. if (nodes_in_use == 0) {
  28036. printf("\n Table contains no nodes\n");
  28037. break;
  28038. }
  28039.  
  28040.  
  28041. while (1) {
  28042. printf("\n Enter node number: ");
  28043. scanf("%d", &index);
  28044. if (index >= 0 && index < nodes_in_use)
  28045. break;
  28046.  
  28047. printf("\n No such node (%d) in table\n", index);
  28048. }
  28049.  
  28050. printf("\tNode %2d => %3d\n", index, ary[index]);
  28051. break;
  28052.  
  28053. /* ------------------------------------------------------------------- */
  28054.  
  28055. case CHANGE_NODE:
  28056. if (nodes_in_use == 0) {
  28057. printf("\n Table contains no nodes\n");
  28058. break;
  28059. }
  28060.  
  28061. while (1) {
  28062. printf("\n Enter node number: ");
  28063. scanf("%d", &index);
  28064. if (index < 0 index >= nodes_in_use)
  28065. printf("\n No such node (%d) in table\n", index);
  28066. break;
  28067. }
  28068.  
  28069. printf("\tNode %2d => %3d\n", index, ary[index]);
  28070. printf("\n Enter new value: ");
  28071. scanf("%3d", &ary[index]);
  28072. printf("\n Node changed");
  28073. break;
  28074.  
  28075. /* ------------------------------------------------------------------- */
  28076.  
  28077. case SEARCH_FNODE:
  28078. if (nodes_in_use == 0) {
  28079. printf("\n Table contains no nodes\n");
  28080. break;
  28081. }
  28082.  
  28083. printf("\n Enter search value: ");
  28084. scanf("%d", &temp);
  28085.  
  28086. for (index = 0; index < nodes_in_use; ++index) {
  28087. if (ary[index] == temp) {
  28088. printf("\tValue %3d found in node %3d\n",
  28089. temp, index);
  28090. goto found1;
  28091. }
  28092. }
  28093.  
  28094. printf("\n No such value (%d) in table\n", temp);
  28095. found1:
  28096. break;
  28097.  
  28098. /* ------------------------------------------------------------------- */
  28099.  
  28100.  
  28101. case SEARCH_LNODE:
  28102. if (nodes_in_use == 0) {
  28103. printf("\n Table contains no nodes\n");
  28104. break;
  28105. }
  28106.  
  28107. printf("\n Enter search value: ");
  28108. scanf("%d", &temp);
  28109.  
  28110. for (index = nodes_in_use - 1; index >= 0; --index) {
  28111. if (ary[index] == temp) {
  28112. printf("\tValue %3d found in node %3d\n",
  28113. temp, index);
  28114. goto found2;
  28115. }
  28116. }
  28117.  
  28118. printf("\n No such value (%d) in table\n", temp);
  28119. found2:
  28120. break;
  28121.  
  28122. /* ------------------------------------------------------------------- */
  28123.  
  28124. case SORT_NODES: /* simple bubble sort */
  28125. if (nodes_in_use == 0) {
  28126. printf("\n Table contains no nodes\n");
  28127. break;
  28128. }
  28129.  
  28130. for (i = nodes_in_use - 2; i >= 0; --i) {
  28131. for (j = 0; j <= i; ++j) {
  28132. if (ary[j] > ary[j + 1]) {
  28133. temp = ary[j];
  28134. ary[j] = ary[j + 1];
  28135. ary[j + 1] = temp;
  28136. }
  28137. }
  28138. }
  28139. printf("\n Nodes sorted");
  28140. break;
  28141.  
  28142. /* ------------------------------------------------------------------- */
  28143.  
  28144. case INSERT_NODE:
  28145. if (nodes_in_use == 0) {
  28146. printf("n\ Table contains no nodes\n");
  28147. break;
  28148. }
  28149.  
  28150. if (nodes_in_use == MAX_NODES) {
  28151. printf("\n Table is full\n");
  28152. break;
  28153. }
  28154.  
  28155. while (1) {
  28156. printf("\n Enter number of node to insert before: ");
  28157. scanf("%d", &index);
  28158. if (index >= 0 && index < nodes_in_use)
  28159.  
  28160. break;
  28161.  
  28162. printf("\n No such node (%d) in table\n", index);
  28163. }
  28164.  
  28165. ++nodes_in_use;
  28166. for (i = nodes_in_use - 1; i > index; --i) {
  28167. ary[i] = ary[i - 1];
  28168. }
  28169. printf("\n Enter new node's value: ");
  28170. scanf("%3d", &ary[index]);
  28171. printf("\n Node inserted");
  28172. break;
  28173.  
  28174. /* ------------------------------------------------------------------- */
  28175.  
  28176. case REMOVE_NODE:
  28177. if (nodes_in_use == 0) {
  28178. printf("\n Table contains no nodes\n");
  28179. break;
  28180. }
  28181.  
  28182. while (1) {
  28183. printf("\n Enter node number: ");
  28184. scanf("%d", &index);
  28185. if (index >= 0 && index < nodes_in_use)
  28186. break;
  28187.  
  28188. printf("\n No such node (%d) in table\n", index);
  28189. }
  28190.  
  28191. for (i = index; i < nodes_in_use; ++i) {
  28192. ary[i] = ary[i + 1];
  28193. }
  28194. --nodes_in_use;
  28195. printf("\n Node removed");
  28196. break;
  28197.  
  28198. /* ------------------------------------------------------------------- */
  28199.  
  28200. }
  28201. }
  28202. end: return (0);
  28203. }
  28204.  
  28205. /* ------------------------------------------------------------------- */
  28206.  
  28207.  
  28208.  
  28209.  
  28210.  
  28211.  
  28212.  
  28213.  
  28214.  
  28215.  
  28216.  
  28217.  
  28218.  
  28219.  
  28220.  
  28221.  
  28222.  
  28223. Questions and Answers
  28224.  
  28225.  
  28226. More Pointer Problems
  28227.  
  28228.  
  28229.  
  28230.  
  28231. Ken Pugh
  28232.  
  28233.  
  28234. Kenneth Pugh, a principal in Pugh-Killeen Associates, teaches C language
  28235. courses for corporations. He is the author of C Language for Programmers and
  28236. All On C, and was a member on the ANSI C committee. He also does custom C
  28237. programming for communications, graphics, image databases, and hypertext. His
  28238. address is 4201 University Dr., Suite 102, Durham, NC 27707. You may fax
  28239. questions for Ken to (919) 493-4390. When you hear the answering message,
  28240. press the * button on your telephone. Ken also receives email at
  28241. kpugh@dukemvs.ac.duke.edu (Internet).
  28242.  
  28243.  
  28244. Q
  28245. I have a wall full of technical manuals, none of which even begins to discuss
  28246. the OBJ file format. Can you tell me where I can get information on OBJ file
  28247. internals?
  28248. Dennis Taylor 
  28249. Vancouver, BC
  28250. CANADA
  28251. A
  28252. You did not say which system you are working on. The Intel Relocatable Object
  28253. Module Format is in the back of the Microsoft MS-DOS Programmer's Reference
  28254. Manual. The date on this edition is 1984. Perhaps they have removed it from
  28255. the later editions.
  28256. Q
  28257. I hope you can help me. I have a problem compiling a certain C source code
  28258. (Listing 2) with Microsoft Quick C.
  28259. The file is DSAT.C, a dynamic string array test program. It consists of three
  28260. functions plus a main to coordinate them. The first, wordcount, is flawless.
  28261. The second, str_to_ptrarray, does not work the way I want it to, and I do not
  28262. know why.
  28263. The str_to_ptrarray function should break the passed string into words
  28264. (characters separated with a space) and compose a null-terminated string array
  28265. of the words in the pointer array supplied. The function returns the number of
  28266. words.
  28267. Hence,
  28268. int nwords;
  28269. char mystr[ ] = "This is a test.",
  28270. **strlist;
  28271. nwords = str_to_ptrarray(mystr,
  28272. &strlist);
  28273. should return the values with results as in Listing 1.
  28274. The function allocates all space necessary for the transformation and I want
  28275. to keep it that way! I do not want to have to calculate memory usage each time
  28276. I call the function separately -- I want the function to handle it.
  28277. Inside the function, it works perfectly with QuickC 2.00 (as you can see with
  28278. my four lines of debugging), but outside the function (back in main, for
  28279. example) the work has gone poof. I get the first element along with a null
  28280. pointer assignment runtime error (R6001). With QuickC 2.51, it does not seem
  28281. to do anything, and the compiler gives me a Near Pointer Error.
  28282. Then free_ptrarray simply frees the memory allocated by the str_to_ptrarray
  28283. function.
  28284. I have looked at this for too long. I need your help. Why does this not work?
  28285. Am I missing a fundamental rule of C programming? Please please please help!
  28286. Your time is profoundly appreciated. Thank you.
  28287. Anthony Whitford
  28288. Sidney, BC
  28289. CANADA
  28290. A
  28291. Ah, triple pointers -- more than twice as bad as double pointers. I have to
  28292. admit, it took me a few minutes to figure out what was wrong with your
  28293. program. I got slightly different errors the first time I ran it, but that was
  28294. due to mistyping. For future reference, if you or another reader has a
  28295. problem, please send the code on a 5.25-inch MS-DOS disk.
  28296. Your problem revolves around the precedence of the index operator [ ] and the
  28297. indirection operator *. The former has higher precedence.
  28298. *ptrarray [count]
  28299. is evaluated as:
  28300. * (ptrarray [count])
  28301. or equivalently as:
  28302. * (* (ptrarray + count))
  28303. This adds the value of count to ptrarray before doing the indirection.
  28304. What you want is:
  28305. (*ptrarray) [count]
  28306. which is equivalent to:
  28307. * ((*ptrarray) + count)
  28308. Simply replace all instances of *ptrarray[x] with (*ptrarray)[x], and your
  28309. problems should be solved.
  28310. The lines below /* THIS IS A NEW PRINTF */ clarify the difference. Run your
  28311. program again and see what values are printed out. (If you use the large
  28312. memory model, use "%lx".)
  28313. Two brief comments on the code. First, I dislike dealing with anything more
  28314. than double pointers. You can eliminate the use of the double pointer (except
  28315. for the actual assignment to the parameter) by having a local variable called
  28316. local_ptr_array and declared as:
  28317. char **local_ptr_array;
  28318. The code would read like:
  28319.  
  28320. if ((local_ptr_array =
  28321. (char **)calloc(words + 1,...)))
  28322. . . .
  28323. local_ptr_array[index] = . . .
  28324. At the end, simply code:
  28325. /* Set the address in the parameter */
  28326. *ptr_array = local_ptr_array;
  28327. This would have eliminated your problems, because no indirection operator
  28328. would have been involved.
  28329. Second, I would replace the calls to calloc as:
  28330. (*ptrarray) [index] =
  28331. (char *)calloc((size_t)(strlen(cptr) + 1),
  28332. sizeof (**ptrarray [index] ) );
  28333. with:
  28334. (*ptrarray) [index] =
  28335. (char *)calloc((size_t)(strlen(cptr) + 1),
  28336. sizeof(char));
  28337. Since you cast the return to char *, the size of the elements should be the
  28338. size of a char. Although your approach is technically correct, it appears
  28339. slightly less readable.
  28340. Q
  28341. I need to be able to execute a program (Listing 3) in UNIX, and no matter what
  28342. the return value for that program is, I need to return zero (success). This is
  28343. because my menu system beeps and displays unwanted messages when the user hits
  28344. the interrupt key to abort the program.
  28345. My solution is to write a shell program that performs the following:
  28346. 1) fork a process
  28347. 2) if parent then
  28348. 2.1. ignore interrupt
  28349. 2.2. wait for child to finish
  28350. 3) if child then
  28351. 3.1. execute the program passed as an argument
  28352. 4) return zero
  28353. The problem is that if the interrupt key is pressed, then the program returns
  28354. 2000 instead of zero. If the program ends normally, then the program returns
  28355. zero (fine). Also, if I instruct the program to return any value other than
  28356. zero, it does so even if the interrupt key is pressed (as it should).
  28357. Tim Riley
  28358. Miami Lakes, FL
  28359. A
  28360. Good question. I tried it on my UNIX machine (a Sequent) and got exactly the
  28361. same results as you did. It looks OK to me (either that or I'm tired out after
  28362. considering triple pointers). Any thoughts out there? (KP)
  28363.  
  28364.  
  28365. Reader's Replies
  28366.  
  28367.  
  28368.  
  28369.  
  28370. The Function Prototype
  28371.  
  28372.  
  28373. I write regarding Firdaus Irani's Q?/A! question about his problems with the
  28374. compare function for qsort in Turbo C++ (which you duplicated in Microsoft C).
  28375. I may have missed something, but my understanding is that qsort is defined as
  28376. accepting a pointer to a function that returns an int and takes two const void
  28377. * parameters. Thus, when Mr. Irani defined his function as taking two unsigned
  28378. char ** parameters, there was a mismatch. I have used the qsort function under
  28379. Turbo C, Turbo C++, Microsoft C 5.1 and 6.0, and IBM C/400 (on the AS/400),
  28380. and the implementation has always been as described. Further, the
  28381. documentation with both Turbo C(++) and Microsoft C state that qsort is
  28382. defined in ANSI C.
  28383. To use qsort as Mr. Irani desires, he should declare his function as:
  28384. int comp (const void *, const void *);
  28385. and call qsort just as he did. His comp function should be as follows:
  28386. int comp (void const *a, void const *b)
  28387. {
  28388. return (strcmp(*((unsigned char **) a),
  28389. *((unsigned char **) b));
  28390. }
  28391. The casts take care of the warnings on the parameters to the strcmp function.
  28392. The important thing to note here is that the const void * parameters to the
  28393. compare function of qsort are so that any type of item can be compared. For
  28394. instance, in a utility I have written, I have a structure I use for building a
  28395. typical directory tree, defined as follows:
  28396. typedef struct
  28397. {
  28398. char achPathName[MAXPATH];
  28399. //MAXPATH is defined in the C header files
  28400. char achPathPic[MAXPATH];
  28401. unsigned short usPath Level;
  28402.  
  28403. } DIR_INFO;
  28404. This structure is used to hold a directory path name (such as C: \TC\INCLUDE),
  28405. a path "picture" that corresponds to the path name (such as -- --INCLUDE), and
  28406. the level of the directory, the root being 0. An array of these structures is
  28407. declared as:
  28408. DIR_INFO stDirInfo[MAX_DIRS];
  28409. //MAX_DIRS is a #define
  28410. After filling this array using findfirst and findnext, I want to sort it
  28411. alphabetically (yet maintain the proper levels). To do this I declare a
  28412. compare function for qsort:
  28413. int DirSort(const void *, const void *);
  28414. I then call qsort with:
  28415. qsort(stDirInfo, usCurNumDirs,
  28416. sizeof(stDirInfo[0]), DirSort);
  28417. //usCurNumDirs = # of directories found
  28418. The DirSort function is shown in Listing 4.
  28419. Using the (DIR_INFO *) cast, I can sort the array of structures based on a
  28420. structure member.
  28421. Again, I may have missed something, but I thought the usage was
  28422. straightforward. Despite PJP's note to the contrary, I certainly don't see how
  28423. this is a bug in Turbo C++, especially if this is the ANSI definition for the
  28424. qsort function.
  28425. A. Donnie Hale, Jr.
  28426. Hilliard, OH
  28427. Regarding the TC++ question from Firdaus Irani, I have come across the same
  28428. question and solved it in Listing 5. As I think about it, I think that TC++ is
  28429. correct, because as I understand C++ will not automatically cast a void
  28430. pointer, it must be done explicitly. The following works correctly and does
  28431. not require changes to the stdlib.h.
  28432. I greatly appreciate your column, many of the questions hit close to home.
  28433. Jay Holovacs
  28434. Warren, NJ
  28435. In regard to a question from Firdaus Irani in the December 1990 issue of the C
  28436. User's Journal, page 92, using qsort.
  28437. The problem is not in the Turbo C++, but in Firdaus Irani's usage of the
  28438. function. First, any compare function always has the prototype:
  28439. int compare(const void *,
  28440. const void *);
  28441. The arguments to the compare function are then cast to the appropriate type
  28442. within the user-defined compare function.
  28443. My example (see Listing 6) has three different sort functions. I read in a
  28444. list of student names and their corresponding grades. The three data
  28445. structures are:
  28446. n array of a structure containing the students' names and grades,
  28447. n array of the students' names, and
  28448. n array of the students' grades.
  28449. Each structure is sorted (the array of structure is sorted by its grade entry)
  28450. and the results were printed.
  28451. Input file used:
  28452.  
  28453. Martin 3.5
  28454. Sheila 4.0
  28455. Marcel 2.7
  28456. Henry 2.9
  28457. Kemberly 3.8
  28458. Cindy 1.7
  28459. The resulting output file (sorted structure, sorted names, sorted grades):
  28460. Cindy 1.70 Cindy 1.70
  28461. Marcel 2.70 Henry 2.70
  28462. Henry 2.90 Kimberly 2.90
  28463. Martin 3.50 Marcel 3.50
  28464. Kimberly 3.80 Martin 3.80
  28465. Sheila 4.00 Sheila 4.00
  28466. The user must cast the argument type within the compare function (see the
  28467. compare function definitions). I do not think that there is a bug in Turbo
  28468. C++'s qsort.
  28469. Martin Schlapfer
  28470. Scotts Valley, CA
  28471. I have enclosed a question and answer from your December 1990 C Users Journal
  28472. column. I think both you and P.J. Plauger missed the boat on this one.
  28473. F. Irani asks why he gets an error when attempting to use qsort. He changed
  28474. the prototype for qsort in stdlib.h to get rid of the error message (!) and
  28475. asks "Does that mean I have to make changes to stdlib every time I try to
  28476. compile a program using qsort that calls a compare function with different
  28477. parameter types?". He says he got no help from Borland tech support. I don't
  28478. think he got any from you or PJP either.
  28479. I think you should have answered him like this:
  28480. Don't change your stdlib.h. The prototype for qsort indicates what qsort
  28481. expects from you, and fooling the compiler is not the answer. The answer is to
  28482. give qsort just what it expects: a compare function that is defined to receive
  28483. void * pointers. If you are actually comparing unsigned char **, then you must
  28484. use a cast inside your compare function.
  28485. For example your comp should have been:
  28486. int comp(const void *a,
  28487. const void *b)
  28488. {
  28489. return strcmp((char *)*(unsigned
  28490. char **)a, (char *)*(unsigned
  28491. char **)b);
  28492. }
  28493.  
  28494. Note the casts to char *. This is because strcmp is defined to expect char *,
  28495. even though it is guaranteed to compare the strings as if they are unsigned
  28496. characters. (See sec. 4.11.4 in the ANSI standard.)
  28497. Now, aside from the annoying error messages, why is it wrong to define the
  28498. compare function to accept something other than void * pointers?
  28499. When you call a function, you must pass it what it expects. Likewise, when a
  28500. library function calls your function, your function must expect what the
  28501. library function passes. There is nothing in the standard to prevent an
  28502. implementation from having different representations for different types of
  28503. pointers (except that void * pointers must have the same representation and
  28504. alignment requirements as char * pointers, according to sec. 3.1.2.5). If a
  28505. function call passes void * and the function is defined to expect unsigned
  28506. char **, anything can happen. This is potentially just as dangerous as passing
  28507. an int to a function expecting a long. The function picks up what it expects
  28508. from the stack (or registers), and it had better be right. The whole purpose
  28509. of the prototype system is to protect against mistakes like these, and
  28510. arbitrarily changing a prototype to get rid of an error or warning message is
  28511. like turning off an alarm system because it keeps going off. Better to find
  28512. the problem, understand it, and fix it.
  28513. P.S. It occurs to me that some of the misunderstanding may be due to a
  28514. perception that, since void * pointers may be arbitrarily assigned to and from
  28515. other pointer types without casts, that they may also be passed to a function
  28516. expecting a different pointer type, or that a function defined to accept a
  28517. void * may be passed a different type. This is wrong. The compiler "knows" how
  28518. to deal with assignments and can do the necessary casting automatically. The
  28519. compiler can automatically cast a pointer when it is passed, if a prototype is
  28520. in scope. But in the case of qsort, the calling is being done "blindly" by
  28521. qsort, which has no knowledge of what it is really dealing with. So it passes
  28522. void * pointers. There is no way for the compiler to automatically convert
  28523. these on the "receiving end."
  28524. Raymond Gardner
  28525. Englewood, CO
  28526. You are right that there are some misconceptions with pointers to void. C++
  28527. has tightened the usage of void pointers, so that assignment requires a cast,
  28528. but Standard C permits assignments without casts. For the benefit of our
  28529. readers, let's examine the uses for void pointers. The major area is in places
  28530. where char * was employed before. For example, the prototype for memset is:
  28531. memset(void *address, int byte,
  28532. size_t length);
  28533. We can pass memset something like:
  28534. struct s_test
  28535. {
  28536. int member;
  28537. . . .
  28538. };
  28539. struct s_test test;
  28540. memset(&test, 0, sizeof(test));
  28541. You do not have to cast something like this:
  28542. memset( (void *) &test, 0,
  28543. sizeof(test));
  28544. because void * is assignment-compatible with any other pointer.
  28545. Now let's take the case of qsort. Suppose you want to compare structures of
  28546. s_test type in a function that would be passed to qsort. Your function must be
  28547. prototyped as
  28548. compare_test_struct(const void *one,
  28549. const void *two);
  28550. to meet the _fcmp(const void *, const void *) requirement in the qsort
  28551. prototype.
  28552. The function would need to look like:
  28553. compare_test_struct(const void *one,
  28554. const void *two)
  28555. {
  28556. if ( ((struct s_test *) one) ->
  28557. member > ((struct s_test *)
  28558. two) -> member)
  28559. return 1;
  28560. . . .
  28561. }
  28562. or
  28563. compare_test_struct(const void *one,
  28564. const void *two)
  28565. {
  28566. struct s_test *one_test = one;
  28567. struct s_test *two_test = two
  28568. if ( one_test->member >
  28569. two_test -> member)
  28570. return 1;
  28571. . . .
  28572. }
  28573. Either way appears to make more hidden the real purpose of the function - to
  28574. compare two structures. The parameters one and two will have to contain valid
  28575. beginning addresses for structures. Of course, qsort is designed to supply
  28576. valid ones.
  28577. Note that you can call qsort with any function that expects two void pointers
  28578. and the prototype will not complain. With the above and your example function,
  28579. you could code:
  28580. struct s_test struct_array[10] =
  28581. { /* Some list */... };
  28582. qsort (struct_array, 10,
  28583. sizeof(struct s_test), comp);
  28584. or
  28585. char *array_of_strings[10];
  28586. qsort(array_of_strings, 10,
  28587. sizeof(char *), compare_test_struct);
  28588. These are both equally valid prototype-wise, but absolutely wrong. You may get
  28589. a storage access error in the latter case, if the variable alignment is not
  28590. proper. The prototype for qsort in this case has not protected you from
  28591. shooting yourself in the foot.
  28592. The compare functions as coded above are not useful for general usage, as they
  28593. do not expect pointers to structures of s_test type. You could do the same
  28594. trick as you did for strcmp. That is, you could code the functions as:
  28595. compare_test_struct_for_qsort(const void *one,
  28596.  
  28597. const void *two)
  28598. {
  28599. return compare_test_struct_normal(
  28600. (struct s_test *) one,
  28601. (struct s_test *) two);
  28602. }
  28603.  
  28604. compare_test_struct_normal(struct s_test *one,
  28605. struct s_test *two)
  28606. {
  28607. if ( one->member > two->member)
  28608. return 1;
  28609. . . .
  28610. }
  28611. Then if you wanted to use the comparison function in a normal mode, you would
  28612. code:
  28613. struct s_test test_a, test_b;
  28614.  
  28615. compare_test_struct_normal (&test_a, &test_b);
  28616. And if you wanted to call qsort, you would use:
  28617. qsort(struct_array, 10, sizeof(struct s_test),
  28618. compare_test_struct_for_qsort);
  28619. However, the overhead of a double function call will cut down the efficiency
  28620. of the sort.
  28621. Since it appears that using a prototype of the form:
  28622. int strcmp(const void *, const void *)
  28623. where qsort is called will eliminate the prototype error, I would opt for that
  28624. solution to this tiny incongruity in the prototype system. (KP)
  28625. [All of these responses are on the money. They also cast useful light on a
  28626. notoriously thorny topic. Tom Plum forced X3J11 to consider the problems
  28627. surrounding the prototype for qsort at some length. We never did resolve the
  28628. issue to his satisfaction.
  28629. I can't determine whether my response was incorrect. My back issues of CUJ are
  28630. half a world away. I recall responding to a specific diagnostic that Mr. Irani
  28631. showed. I have caught the C part of Turbo C++ out in this area, and the Gnu C
  28632. compiler as well. It was easy for me to believe that Turbo C could botch type
  28633. checking involving void pointer arguments. Whether or not it does, these
  28634. letters address the basic issue much better than I did. Thanks. (PJP)]
  28635.  
  28636.  
  28637. Automatic Buffers
  28638.  
  28639.  
  28640. I'm writing about the sticky automatic buffer that was the subject of Doug
  28641. Oliver's 9/90 CUG and John Brand's 1/91 CUG letters - used in the function
  28642. repeat_format. This function returns a pointer to local storage that was not
  28643. guaranteed to be valid. I was hoping someone else would point out the reason
  28644. why this technique should never be used, but no one did. Hence this letter.
  28645. Using this technique will introduce some of the hardest-to-find bugs you'll
  28646. ever see. The reason automatic storage isn't guaranteed valid is because an
  28647. interrupt service routine (ISR) occurring at precisely the right moment will
  28648. trash the buffer.
  28649. One of the reasons the bugs will be hard to find is that standard PC
  28650. interrupts (e.g., the clock) seem to switch to their own stacks after using
  28651. only 10 bytes or so of the user stack. Other drivers (e.g., an ethernet drive)
  28652. may or may not use a local stack. Depending on the PC's hardware
  28653. configuration, the buffer may be trashed seemingly at random with a
  28654. probability proportional to the frequency of interrupts and the length of the
  28655. delay before the user function copies the buffer to "safe" storage. Thus, the
  28656. program may always work just fine on the author's machine but may develop
  28657. annoying random quirks after it is distributed.
  28658. Some CPUs (e.g., 68030) have a separate stack for interrupts and are not
  28659. subject to this problem. However, the operating system may reclaim the stack
  28660. space for use elsewhere, causing sporadic memory protection faults.
  28661. In any case, this technique is non-portable. I suggest that it be replaced
  28662. with a call to malloc or strdup.
  28663. As long as I'm writing, I'll throw in my contribution to the external variable
  28664. declaration dialog (Andreas Lang, Bill Sharar II, David Hanson, Larry
  28665. Leonard). Listing 7 shows a technique I've used for years.
  28666. This technique is similar to Mr. Leonard's solution. Your initialization of:
  28667. array[15] INIT(
  28668. 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15} );
  28669. doesn't work in my system (And I'm assuming others. Did you compile your
  28670. example?) because the preprocessor does not honor the braces around the
  28671. aggregate initializer in the macro instantiation, instead assuming that the
  28672. commas separate actual arguments. Compound data objects can be declared as w
  28673. is in my example or using a variation of the technique you presented as
  28674. Listing 2 (1/91 CUG).
  28675. Don Drantz
  28676. Eden Prairie, MN
  28677. I agree with you on the use of automatic storage. I prefer for the user to
  28678. pass the address of a buffer that would be filled in with the appropriate
  28679. information. Using malloc inside a function without a warning that memory
  28680. might not be available is not a wise idea. Of course, one could return the
  28681. address of a static buffer (even some library routines do it), but that makes
  28682. multi-user libraries not feasible.
  28683. I have to admit I didn't compile my example. It was so ugly that I didn't have
  28684. the heart to send it to the compiler. (KP)
  28685.  
  28686. Listing 1
  28687. nwords is equal to 4,
  28688. mystr is not changed "This is a test.",
  28689. *strlist[0] points to "This",
  28690. *strlist[1] points to "is",
  28691. *strlist[2] points to "a",
  28692. *strlist[3] points to "test.",
  28693. *strlist[4] points to NULL.
  28694.  
  28695.  
  28696. Listing 2
  28697. /* ***************************************************
  28698.  
  28699. DSAT.C Dynamic String Array Test
  28700. Version 1.0
  28701. Created by: Anthony Whitford
  28702. (c) 1990
  28703. *********************************************** */
  28704.  
  28705. #include <process.h>
  28706. #include <stdio.h>
  28707. #include <stdlib.h>
  28708. #include <string.h>
  28709.  
  28710. typedef char ** ptrarray_t;
  28711.  
  28712. int wordcount (const char *);
  28713. int str_to_ptrarray (const char *, char ***);
  28714. void free_ptrarray (char ***);
  28715.  
  28716. void main (void)
  28717. {
  28718. int numofitems, retval;
  28719. ptrarray_t strlist;
  28720. if ((retval = str_to_ptrarray("This is a test.", &strlist)) == -1
  28721. printf("Error turning the string into a pointer list.");
  28722. else
  28723. {
  28724. /* Test the data. */
  28725. printf("\nString list outside function...\n");
  28726. for (numofitems = 0; strlist[numofitems] !=NULL; numofitems++)
  28727. {
  28728. printf("%u --> ***%s***\n",
  28729. numofitems, strlist[numofitems]);
  28730. }
  28731. puts("");
  28732. free_ptrarray(&strlist); /* Free pointer array. */
  28733. }
  28734. exit (retval); /* Return number of words or error. */
  28735. }
  28736.  
  28737. /* *******************************************************
  28738. Function: wordcount
  28739. Parameters: const char *str -> the string.
  28740. Description: Counts the number of words in a string p to
  28741. 255 bytes.
  28742. Returns: The number of words in astring.
  28743. ****************************************************** */
  28744.  
  28745. int wordcount (const char *str)
  28746. {
  28747. int words = 1; /*Initialize word cunt. */
  28748. char tstr[256]; /*Declare a temporary string. */
  28749. strcpy(tstr, str); /* Make a working copy. */
  28750. if (strtok(tstr, "") == NULL) /* If string is only */
  28751. return (0); /* spaces, return 0. */
  28752. while (strtok(NULL, " ") !=NULL)
  28753. words++; /* Count how many words. */
  28754. return (words); /* Return number of words in string. */
  28755. }
  28756.  
  28757. /* ************************************************************
  28758.  
  28759. Function: str_to_ptrarray
  28760. Parameters: const char *orgstr -> the original string
  28761. to be parsed,
  28762. char ***ptrarray -> the address of the
  28763. pointer array.
  28764. Description: Breaks a string into its words and composes
  28765. a pointer array of the words.
  28766. Returns: "ptrarray" becomes a NULL terminating pointer
  28767. array, and pointer array, or -1 if an error
  28768. occurred.
  28769. ********************************************************* */
  28770.  
  28771. int str_to_ptrarray (const char *orgstr, char ***ptrarray)
  28772. {
  28773. unsigned index = 0, words;
  28774. char *cptr,*thestr;
  28775. if (( thestr = (char *) calloc((size_t)(strlen(orgstr) + 1),
  28776. sizeof(*thestr))) == NULL)
  28777. return (-1); /* Make a working copy of the
  28778. string. */
  28779. strcpy(thestr, orgstr);
  28780. words = wordcount(thestr); /* Get number of words. */
  28781. if ((*ptrarray = (char **) calloc(words + 1,
  28782. (size_t) sizeof(**ptrarray))) ==NULL)
  28783. {
  28784. free(thestr);
  28785. return (-1); /* Error allocating space for
  28786. ptrarray.*/
  28787. }
  28788.  
  28789. /* THIS IS A NEW PRINTF */
  28790. for (index = 0; index < words; index ++)
  28791. {
  28792. printf("\n & *ptrarray[index] %x & (*ptrarray)[index] %x",
  28793. ptrarray[index], &((*ptrarray)[index]));
  28794. }
  28795. index = 0;
  28796.  
  28797. cptr = strtok(thestr, "");
  28798. while (index < words)
  28799. {
  28800. if ((*ptrarray[index] = (char *)calloc((size_t)(strlen(cptr) + 1),
  28801. sizeof(**ptrarray[index]))) == NULL)
  28802. {
  28803. free_ptrarray(ptrarray);
  28804. free(thestr); /* If memory allocation
  28805. error, free */
  28806. return (-1); /* thestr, ptrarray, and
  28807. return -1. */
  28808. }
  28809. strcpy(*ptrarray[index++], cptr);
  28810. cptr = strtok (NULL, " ");
  28811. }
  28812. *ptrarray[index] = NULL; /* Terminate ptrarray with a NULL. */
  28813. free(thestr); /* Free allocated memory. */
  28814. /* Test the data. --- Delete the next four lines when
  28815. debugged!! */
  28816. printf("\nString list inside function...\n");
  28817. for (index = 0; *ptrarray[index] != NULL; index++)
  28818.  
  28819. printf("%u --> ***%s***\n", index, *ptrarray[index]);
  28820. puts(" ");
  28821.  
  28822. return (words); /* Return the number of pointers. */
  28823. }
  28824.  
  28825. /* **************************************************************
  28826. Function: free_ptrarray
  28827. Parameters: char ***ptrarray _> address of the pointer
  28828. array.
  28829. Description: Frees allocated memory for the pointer array
  28830. allocated with the str_to_ptrarray function.
  28831. Returns: Nothing.
  28832. ************************************************************ */
  28833. void free_ptrarray (char ***ptrarray)
  28834. {
  28835. unsigned count = 0; /* Declare and reset counter. */
  28836. for (; *ptrarray[count] != NULL; count++)
  28837. free(*ptrarray[count]); /* Free each string/word. */
  28838. free(*ptrarray); /* Free the handle. */
  28839. }
  28840.  
  28841.  
  28842. Listing 3
  28843. /* all_true.c */
  28844. /* -----------*/
  28845. */
  28846. No matter what code the calling program returns, this
  28847. program will always return 0 (success).
  28848.  
  28849. Usage: all_true program args
  28850. Example: all_true sleep 5
  28851.  
  28852. */
  28853.  
  28854. #include <stdio.h>
  28855. #include <signal.h>
  28856.  
  28857. #define RET_VALUE 0
  28858.  
  28859. main(argc,argv)
  28860. int argc;
  28861. char **argv;
  28862. {
  28863. process(argc,argv);
  28864. exit(RET_VALUE);
  28865. }
  28866.  
  28867. process(argc,argv)
  28868. int argc;
  28869. char **argv;
  28870. {
  28871. int pid;
  28872.  
  28873. if ((pid = fork()) == -1)
  28874. {
  28875. perror("all_true");
  28876. exit(l);
  28877. }
  28878.  
  28879.  
  28880. if (pid > 0 )
  28881. {
  28882. signal(SIGINT,SIG_IGN); /* Ignore interrupt key */
  28883. while (wait( (int *) 0 ) == pid);
  28884. return;
  28885. }
  28886.  
  28887. signal(SIGINT,SIG_DFL); /* Default interrupt key */
  28888. argv++; /* Point to program argument */
  28889. execvp(*argv, argv);
  28890. perror("all_true");
  28891. }
  28892.  
  28893.  
  28894. Listing 4
  28895. int DirSort(const void *pItem1, const void *pItem2)
  28896. {
  28897. CHAR ach1[MAXPATH], ach2[MAXPATH];
  28898. register CHAR *psz;
  28899.  
  28900. /*
  28901. * Copy the path names to buffers
  28902. */
  28903. strcpy(ach1,((DIR_INFO *)pItem1)->achPathName);
  28904. strcpy(ach2,((DIR_INFO *)pItem2)->achPathName);
  28905.  
  28906. /*
  28907. * Convert all backslashes (\) to hex 01s. Since the
  28908. * backslash falls in the middle of the valid ASCII
  28909. * codes for directory names, it could (and I have
  28910. * seen it) cause the strcmp() to wrongly compare two
  28911. * directories when they are different levels.
  28912. */
  28913. for (psz = ach1; *psz; psz++)
  28914. if (*pse == '\\')
  28915. *psz = 0x01;
  28916. for (psz = ach2; *psz; psz++)
  28917. if (*psz == '\\')
  28918. *psz = 0x01;
  28919.  
  28920. return(strcmp(ach1,ach2));
  28921.  
  28922. }
  28923.  
  28924.  
  28925. Listing 5
  28926. /* from cuj */
  28927. #include <stdio.h>
  28928. #include <stdlib.h>
  28929. #include <string.h>
  28930. int comp(const void *, const void *);
  28931. unsigned char *list[] = {"cat", "car", "cab", "cap", "can"};
  28932.  
  28933. main()
  28934.  
  28935. {
  28936. int x;
  28937. qsort(list, 5, sizeof(unsigned char *), comp);
  28938.  
  28939. for (x = 0; x < 5; x++)
  28940. printf("%s\n", list[x]);
  28941. return 0;
  28942. }
  28943.  
  28944. int comp(const void *a, const void *b)
  28945. {
  28946. return strcmp( *(char **)a, *(char **)b);
  28947. }
  28948.  
  28949.  
  28950. Listing 6
  28951. // PROGRAM LISTING
  28952.  
  28953. #include <stdlib.h> // necessary include files
  28954. #include <stdio.h>
  28955. #include <string.h>
  28956.  
  28957. // *** DEFINE DATA STRUCTURES
  28958. struct studentEntry
  28959. {
  28960. char name[40];
  28961. float grade;
  28962. } student [100]; // student is an array of studentEntry elements
  28963.  
  28964. char name[100][40]; //name is an array of 40-character elements
  28965. float grade[100]; //grade is an array of float elements
  28966.  
  28967. // *** PROTOTYPE QSORT() COMPARE FUNCTIONS FOR EACH DATA TYPE
  28968.  
  28969. int nameComp(const void *, const void *);
  28970. int gradeComp(const void *, const void *);
  28971. int structComp(const void *, const void *);
  28972.  
  28973. // *** BEGIN MAIN PROGRAM
  28974.  
  28975. int main( int argc, char *argv[] )
  28976. {
  28977. // read student file (however it must be done)
  28978. FILE *studentFile;
  28979. if(!(studentFile=fopen(argv[1],"rt")))
  28980. {
  28981. printf("error opening input student file\n");
  28982. exit(1);
  28983. }
  28984.  
  28985. // read studentFile
  28986. char line[100];
  28987. char tmpName[40];
  28988. float tmpGrade;
  28989. for( int i=0; fgets(line, 100, studentFile); i++ )
  28990. {
  28991. sscanf(line,"%s %f", tmpName, &tmpGrade);
  28992. grade[i]=student[i].grade=tmpGrade;
  28993. strcpy(name[i],strcpy(student[i].name,tmpName));
  28994. }
  28995.  
  28996. fclose(studentFile);
  28997.  
  28998.  
  28999. // *** SORT each list using its sort function
  29000. qsort((void *)student,i,sizeof(studentEntry),structComp);
  29001. qsort((void *)name,i,sizeof(char)*40,nameComp);
  29002. qsort((void *)grade,i,sizeof(float),gradeComp);
  29003.  
  29004. // print results
  29005. for( int j=0; j<i; j++ )
  29006. printf ("%20s %6.2f %20s %6.2f\n",
  29007. student[j].name,student[j].grade,name[j],grade[j]);
  29008. }
  29009.  
  29010. // *** COMPARE FUNCTION DEFINITIONS
  29011. // *** Notice the header. In the main body of the function the
  29012. // *** arguments are cast to what they represent. The user must
  29013. // *** do this1
  29014.  
  29015. int name Comp(const void *a, const void *b)
  29016. {
  29017. return strcmp((char *)a,(char *)b);
  29018. }
  29019.  
  29020. int gradeComp(const void *a, const void *b)
  29021. {
  29022. if(*((float *)a)<*((float *)b)) return -1;
  29023. if(*((float *)a)>*((float *)b)) return 1;
  29024. else return 0;
  29025. }
  29026.  
  29027. int structComp(const void *a, const void *b)
  29028. {
  29029. if(((student Entry *)a>->grade<((studentEntry *)b)->grade)
  29030. return -1;
  29031. if(((studentEntry *)a)->grade>((studentEntry *)b)->grade)
  29032. return 1;
  29033. return 0;
  29034. }
  29035.  
  29036.  
  29037. Listing 7
  29038. /* the following appears in the "globals"
  29039. header file(s) */
  29040. #ifdef IN_MAIN
  29041. #define declare( obj, init ) obj = init
  29042. #else
  29043. #define declare( obj, init ) extern obj;
  29044. #endif
  29045.  
  29046. /* the declarations/definitions */
  29047.  
  29048. declare( int i, 10 );
  29049. declare( char str[], "An initialized string" );
  29050. declare( int w[ 10 ][10 ], { 0 } );
  29051.  
  29052.  
  29053.  
  29054.  
  29055.  
  29056.  
  29057.  
  29058.  
  29059.  
  29060.  
  29061.  
  29062.  
  29063.  
  29064.  
  29065.  
  29066.  
  29067.  
  29068.  
  29069.  
  29070.  
  29071.  
  29072.  
  29073.  
  29074.  
  29075.  
  29076.  
  29077.  
  29078.  
  29079.  
  29080.  
  29081.  
  29082.  
  29083.  
  29084.  
  29085.  
  29086.  
  29087.  
  29088.  
  29089.  
  29090.  
  29091.  
  29092.  
  29093.  
  29094.  
  29095.  
  29096.  
  29097.  
  29098.  
  29099.  
  29100.  
  29101.  
  29102.  
  29103.  
  29104.  
  29105.  
  29106.  
  29107.  
  29108.  
  29109.  
  29110.  
  29111.  
  29112.  
  29113.  
  29114.  
  29115.  
  29116.  
  29117.  
  29118.  
  29119.  
  29120.  
  29121.  
  29122. On The Networks
  29123.  
  29124.  
  29125. We Welcome A New Moderator
  29126.  
  29127.  
  29128.  
  29129.  
  29130. Sydney S. Weinstein
  29131.  
  29132.  
  29133. Sydney S. Weinstein, CDP, CCP is a consultant, columnist, author, and
  29134. president of Datacomp Systems, Inc., a consulting and contract programming
  29135. firm specializing in databases, data presentation and windowing, transaction
  29136. processing, networking, testing and test suites, and device management for
  29137. UNIX and MS-DOS. He can be contacted care of Datacomp Systems, Inc., 3837
  29138. Byron Road, Huntingdon Valley, PA 19006-2320 or via electronic mail on the
  29139. Internet/Usenet mailbox syd@DSI.COM (dsinc!syd for those who cannot do
  29140. Internet addressing).
  29141.  
  29142.  
  29143. Last column, I mentioned that Brandon Allbery has gotten too busy to moderate
  29144. comp.sources.misc. He asked for volunteers to do the job. The new moderator is
  29145. now in place, and comp.sources.misc. is getting back to normal. Before I
  29146. announce the new one, let me thank Brandon for his time as moderator. Being a
  29147. moderator is at times a thankless job. Everyone complains when things go
  29148. wrong, but few are there to thank the moderator when the network is flowing
  29149. smoothly. It takes several hours per week to be a moderator of a group such as
  29150. comp.sources.misc.
  29151. The new moderator is Kent Landfield of Sterling Software, IMD. He can be
  29152. reached at any of:
  29153. kent@sparky.imd.sterling.com
  29154. kent@uunet.uu.net
  29155. sources-misc-request@uunet.uu.net
  29156. Submissions for publication in comp.sources.misc should be sent to
  29157. sources-misc@uunet.uu.net. Most of the major moderators maintain a mail
  29158. clearinghouse at UUNET.
  29159.  
  29160.  
  29161. New Sources In comp.sources.misc
  29162.  
  29163.  
  29164. If you run a PC-compatible machine and keep different operating systems in
  29165. different partitions, then you need bootmenu.bootmenu is a replacement for the
  29166. primary boot sector of MS-DOS-compatible computers. This boot program lets you
  29167. select the partition to be booted from a menu. Those with both UNIX and DOS on
  29168. their disk can select which to boot. Two versions are provided. The first is a
  29169. very small bootmenu that is compatible with SpeedStor-formatted disks but does
  29170. not time out and automatically boot the active partition. The second is
  29171. bootauto, which has a timeout to automatically boot the active partition if no
  29172. entry is made. bootmenu also includes pfdisk v1.3 for installing the new boot
  29173. sector without clobbering the current partition table. bootmenu was
  29174. contributed for Volume 15, Issues 84 and 85 by Gordon Ross of the Mitre
  29175. Corporation. He can be reached at <gwr@linus.mitre.org>.
  29176. A start on a set of utilities to encode and decode Group III and Group IV fax
  29177. images was contributed by Michael Marking <ipel!marking> for Volume 15, Issues
  29178. 86-88. If you are playing with fax boards and fax images, these might be a
  29179. base for your own software. Michael expects to extend these utilities to
  29180. support these images imbedded in TIFF files. He also notes that these routes
  29181. differ slightly from the ones in the CUJ article (CUJ July 1990, CUG volume
  29182. 317).
  29183. An interesting program was posted by Gene Olson, at <gene@zeno.mn.org>.
  29184. Compact_sv is a System V program to compress large data streams. It
  29185. demonstrates using shared memory as a buffer and having multiple tasks process
  29186. the data in this buffer to speed throughput. The program is faster and
  29187. produces smaller output than when compressing very large files (larger than
  29188. 1Mb), but it requires more than 1Mb of memory to run. It implements data
  29189. compression using a system very similar to V.42bis, entitled modified
  29190. Lempel-Ziv-Welsh. The problem with this posting is its legal warning: "This
  29191. program may be used only for research into data compression, as a study in
  29192. program optimization, or as an example of System V interprocess communication.
  29193. The author originally intended the program for practical day-to-day usage.
  29194. However, in final preparation for program release, it was learned the program
  29195. probably infringes one or more widely licensed patents in data compression.
  29196. There is no choice but to release the program for research purposes only." I
  29197. have never seen a program released with such a restriction before, but this
  29198. one is worth looking at, if only to revive the concept of multiple tasks
  29199. processing a buffer to increase throughput. Compact_sv is Volume 15 Issue 89.
  29200. Enquire has been upgraded to v4.3 by Steven Pemberton <steven@cwi.nl> for
  29201. Volume 15, Issue 95. Enquire determines many properties of a C compiler and
  29202. its execution environment. These include minimum and maximum signed and
  29203. unsigned char, int, long, float, and double, as well as other properties of
  29204. floating point numbers. It can also produce ANSI C float.h and limits.h files
  29205. based upon its tests. Other tests check for allocatable memory, bit order,
  29206. casts, promotions, pointer properties, and accuracy of the math routines.
  29207. Volume 15, Issue 98 was a patch to an item of interest to many. PCMAIL2, an
  29208. MS-DOS PC program to exchange UUCP messages and electronic mail with UNIX
  29209. hosts, has been patched to patchlevel 3. The full version was posted in
  29210. January 1990. This patch fixes some problems with UUCP control messages and
  29211. timing. They were submitted by Wietse Venema <wswietse@svbs01.bs.win.tue.nl>.
  29212. If you are running a System V UNIX from a terminal that supports a status
  29213. line, Charles Spell <cs00chs@unccvax.uncc.edu> has submitted a program written
  29214. by John Ribera to monitor the system via that status line. Watch notifies the
  29215. arrival of mail, the current time, and logons and logoffs from the system. The
  29216. terminfo parameter tsl, to go to the status lin,e and fsl, to return from the
  29217. status line and restore the cursor, must be in the terminal's terminfo
  29218. descriptor. It also helps if ws#, the number of characters in the status line,
  29219. is in the terminfo descriptor. Watch is Volume 15, Issue 99.
  29220. I mentioned last column that, dmake, an enhanced make utility was updated to
  29221. v3.6, and patch 1 was posted to comp.sources.bugs. Patch 1 has now appeared in
  29222. comp.sources.misc as Volume 15, Issue 90. Because the bugs group is rarely
  29223. archived, it is important that patches do eventually appear in the source
  29224. group. Dennis Vadura <dvadura@watdragon.waterloo.edu> has also submitted a
  29225. patch 2 to the bugs group, which should soon appear in the sources group.
  29226. In last column's alt.sources preview section, I mentioned a CP/M emulator.
  29227. This month another contributor has submitted a CP/M emulator in C complete
  29228. with CP/M BIOS to comp.sources.misc. Nick Sayer <mrapple@quack.sac.ca.us>
  29229. submitted upm, a UNIX CP/M as Volume 15, Issues 101 and 102. It emulates the
  29230. Z-80 CPU and the CP/M emulator.
  29231. Our new moderator took over with Volume 16. He started it out, with Issue 1
  29232. being a simple utility. Submitted by Mark Allsop
  29233. <mallsop@stimage.mqcc.mq.oz.au>, dosdu is a MS-DOS work-alike for the UNIX du
  29234. (disk usage) command. It supports the major options of the UNIX version plus
  29235. some for displaying sizes in Kb or Mb.
  29236. A rather large contribution was Metalbase from Richard Jernigan
  29237. <rpj@pygmy.rice.edu>. I normally don't mention shareware in this column, but
  29238. there is no suggested contribution in this one, so I'll make an exception.
  29239. Metalbase, submitted at v3.1, is a relational database system. It adds
  29240. commands to C for adding, updating, deleting, and retrieving records from
  29241. relations built with the utilities included in the package. The schemas are
  29242. written in a pseudo-English. Originally written for Amigas, its been ported to
  29243. UNIX System V. It was released as mbase in Volume 16, Issues 2-4.
  29244. Jonas Yngvesson <jonas-y@isy.liu.se> and Inge Wallin <ingwa@isy.liu.se> have
  29245. contributed a beta-test release of v2.0 of their SIPP, the simple polygon
  29246. processor. SIPP is a library for creating three-dimensional scenes and
  29247. rendering them using a scan-line z-buffer algorithm. The scene is built of
  29248. objects that can be transformed with rotation, translation, and scaling.
  29249. Objects form hierarchies, and hierarchies can have arbitrarily many subobjects
  29250. and subsurfaces. The library has an internal database for the objects that are
  29251. to be rendered. The library also provides three-dimensional texture mapping
  29252. with automatic interpolation of texture coordinates. Simple anti-aliasing is
  29253. performed via sampling, and multiple light sources and shading are supported.
  29254. Images are produced in the portable pixmap format (ppm) which has been
  29255. described in several prior columns. SIPP is Volume 16, Issues 5-10.
  29256. A rather large patch appearing this time was patch 2 to gnuplot 2.0. The major
  29257. additions are support for X11 Motif, new bit-mapped graphics routines, and new
  29258. terminal drivers for vttek (VT like Tektronix emulators), HP LaserJet II,
  29259. Kyocera Laser Printer, and SCO CGI graphics. This patch is applied to Volume
  29260. 11, Issues 65-79 (gnuplot 2.0) and is Volume 16, Issues 11-17.
  29261. For those sites wishing to run USENET Network News without installing the
  29262. entire new package, bootstrap news contributed by Ronald Florence
  29263. <ron@mlfarm.com> is for you. It allows a leaf node (you don't send news on to
  29264. anyone else) to receive a limited news feed by spooling the articles to mail
  29265. messages. It was released as Volume 16, Issue 18.
  29266. If you're having problems debugging programs that use malloc/free for memory
  29267. leaks, you need Marcus Ranum's <mjr@decuac.dec.com> mnemosyne from Volume 16,
  29268. Issue 19. mnemosyne is a set of tools to find memory leaks and hog functions.
  29269. It is a wrapper library for malloc/free and a #include file that intercepts
  29270. those calls and sends the calls to the wrapper routines. This provides memory
  29271. profiling and leak detection with no code changes save a single #include line.
  29272. James Plank <jsp@princeton.edu> has contributed a graphing program that takes
  29273. the description of the graph as input and outputs PostScript code. This
  29274. PostScript code can be directly printed or imported into a text processor that
  29275. allows for included PostScript images. Jgraph was released as Volume 16, Issue
  29276. 20. Note, this was a very large issue (over 9,000 lines and 200,000
  29277. characters). Usual issues are limited to less than 60,000 characters. Patch 1
  29278. was issued as Volume 16, Issue 77.
  29279. The largest posting this time was ECU, v3.00.00 and some patches to ECU. ECU,
  29280. an extended cu, is an asynchronous communications program for SCO UNIX V/386
  29281. and SCO Xenix V/386 or V/286. It incorporates a typical interactive
  29282. communications capability with a rich procedure language and an extended set
  29283. of interactive commands. It also supports several file transfer protocols. It
  29284. requires SCO Xenix 2.3.1 or later or SCO UNIX 3.2.0. Submitted by Warren
  29285. Tucker <n4hgf!wht>, ECU is Volume 16, Issues 25-59; patch 1 is Volume 16 Issue
  29286. 60; and patch 2 is Volume 16, Issues 70 and 71. The manual for ECU was posted
  29287. as Volume 16, Issues 22-24. An additional tool to take the log output from ECU
  29288. 3 and convert it to Microsoft Excel spreadsheet format was posted as Volume
  29289. 16, Issues 72 and 73.
  29290. Ron Gallant has submitted a C++ implementation of the statistical methods used
  29291. in his Non-linear Statistical Models book. Nlmdl can estimate univariate
  29292. non-linear regression models by least squares, multivariate regression models
  29293. by generalized least squares, simultaneous equations models by three-stage
  29294. least squares, and dynamic simultaneous equations models by generalized method
  29295. of moments. Errors can be heteroskedastic or serially correlated in any of
  29296. these models. It was posted as Volume 16, Issues 63-68 with a patch in Issue
  29297. 74.
  29298. Patch 1 to the MS-DOS Shell written by Ian Stewartson <istewart@datlog.co.uk>
  29299. (Volume 12, Issues 19-26) was released as Volume 16, Issue 78. This patch
  29300. fixes some bugs and adds some features from the Korn and POSIX shells. In
  29301. addition, support of use under OS/2 has been added.
  29302.  
  29303.  
  29304. Comments Continue...
  29305.  
  29306.  
  29307. Comments about the volume of postings in comp.sources.unix continue to appear,
  29308. but the moderator, Rich Salz, keeps ignoring the flack and plodding along
  29309. posting at his usual pace. I hope Rich keeps ignoring them, but I do wish he
  29310. would post patches sooner. There are several new releases this time:
  29311. If you're having difficulty making sense from the output of UNIX's ps command
  29312. (process status), and if you are running a BSD derived UNIX (sorry, System V
  29313. folks), Show Process Status (SPS) from J. Robert Ward of Olsen & Associates
  29314. <robert@olsen.uu.ch> fits your needs. SPS displays the information sorted into
  29315. parent/child order, with the fields displayed in English instead of hex where
  29316. useful. In addition, it's faster than ps. Makefiles are provided for many BSD
  29317. derived systems. SPS appeared as Volume 23, Issues 47-50.
  29318. A line-oriented macro processor called LOME (Line Oriented Macro Expandor) was
  29319. contributed by Darren New <new.ee.udel.edu> for Volume 23, Issues 51-59. LOME
  29320. processes its input file one line at a time applying the macros until no more
  29321. matches can be performed. The macros can also reference other files allowing
  29322. input and output to multiple source or output files.
  29323. Those long-time subscribers to CUJ may remember my article on source-code
  29324. librarians. One of the librarians I described was RCS. In Volume 23, Issue 75
  29325. Jason Winters <grinch!jason> submitted a front-end to RCS's ci/co (check in
  29326. and check out) entitled cio. The wrapper allows for recursively checking in or
  29327. out of directory structures and limiting the command to ASCII files only, thus
  29328. enabling the program to be run on directories with both source and binaries.
  29329. Running multiple printers from the same queue has been easy in the System V
  29330. line printer spooler for a long time using printer classes. However, the BSD
  29331. spooler has not supported this same feature. Matt Robinson
  29332. <yakker@ucrmath.ucr.edu> submitted mlpd for Volume 23, Issue 76. mlpd supports
  29333. using a single printer queue for a set of multiple printers. The idea is
  29334. similar to that of a common line at a bank. The next job gets the first
  29335. available printer from the set. It's simple, but not optimal. More optimal
  29336. would be to place short jobs on one printer with a higher priority. Longer
  29337. jobs and the overflow of short jobs get distributed among the rest of the set
  29338. (if optimizing throughput).
  29339. Also for sites running BSD derived UNIX systems is xmodem3.9. This version of
  29340. XMODEM supports the BSD flavor of terminals and terminal programs. It adds
  29341. running through tip, delayed startup, status messages, turning off EOT
  29342. verification, aborted transfers, and YMODEM-G support. Older xmodem3 versions
  29343. for BSD already supported XMODEM/CRC, XMODEM Block, and YMODEM batch. This
  29344. version was contributed by Steve Grandi <grandi@noao.edu> for Volume 23,
  29345. Issues 77-79. This does cause a slight conflict in version identification. For
  29346. the System V derived systems, a similar program called UMODEM is available,
  29347. currently in the 4.x version range.
  29348. Another news reader variant was posted recently. One of the older news readers
  29349. is Larry Wall's rn. New from Wayne Davison <davison@sri.com> is Threaded RN
  29350. (trn). It uses the References line to order the discussions in a natural,
  29351. reply-ordered sequence called threads. Having the replies associated with
  29352. their parent articles makes the following discussion easier. trn also has a
  29353. visual representation of the current thread in the upper right corner of the
  29354. header. A thread browser/selector has been added to make long threads easier
  29355. to handle. trn uses a thread database, created by a separately run database
  29356. manager (included). The space taken up by the database is an additional 5
  29357. percent of the space already used in the news spool area. trn requires rn
  29358. patchlevel 47 or greater. The additions are all marked for conditional
  29359. compilation. The same source can then be used to compile trn or rn. trn was
  29360. posted twice, the first posting had problems with the packaging so it was
  29361. reposted again with patch 1 applied. The revised patch was Volume 23, Issues
  29362. 60-73. Also posted as part of trn was unipatch format. Unipatch makes patches
  29363. that are smaller than context differences but contain the same information for
  29364. the patching program. It does this by merging the add and deletes into one
  29365. list instead of showing the before and after lines as two lists.
  29366. The last large posting in comp.sources.unix this time is an implementation of
  29367. ABC, a new interactive programming language. The source provides versions for
  29368. UNIX, the Atari ST, the Macintosh, and MS-DOS. ABC is an imperative language
  29369. originally designed as a replacement for BASIC. In addition to being
  29370. interactive and easy to learn, ABC is structured and high-level. It is
  29371. suitable for general everyday programming, the same as you would use BASIC,
  29372. Pascal, or AWK. It is not a systems programming language but it is an
  29373. excellent teaching language. It is interactive and supports prototyping. It is
  29374. a compact language with the source for most programs being about a quarter to
  29375. a fifth the size of the equivalent Pascal program. Submitted by Steven
  29376. Pemberton <steven@cwi.nl>, ABC is Volume 23, Issues 80-106, with Issue 105
  29377. being a correction for two files that some systems truncated due to line
  29378. length restrictions, and Issue 106 being patch 1. Patch 2 was issued as a
  29379. second Issue 106. (Seems Rich got a bit confused with all the problems that
  29380. occurred in the posting of ABC.) Patch 3 was Issue 108. Since there was no
  29381. 107, patch 2 should have been Issue 107.
  29382.  
  29383.  
  29384.  
  29385. reve Continues To Evolve
  29386.  
  29387.  
  29388. Last column I mentioned an Othello-like game, reve, which was upgraded to
  29389. v1.1. It was submitted by Rich Burridge <rburridge@sun.com> and Yves Gallot
  29390. <galloty@cernvax.cern.ch> for Volume 11, Issues 52-58 with patch 1 in Volume
  29391. 11, Issues 61-64. This time patch 2 was released as Volume 11, Issues 97-99.
  29392. Patch 3 was released as Volume 12, Issues 1-9. Patch 4 followed immediately as
  29393. Volume 12, Issues 10-13 and a small follow-up patch 5 as Issue 14. There are
  29394. too many additions and changes to mention here. Most changes are due to making
  29395. reve run on many types of computers and variants of display and operating
  29396. systems.
  29397. Other patches released this period include patch 1 to bt, the Broken Throne
  29398. multiplayer real-time conquest game from Tom Boutell
  29399. <boutell@freezer.it.udel.edu>. Patch 1 was issued as Volume 11, Issue 77. A
  29400. Sunview clear-the-bricks game, blockbuster (Volume 11, Issues 18-20) had patch
  29401. 1 issued by Eric Van Gestel <ericvg%BLEKUL60.BITNET@cunyvm.cuny.edu> as Volume
  29402. 11, Issue 95. Lastly on the patch front, sdi, a Sunview missile-command game,
  29403. had patch 1 issued. sdi is Volume 1, Issues 54-59 and the patch from Bill
  29404. Randle <billr@saab.cna.tek.com> came from a bug fix submitted from Brian
  29405. Nakata. The patch was released as Volume 11, Issue 96.
  29406. A port of the X11 game Boulder Dash to MS-DOS PCs using VGA display adapters
  29407. was posted as Volume 11, Issues 81-83. It was submitted by Herve Soulard
  29408. <soulard@fantasio.inria.fr>. The original version for X11 windowing systems
  29409. was written by Jeroen Houttuin <houttuin@ks.id.ethz.ch>. Included are three
  29410. programs, XBD, the game itself, XBDE, a level editor for XBD, and BITMAP, a
  29411. bitmap editor for XBD.
  29412. The largest posting in the games group was larn, a dungeon type adventure game
  29413. for UNIX, VMS, MS-DOS, or any termcap supporting system. larn, now at v12.2,
  29414. is an enhancement of the original larn (written by Noah Morgan) by Kevin
  29415. Routley <routley@tle.enet.dec.com>. This version differs from ularn in Volume
  29416. 7 of the game's archives and is closer to the original in look and feel than
  29417. that version. larn is similar in concept to hack, rogue, or moria, but with a
  29418. different feel and winning criteria. larn was posted in 11 parts as Volume 11,
  29419. Issues 84-94 with patch 1 as Volume 12, Issue 16.
  29420.  
  29421.  
  29422. Previews From alt.sources
  29423.  
  29424.  
  29425. There was an explosion of postings in alt.sources. So many, that I can only
  29426. touch on some of them. I hope the better ones will be checked out and posted
  29427. to the mainstream source groups in the future.
  29428. remind, v2.2 was posted in five parts on Nov. 16, 1990 by David Skoll
  29429. <dfs@doe.carleton.ca>. remind is a program to prompt about upcoming events. It
  29430. can mail reminders, prompt them on the screen, and now produce an annotated
  29431. calendar for printout. New in 2.2 is the ability to read and handle standard
  29432. UNIX calendar files. remind 2.2, patch 3 was posted on Nov. 28, 1990, patch 4
  29433. on Nov. 29, 1990 and patch 5 on December 3, 1990. I did not see patches 1 or 2
  29434. go by, but they may have already been applied to the original posting. Since
  29435. postings in alt.sources are uncontrolled, it's hard to tell if something is
  29436. missing.
  29437. A set of routines to extend printf were posted by D. Richard Hipp
  29438. <drh@duke.cs.duke.edu> on Nov. 29, 1990. The new variations include mprintf
  29439. that uses malloc to acquire space to place its output, snprintf, a version
  29440. that limits its length to a set number of characters a la strncpy, xprintf
  29441. that calls a function to dispose of its output, and nprintf, which only
  29442. returns the number of characters that would have been output. The other
  29443. enhancement is that new conversion format letters can be added at runtime. An
  29444. additional enhancement for centered output was posted on Nov. 30, 1990.
  29445. Need a cross-assembler? Mark Zenier <ssc!markz> posted his frankenstein
  29446. cross-assembler. It supports 13 flavors of assemblers (1804, 2650, 6301, 6502,
  29447. 6805, 6809, 6811, tms7000, 8048, 8051, 8096, z8 and z80). No macros,
  29448. relocatable linkers, fancy print controls, or structured control statements,
  29449. but as he states, "It's a start." It was posted on Dec. 4-7, 1990 in many
  29450. parts, about 1.2Mb in all.
  29451. The vi editor clone, Elvis, was upgraded to 1.4 on Dec. 5, 1990. A nine-part
  29452. posting by Steve Kirkendall <kirkendall@eecs.cs.pdx.edu>, Elvis now runs under
  29453. UNIX, MS-DOS, OS-9, and Coherent. New in 1.4 is an alias program to allow for
  29454. smaller disk usage on systems that do not support links. This allows a small
  29455. wrapper for the view, ex, and vi varients of the Elvis executable. Also new
  29456. are line number support for the :e command, appending to a named cut buffer,
  29457. limited macro facility, addition of the :abbr command, adding a delta to vi
  29458. mode searches, and several new :set options including showmatch, autoprint,
  29459. and edcompatible. Also added are a new set of commands to scan the error
  29460. messages issued by the compiler. These are :make and :cc. Work is in progress
  29461. for v1.5.
  29462. A seven-part posting of a work-alike of the Korn shell by Eric Gisin was
  29463. posted by netcom!netnews on Dec. 12, 1990. I only mention it because a large
  29464. number of messages have recently been asking about a public domain version of
  29465. the Korn shell. Apparently the author is no longer reachable, so this makes a
  29466. starting point. It supports most of the older Korn shell, but only has EMACS
  29467. line editing. The other line-editing modes were not present.
  29468. Tom Horsley <tom@ssd.csd.harris.com> posted mkid, an identifier cross
  29469. reference tool on Dec. 12, 1990 in an eleven-part posting. He is looking for
  29470. comments and corrections. This is a variation on the mkid package from 1987
  29471. written by Greg McGary. He has made several additions and corrections, and is
  29472. asking others to send him their patches to the original program. He will
  29473. attempt to merge all of them and issue a new version to comp.sources.unix.
  29474. Among the shell variants, the Z shell (zsh) was posted in eight parts by Paul
  29475. John Falstad <pfalstad@phoenix.princeton.edu> on Dec. 14, 1990. The Z shell is
  29476. similar to ksh and tcsh. It is designed to be an interactive shell as well as
  29477. a batch command language. Concepts were borrowed heavily from ksh, bash, tcsh,
  29478. sh, and csh.zsh is a work in progress, but it has a manual page and make
  29479. files.
  29480. If you wanted to try one of the programs I described that required BSD type
  29481. sockets, but you were on a machine that does not support networking,
  29482. pgd@bbt.se (I never did find his name on the posting.) posted a socket
  29483. emulation library for System V on Dec. 19, 1990. It uses named pipes and file
  29484. locks. Of course, without networking you are limited to the local machine, but
  29485. it works well enough to put up X-windows on a Xenix machine without networking
  29486. or streams.
  29487. On Dec. 20, 1990 James R. Purdon, III <purdon@athena.mit.edu> posted slugnet
  29488. in six parts. slugnet is a multi-user interactive conferencing facility. It
  29489. does require sockets and runs in a client/server configuration. It allows
  29490. multiple site conferences as well as multiple conferences.
  29491. For those sites that need an ndbm, Ozan Yigit <oz@nexus.yorku.ca> has provided
  29492. sdbm. The Substitute DBM library was posted on Dec. 15, 1990. It provides a
  29493. complete, portable replacement for the ndbm system. ndbm uses a different
  29494. algorithm so the data files are not compatible.
  29495. Another menuing system was posted by Paul Condie <pcbox!pjc> on Dec. 26, 1990
  29496. in 14 parts. A simple utility that allows for adding menus to UNIX. Menus
  29497. supports sub-menus and processes. Nesting does not increase startup time or
  29498. memory usage. Menus can also be data entry screens allowing for choices and
  29499. responses to be controlled.
  29500. The moderator of comp.sources.unix even made a posting to alt.sources. Rich
  29501. Salz <rsalz@bbn.com> submitted his pattern matching subroutines wildmat.c on
  29502. Jan. 3, 1991. It can match arbitrary patterns using wild cards. It is 8-bit
  29503. clean. An additional correction and comment was made on Jan. 4, 1991 by Lars
  29504. Henrik Mathiesen <thorinn@diku.dk> in regard to the abort code speedup.
  29505. William E. Davidsen, Jr. <davidsen@crdos1.crd.ge.com> posted a version of
  29506. LHARC that he modified to run cleanly on a large number of UNIX systems. His
  29507. two-part posting on Dec. 6, 1990 is version 0.03 beta.
  29508. Those longing for X-11 Rel 4 and still running SCO Xenix 386 need only see the
  29509. 12-part posting on Jan. 7, 1991 by Chain Lee <chain@paul.rutgers.edu>. He
  29510. posted a large set of patches to adapt the source to Xenix. Of course you
  29511. still need the X11R4 source and its 18 patches first. That alone takes four
  29512. magnetic tapes.
  29513. All UNIX system administrators need to acquire COPS. The latest version, 1.02,
  29514. was posted in nine parts on Jan. 8, 1991 by Dan Farmer <df@sei.cmu.edu>. Dan
  29515. is part of the Computer Emergency Response Team (CERT). This package, which he
  29516. started before working for CERT, is based on his suspicion that most security
  29517. problems are caused by trivial misconfigurations. COPS checks out your UNIX
  29518. system for security holes and reports its findings. It is up to the system
  29519. administrator to correct those holes. COPS is a tool to assist in checking for
  29520. potential security problems, not a crutch. Reliance on it to find all problems
  29521. is asking for trouble. But it is easy to install and run, and it should be run
  29522. regularly.
  29523. A cron work-alike including the at facility (batch jobs) was posted on Jan.
  29524. 13, 1991 by Donald Lashomb <uncle!donlash> in four parts. It supports cron,
  29525. crontab, at, and batch commands. It permits names as well as numbers for the
  29526. date fields in the cron tables, enforces the real uid for security, and
  29527. includes a new cronjob. The last is like the batch at command, but works
  29528. repetitively like a crontab. It is mostly compatible with the standard System
  29529. V versions.
  29530.  
  29531.  
  29532.  
  29533.  
  29534.  
  29535.  
  29536.  
  29537.  
  29538.  
  29539.  
  29540.  
  29541.  
  29542.  
  29543.  
  29544.  
  29545.  
  29546.  
  29547.  
  29548.  
  29549.  
  29550.  
  29551.  
  29552.  
  29553.  
  29554.  
  29555.  
  29556.  
  29557.  
  29558.  
  29559.  
  29560.  
  29561.  
  29562.  
  29563. C Communications Toolkit
  29564.  
  29565.  
  29566. Victor R. Volkman
  29567.  
  29568.  
  29569. Victor R. Volkman received a bachelor's degree in computer science from
  29570. Michigan Technological University. He is a software engineer at Cimage
  29571. Corporation, Ann Arbor, Michigan, and can be reached at the HAL 9000 BBS,
  29572. 313-663-4173, 1200/2400/9600 baud or via Usenet as vrv@cimage.com.
  29573.  
  29574.  
  29575. The C Communications Toolkit (hereafter CCT or toolkit) from Magna Carta
  29576. Software (Garland, TX) is a comprehensive package providing everything from
  29577. low-level UART register manipulation to batch file-transfer protocols. It also
  29578. supplies data translation and ANSI terminal emulation facilities. The CCT is
  29579. shipped with libraries compiled for Turbo C, Watcom C, Microsoft C, and Mix
  29580. Power C. Each of the libraries includes versions built for the small, medium,
  29581. and large memory models. The toolkit includes all the source code you need to
  29582. port to an unsupported memory model or compiler. It retails for $150. Magna
  29583. Carta backs its product with a 30-day money-back guarantee.
  29584. I received version 1.00B of the toolkit, which was dated 06/09/90. The entire
  29585. install set was squeezed onto three 5¼ 360K disks using LZH compression.
  29586. Installation consists of uncompressing the .LIB, .C, and .H files into the
  29587. directories of your choice. The tookit should work fine with any PC (from 8088
  29588. to 80486) on any version of DOS (3.0 or later).
  29589. The CCT supports a variety of communications hardware. The primary emphasis is
  29590. on the INS8250 family of Universal Asynchronous Receiver/Transmitters (UART)
  29591. chips. The INS8250 family, which has been the PC standard for almost a decade,
  29592. includes NS16450 and NS16550A compatible chips as well. Special support is
  29593. provided for the FIFO buffer and interrupt threshold features of the NS16550A.
  29594. This toolkit also supports the Zilog Z-80 SIO communications chip. The SIO
  29595. provides both synchronous and asynchronous communications, but it is normally
  29596. found only on 3270 terminal emulation adapters. Because there is no standard
  29597. hardware interface for the SIO on the PC, the CCT implementation is only
  29598. guaranteed to work with AST's 3270 adapters.
  29599. The toolkit also supports the Intel Connection Coprocessor using the
  29600. Communication Applications Standard (CAS). The CAS interface is a high-level
  29601. mechanism for scheduling file and facsimile transmissions.
  29602.  
  29603.  
  29604. Initializing Communications
  29605.  
  29606.  
  29607. The CCT supports both polled and interrupt-driven communications for receiving
  29608. and transmitting data. The init_port function can initialize each of these
  29609. four cases. Once a port is initialized, it is available to begin transmitting
  29610. and receiving characters. An example invocation is:
  29611. init_port(&port1, COM1, 1200L, DATABITS8,
  29612. PARITY_NONE, STOPBITS1, txbuf, rxbuf);
  29613. The first argument is a pointer to a mostly uninitialized COMM_PORT data
  29614. structure. The second argument is the base address of the serial port to be
  29615. initialized. The next four parameters set up the basic configuration of the
  29616. serial link (1,200 baud, 8N1). The last two arguments specify optional
  29617. transmit and receive buffering.
  29618. If the rxbuf argument is not a null pointer, then init_port sets up an
  29619. interrupt-handler for receiving characters into the buffer. If the txbuf
  29620. argument is not a null pointer, then the function sets up a similar handler
  29621. for transmitting characters. If both rxbuf and txbuf are null, then all I/O
  29622. takes place in polled mode.
  29623. The default transmitter and receiver buffer sizes are set to 1,024 bytes and
  29624. 2,048 bytes respectively. If these are inappropriate, then you must either
  29625. change the #defines in COMM.H or call the lower-level c_open function. For
  29626. c_open to work correctly, you must initialize several more fields in the
  29627. COMM_PORT data structure in advance.
  29628.  
  29629.  
  29630. Ports And Modems
  29631.  
  29632.  
  29633. The CCT keeps track of everything related to a given communications session in
  29634. a COMM_PORT data structure. This all-encompassing data structure contains more
  29635. than 150 members to describe the state of the session. COMM_PORT conforms to
  29636. the guidelines set forth by Campbell (1987) for writing UART-independent
  29637. communications programs. Both the COMM_PORT and MODEM data structures closely
  29638. parallel Campbell's definitions. The CCT does not use Campbell's "virtual
  29639. UART" model, but it does capably isolate UART-specific registers by
  29640. incorporating a union pointer in COMM_PORT. The utype member of COMM_PORT tags
  29641. the UART union being used. A UART union appears as:
  29642. typedef union uart {
  29643. UART8250 u8250; /* Intel */
  29644. UARTZ80SIO uz80sio; /* Zilog */
  29645. } UART;
  29646. The other members of COMM_PORT define parameters for buffers, flow control,
  29647. RS-232C inputs and outputs, line characteristics, receiver and transmitter
  29648. data translation, and physical port characteristics. In addition to the
  29649. session-specific parameters, COMM_PORT also includes more than two dozen
  29650. function pointers. Last, COMM_PORT contains a pointer to a MODEM structure.
  29651. The COMM_PORT function pointers designate Interrupt Service Routines (ISRs),
  29652. low-level UART-specific services, and callback functions. The ISR function
  29653. pointers in the COMM_PORT structure keep track of which ISRs belong to a given
  29654. port. Generally, each communications port on the PC requires a dedicated
  29655. Interrupt Request (IRQ) line. The PC architecture dictates that each IRQ line
  29656. must have its own ISRs. The function pointers for low-level UART-specific
  29657. services transmit and receive characters without requiring every function to
  29658. be intimately familar with how UART works. The callback function pointers
  29659. enable the CCT functions to better serve your application's environment. For
  29660. example, you can transparently send a copy of the communications stream to
  29661. your printer by pointing the p_out member to your own printer echoing
  29662. function.
  29663. As mentioned earlier, the COMM_PORT data structure also contains a pointer to
  29664. a MODEM data structure. The MODEM data structure holds all of the data needed
  29665. to initialize a modem before connection. Nearly 100 initialization parameters
  29666. can be set if desired. MODEM is composed almost entirely of Hayes Smartmodem
  29667. command strings. For example, the dial_cmd member typically contains the ATDT
  29668. dialing prefix. Nearly all modems manufactured within the last five years
  29669. understand at least a subset of these commands.
  29670. The CCT provides several functions to take advantage of the MODEM information.
  29671. Each of these functions locates a MODEM structure via a COMM_PORT. The
  29672. modem_init function sends the initialization strings in MODEM. The modem_dial
  29673. function takes a telephone number string and performs the dialing for you. The
  29674. modem_hangup function hangs up the phone immediately. The general purpose
  29675. modem_scmd function sends a command to the modem and returns a pointer to the
  29676. reply buffer. For example, you use the following sequence to turn off the
  29677. modem speaker:
  29678. strcpy(my_port->modem.speaker, "MO");
  29679. reply = modem_scmd(my_port,
  29680. my_port->modem.speaker, BUF_SIZE);
  29681. if (strcmp(reply,"OK") != 0)
  29682. printf("Error: modem not responding!\n");
  29683.  
  29684.  
  29685. Data Translation
  29686.  
  29687.  
  29688. The toolkit provides extensive support for translation on both received and
  29689. transmitted data. The CCT data translation functions govern character echoing,
  29690. newline handling, programmable delays, and flow control issues. The parameters
  29691. for data translation can be changed at any time via the set_rx_xlat and
  29692. set_tx_xlat functions. Both of these functions take three arguments. The first
  29693. is the COMM_PORT to be affected. The last two tell which translation item to
  29694. set and to which value to set it. For example, to enable printer echoing you
  29695. would call the function:
  29696. set_rx_xlat(&myport, PRINTER_ECHO, ON);
  29697. The data translation functions enable you to control when and where characters
  29698. will be echoed. The LOCAL_ECHO, REMOTE_ECHO, PRINTER_ECHO, and
  29699. CAPTURE_BUFFER_ECHO flags control echoing. The LOCAL_ECHO flag tells whether
  29700. characters should be displayed as they are read or written. On receive, the
  29701. REMOTE_ECHO flag tells whether received characters should be echoed back. On
  29702. transmit, REMOTE_ECHO tells whether you should wait for a return echo before
  29703. transmitting the next character. The PRINTER_ECHO flag allows received and/or
  29704. transmitted characters to be logged to a printer. Last, CAPTURE_BUFFER_ECHO
  29705. permits received or transmitted characters to be simultaneously copied to a
  29706. memory buffer.
  29707. The CCT data translation functions also give you extensive control over
  29708. newline handling. Special newline handling is often needed for communicating
  29709. with mainframe hosts and UNIX workstations. As with other translations,
  29710. newline handling can be independently specified for receiving and
  29711. transmitting. The nine different options available include Linefeed to
  29712. Carriage-Return (LF2CR) and Carriage-Return Linefeed to Linefeed (CRLF2LF).
  29713. Although programmable delays are not strictly a data translation, the same CCT
  29714. functions enable them as well. The INTERBYTE_DELAY and TRAILINGBYTE_DELAY
  29715. flags control delays. On receive, INTERBYTE_DELAY allows you to specify a
  29716. timeout value to wait for each incoming character. On transmit,
  29717. INTERBYTE_DELAY specifies a pause between each outgoing character. This is
  29718. handy for communications sessions with mainframe hosts, which have notoriously
  29719. poor receive buffering. TRAILINGBYTE_DELAY allows for extra time after the
  29720. newline is transmitted or received.
  29721. Last, the translation functions also set up the flow control parameters. Flow
  29722. control prevents overrunning the receive buffer when data is coming in faster
  29723. than it can be processed. The C Communications Toolkit supports RTS/CTS,
  29724. DTR/DSR, and XON/XOFF handshaking. For XON/XOFF control, you must also
  29725. designate the characters used to represent these conditions. Some of the flow
  29726. control techniques can be combined.
  29727.  
  29728.  
  29729. File Transfers
  29730.  
  29731.  
  29732.  
  29733. The CCT has high-level functions to send and receive files using several
  29734. popular protocols. Specifically, CCT supports the XModem, XModem-CRC,
  29735. XModem-1K, YModem, Y- Modem-G, and Kermit protocols. With the CCT, receiving a
  29736. file can be as simple as calling freceive with the appropriate parameters. A
  29737. sample invocation of freceive is:
  29738. ret = freceive(&myport, &xinfo, protocol,
  29739. 10*1024L, transfer_progress);
  29740. The first argument to freceive is the COMM_PORT structure pointer. The next is
  29741. a pointer to an unitialized XFER structure. An XFER structure consists of
  29742. about two dozen members that describe every aspect of the file transfer. The
  29743. third argument to freceive specifies the suggested protocol to use. Under
  29744. certain circumstances, freceive can change the protocol in midstream if it
  29745. discovers that the sender is using a different protocol. The next argument
  29746. tells how large the receive buffer should be. The receive buffer is
  29747. dynamically allocated within freceive and will be automatically reduced if
  29748. insufficient heap space is available. The last parameter is a pointer to a
  29749. callback function for progress reports. The progress report function must
  29750. receive parameters for the COMM_PORT, status, and bytes received so far.
  29751. Typically, a progress report function formats and displays status messages in
  29752. a screen window.
  29753. With the CCT, sending a file is only slightly more complicated than receiving
  29754. one. The primary difference is that files must be enqueued before sending
  29755. them, even if only one file is going to be sent. The fqueue function takes a
  29756. pointer to an XFER structure and a string containing the filename to enqueue.
  29757. For multiple-file (a.k.a. batch) protocols, each invocation of fqueue adds
  29758. another file to the list. The fsend function does the actual work of sending
  29759. the file:
  29760. fqueue(&xinfo, "HAL_9000.ZIP");
  29761. fqueue (&xinfo, "TOOLKIT.DOC");
  29762. ret = fsend(&myport, &xinfo, YMODEM,
  29763. transfer_progress);
  29764. The arguments to fsend are similar to those described for freceive, except
  29765. fsend uses a smaller fixed-size buffer for transmission. fsend can also change
  29766. the protocol in midstream to any XModem or YModem variant if it detects the
  29767. receiver using a different protocol.
  29768. The CCT makes the normally unwieldly Kermit protocol easy to use. To send or
  29769. receive via Kermit, you need only add a call to init_kermit just before the
  29770. actual freceive or fsend. init_kermit initializes a KERMIT_PARMS structure
  29771. containing about 40 members. By modifying the KERMIT_PARMS structure, you can
  29772. fine-tune the Kermit protocol parameters.
  29773. Although you can implement file transfer protocols that are not supported by
  29774. CCT, it might be a daunting task to do so. The source code for the supplied
  29775. protocols is heavily tailored toward the eccentricities of the XModem
  29776. variants. However, you could still use the library source as a model for new
  29777. protocol development.
  29778.  
  29779.  
  29780. Terminal Emulation
  29781.  
  29782.  
  29783. The CCT also provides VT-52/100 and ANSI X3.64 terminal protocol emulation.
  29784. These two emulations together account for nearly all character-based terminals
  29785. used today. The init_term function selects and installs terminal emulation.
  29786. init_term requires three arguments. The two supported invocations of init_term
  29787. are:
  29788. t = init_term(&myport,
  29789. vt100_write, vt100_conoutc);
  29790. t = init_term(&myport,
  29791. ibmansi_write, ibmansi_conoutc);
  29792. The first argument is the COMM_PORT for which terminal emulation will be
  29793. enabled. The next argument is a pointer to the function that will process
  29794. serial output. The final argument is a pointer to the function that will
  29795. handle screen output. Although the documentation makes much ado about how new
  29796. terminal drivers can be added in a "cookbook fashion," there are barely two
  29797. pages devoted to writing an alternate terminal emulation.
  29798. Unfortunately, the CCT terminal emulations use the BIOS video INT 10h for all
  29799. output. The INT 10h interface is an order of magnitude slower than writing
  29800. directly to memory, which will undoubtedly reduce throughput on high speed
  29801. (9,600 baud or greater) links.
  29802.  
  29803.  
  29804. Performance
  29805.  
  29806.  
  29807. The best measure of a communications library is how well it performs versus
  29808. other applications. I compared the example programs supplied with the CCT
  29809. against TTYTALK and ProcommPlus v1.0. I developed TTYTALK, a complete terminal
  29810. program for the IBM PC written with C, in 1988 (see bibliography). TTYTALK
  29811. supports a subset of the command language found in the popular CROSSTALK XVI
  29812. package by DCA. TTYTALK features input/output filtering, data translation, and
  29813. send and capture of ASCII files. The CCT example program #8 most closely
  29814. matches the capabilities of TTYTALK. My comparison indicated that the size of
  29815. the source files for TTYTALK and CCT example #8 were roughly equal (23K vs.
  29816. 18K). When built with Microsoft C 5.1 small model, the executable size for CCT
  29817. example #8 was only about 32K larger than TTYTALK.EXE (which was 17K). See
  29818. Figure 1. Since the entire CCT large model library is less than 100K, the
  29819. library imposes little overhead.
  29820. For the second stage of performance evaluation, I benchmarked the CCT examples
  29821. #9 and #10 against the ProcommPlus v1.0. I designed this test to match their
  29822. file transfer capabilities on XModem-1K at both 2,400 and 9,600 baud (v.32). I
  29823. ran all tests on a 25MHz 80386 computer with a NS16550A UART chip and a Hayes
  29824. ULTRA 96 v.32 modem. The programs were run in a DESQview v2.26 DOS window with
  29825. 340K RAM available. On the other end of the communications link, I used a
  29826. Telebit T2500 v.32 modem in conjunction with a 10MHz 80286 computer. The
  29827. connections uniformly used the LAP-M link protocol and v.42bis data
  29828. compression to transmit .ZIP files. The host BBS software used was PCBoard
  29829. v14.5/E3 with internal protocols. The PCBoard host software reported the
  29830. effective file transfer rates. My results show the "best case" transfer rates
  29831. that I obtained after at least four trials.
  29832. The results of testing at 2,400 baud showed no significant differences between
  29833. ProcommPlus and the CCT (see Figure 2). The file transfer efficiency was
  29834. within expected norms for the X-Modem-1K protocol. However, at 9,600 baud I
  29835. had some problems with the CCT application. The XModem-1K sent reported errors
  29836. on several occasions and aborted the transfer. When the file sends were
  29837. successful, the CCT application performed at the same level as ProcommPlus
  29838. (see Figure 3). Although I had no difficulty receiving files with the CCT at
  29839. 9,600 baud, the transfer rates were consistently about 100 characters per
  29840. second (cps) behind ProcommPlus. The XModem family of protocols often has
  29841. difficulty at high speeds. For this reason, most highspeed modem users prefer
  29842. Zmodem when at 9,600 baud and above.
  29843.  
  29844.  
  29845. Documentation
  29846.  
  29847.  
  29848. The CCT documentation consists of a single 600-page paperback reference and
  29849. tutorial book. Approximately half the book is devoted to a tutorial of basic
  29850. communications concepts including transmission modes, the RS-232 standard,
  29851. UART register sets, the Hayes standard "AT" command set, file transfer
  29852. protocols, and terminal emulation. The rest of the manual describes header
  29853. files and gives an alphabetical list of functions, parameters, and return
  29854. codes. The manual could easily be improved in two regards. First, it lacks a
  29855. glossary of communications terminology. Second, the index should contain
  29856. entries for all of the CCT functions.
  29857. The CCT takes a gentle approach to demonstrating typical communications
  29858. applications and how they can be solved. The tutorial book presents a series
  29859. of 17 complete mini-applications in order of increasing complexity from a
  29860. polling dumb-terminal to a full transmit/receive interrupt-driven terminal.
  29861. Along the way, the book shows capabilities such as terminal emulation and file
  29862. transfer protocols. None of the tutorial programs demonstrates how to use the
  29863. advanced features of the NS16550A UART.
  29864. The documentation discusses the Hayes command set and individually covers
  29865. specifics of the Smartmodem 300, 1,200, and 2,400 baud models. The Hayes
  29866. V-series is also mentioned, although the Hayes ULTRA 96 v.32 modem is not
  29867. covered. The manual also ignores the differences between the Smartmodem 1200
  29868. and 1200EF (Extended Features) products. Although the CCT contains
  29869. vendor-specific support for Telebit series modems, the manual does not list
  29870. the Telebit extensions to the Hayes command set.
  29871. The manual precisely describes the XModem file transfer protocol. The authors
  29872. correctly indicate all of XModem's faults, variants, and workarounds. All of
  29873. the popular Xmodem extensions including XModem-CRC, XModem-1K, YModem, and
  29874. YModem-G protocols are summarized. The Kermit file transfer protocol is
  29875. treated the same as the Xmodem protocol. The documentation also describes the
  29876. Kermit 8th-bit quoting and Run-Length Encoding (RLE) extensions that the CCT
  29877. offers.
  29878. The function reference section groups the function calls by category. The
  29879. functions are then presented alphabetically within each category. The notes
  29880. for each function indicate its purposes, arguments, and return values.
  29881. Additionally, the notes mention the header file in which it is declared, any
  29882. hardware limitations it might have, and all of the lower-level functions that
  29883. it might call.
  29884. One area of communications ignored by the tutorial programs and text is
  29885. development of host (e.g. BBS) software with the toolkit. Although many of the
  29886. issues are the same for both host and terminal modes, a novice could benefit
  29887. from such a discussion. Topics for host support would include ring detection
  29888. and answering, loss of carrier detection, line settings, and Hayes register
  29889. settings for the host.
  29890. The manual does contain some typographical errors, but these are easily
  29891. overlooked given the clarity of the explanations. There are also a few
  29892. references to nonexistent figures and tables.
  29893.  
  29894.  
  29895. Support
  29896.  
  29897.  
  29898. Magna Carta Software provides technical support via telephone and Bulletin
  29899. Board System (BBS). However, you must mail in the registration form to obtain
  29900. a serial number before you can obtain any technical support. The Magna Carta
  29901. BBS consists of a single 2,400 baud modem line running OPUS software. The BBS
  29902. is frequented by Andrew Chalk, the principal developer and president of Magna
  29903. Carta Software. In addition to communication utilities and specifications, the
  29904. BBS also has the latest beta test version of the CCT. During my product
  29905. testing, I downloaded CCT v1.00D from the Magna Carta BBS. Most support
  29906. questions posted on the BBS are answered within 24 hours.
  29907. The back cover of the CCT manual and all of the advertising indicates that
  29908. several intelligent multi-port serial boards are supported, including models
  29909. from AST, CommTech, Digiboard, and Arnet. An intelligent multi-port serial
  29910. board usually includes an onboard CPU, such as an Intel 80186. These boards
  29911. typically cost more than the motherboard on your computer. A multi-port board
  29912. may contain 4, 8, 16, or even 32 serial ports. These adapters often include
  29913. onboard EPROM and RAM areas. The adapter RAM may be either privately
  29914. transferred via DMA or mapped through a window in high DOS memory (like video
  29915. RAM).
  29916. A thorough search of the documentation and source code revealed no references
  29917. to any of these multi-port boards. After scouring the Magna Carta BBS, I
  29918. discovered supplemental source code for the CommTech and Digiboard products
  29919. which had been omitted from the CCT. I asked Andrew Chalk about this apparent
  29920. support discrepancy. He said the CommTech and Digiboard drivers would be
  29921. distributed in the future starting with CCT v1.01. Chalk also said that
  29922. drivers for Star Gate ACL/II and the AST CC-832 adapters were currently being
  29923. developed. The release dates for these drivers have not yet been announced at
  29924. the time of this writing.
  29925. Support for the CommTech FASTCOMM4 board consists of 200 lines of C allowing
  29926. it to be addressed as an array of serial ports. Support for the DigiBoard
  29927. DigiCHANNEL COM/4i and COM/8i is much more extensive than for the CommTech
  29928. board. The DigiCHANNEL driver provides methods to download communications
  29929. functions and execute them on the adapter's own CPU. Special board-specific
  29930. functions manipulate the command and data queues for each port. The CCT
  29931. functions modified for the DigiCHANNEL board all have an xi_ prefix added to
  29932. them. For example, the set_speed function is renamed xi_set_speed for the
  29933. DigiCHANNEL. This function naming convention would seem to make it difficult
  29934. for an application programmer to maintain a single set of device-independent
  29935. source modules. An application programmer would most likely need to resort to
  29936. #ifdefs to work around this naming convention.
  29937. If you are using a multi-port board that the CCT does not currently support,
  29938. Magna Carta Software will consider adding support under an exchange agreement.
  29939. If you are willing to loan the board to Magna Carta for 30 days, they will
  29940. consider writing the drivers for you. In return, Magna Carta retains the
  29941. rights to distribute the drivers in subsequent releases of CCT.
  29942. Both data structures and functions of the CCT have been revised since release
  29943. 1.00 in May 1990. The revisions leading up to v1.00D require changes at the
  29944. source code level to applications written with the initial version of CCT.
  29945. These changes are fully documented in the release notes and are mainly
  29946. oriented toward removing hardware-dependent code at the UART level -- an
  29947. admirable goal. The source-level interface should remain stable in future
  29948. releases.
  29949.  
  29950.  
  29951.  
  29952. The Competition
  29953.  
  29954.  
  29955. Magna Carta's CCT compares favorably in both price and functionality against
  29956. competing DOS communications libraries. Some other packages do not support the
  29957. Z-80 SIO USART or VT-100 emulation. Most DOS communications libraries offer
  29958. support for the same file-transfer protocols, flow-control mechanisms, UARTS,
  29959. and multi-port serial boards. In addition, many libraries are including full
  29960. library source code at no extra charge.
  29961. With a list price of $150, the C Communications Toolkit is the least expensive
  29962. of the libraries I have seen. Other packages, such as the C Asynch Manager by
  29963. Blaise Computing ($189), C Asynch Library by SilverWare ($250), and Essential
  29964. Communications by South Mountain Software ($329), provide similar
  29965. functionality but with a higher cost.
  29966. The CCT file transfer protocol suite is only missing ZModem capability.
  29967. Z-Modem is by far the most efficient file transfer protocol available. ZModem
  29968. offers CRC-32 error detection, dynamically negotiated block sizes, and the
  29969. ability to restart aborted file transfers (Forsberg 1990). The Solid Link
  29970. communications library by Solid Software ($199) is the only competitor that
  29971. includes the ZModem protocol. However, the source code for Solid Link is
  29972. optional and costs significantly more than the package itself.
  29973.  
  29974.  
  29975. Conclusion
  29976.  
  29977.  
  29978. The C Communications Toolkit is one of the most comprehensive packages I've
  29979. come across. The CCT price compares well with other packages, and the tutorial
  29980. text and example programs are sufficient for even a novice C programmer. The
  29981. function set is adequate for developing a commercial quality terminal program
  29982. and includes full library source. In file-transfer protocols, the CCT performs
  29983. as well as existing communications programs such as ProcommPlus. If you plan
  29984. to use an intelligent multi-port serial board, be sure to contact the vendor
  29985. to determine exactly what level of support is available. For many
  29986. communication applications, the C Communications Toolkit may be all the help
  29987. you need.
  29988.  
  29989.  
  29990. Bibliography
  29991.  
  29992.  
  29993. Campbell, Joe. C Programmer's Guide to Serial Communications. Indianapolis:
  29994. Howard W. Sams & Co., 1987. This text is loaded with useful commentary on
  29995. Hayes Smartmodem programming, UART control, XModem file transfer, and CRC
  29996. calculations. Its hallmark is a device-independent library of serial I/O
  29997. functions. Campbell is also the author of The RS-232 Solution.
  29998. Forsberg, Chuck. DSZ -- a ZMODEM-90TM File Transfer Program. Portland, OR:
  29999. Omen Technology Inc., 1990. This is the user's manual for Forsberg's
  30000. implementation of the Z-Modem protocol. This file is available in the file
  30001. DSZ1190.ZIP on my BBS.
  30002. Volkman, Victor. "TTYTALK: How Serial Telecommunication Works." The C Gazette
  30003. (Summer 1988): pp 4-27. This article introduces TTYTALK, a C program CROSSTALK
  30004. XVI compatible terminal program in C. Features include filtering, ASCII file
  30005. transfer, command language, and dialing. Complete source code for this program
  30006. is available in the file CGAZV3N1.ZIP on my BBS.
  30007.  
  30008.  
  30009. Acknowledgements
  30010.  
  30011.  
  30012. PCBoard is a registered trademark of Clark Development Corporation.
  30013. ProcommPlus is registered trademark of Data-Storm Technologies, Inc. DESQview
  30014. is a registered trademark of Quarterdeck Office Systems, Inc. 
  30015. Magna Carta Software, Inc.
  30016. P.O. Box 475594
  30017. Garland, TX 75047-5594
  30018. Phone: (214) 226-6909
  30019. BBS: (214) 226-8088 (1200/2400 baud)
  30020. Figure 1 Source and Executable Sizes
  30021.  "TTYTALK Terminal" CCT Equivalent to
  30022.  Benchmark by VRV TTYTALK (CCT08.C)
  30023. -------------------------------------------------------------
  30024. Source size (.C) 23K 18K
  30025. Executable size (.EXE) 17K 49K
  30026. Figure 2 Effective File Transfer Rates at 2400 Baud
  30027.  ProcommPlus CCT Examples
  30028.  File Transfer by DataStorm #8 and #9
  30029. ---------------------------------------------
  30030. XModem-1K Send 226 cps 216 cps
  30031. XModem-1K Receive 206 cps 205 cps
  30032. Figure 3 Effective File Transfer Rates at 9600 Baud
  30033.  ProcommPlus CCT Examples
  30034.  File Transfer by DataStorm #8 and #9
  30035. ---------------------------------------------
  30036. XModem-1K Send 949 cps 905 cps
  30037. XModem-1K Receive 857 cps 723 cps
  30038.  
  30039.  
  30040.  
  30041.  
  30042.  
  30043.  
  30044.  
  30045.  
  30046.  
  30047.  
  30048.  
  30049.  
  30050.  
  30051.  
  30052.  
  30053.  
  30054.  
  30055.  
  30056.  
  30057.  
  30058.  
  30059.  
  30060.  
  30061.  
  30062.  
  30063.  
  30064.  
  30065.  
  30066.  
  30067.  
  30068.  
  30069.  
  30070.  
  30071.  
  30072.  
  30073.  
  30074.  
  30075.  
  30076.  
  30077.  
  30078.  
  30079.  
  30080.  
  30081.  
  30082.  
  30083.  
  30084.  
  30085.  
  30086.  
  30087.  
  30088.  
  30089.  
  30090.  
  30091.  
  30092.  
  30093.  
  30094.  
  30095.  
  30096.  
  30097.  
  30098.  
  30099.  
  30100.  
  30101.  
  30102.  
  30103.  
  30104.  
  30105.  
  30106.  
  30107.  
  30108.  
  30109.  
  30110.  
  30111.  
  30112. The Complete C++ Primer
  30113.  
  30114.  
  30115. Sam Hobbs
  30116.  
  30117.  
  30118. Sam Hobbs is a nuclear engineer and project manager for GDS Associates. He has
  30119. been involved with computers since 1966 and is an avid C fan. He assisted in
  30120. the development of the Powerpro daily load forecasting and energy resource
  30121. scheduling software package and the data conversion for the GrayBase nuclear
  30122. power plant monthly operating report history database. Readers may contact Sam
  30123. at GDS Associates, Inc., Suite 720, 1850 Parkway Place, Marietta, GA 30067,
  30124. (404) 425-8100.
  30125.  
  30126.  
  30127. This is an excellent book for several different audiences. First for newcomers
  30128. to object-oriented programming, this book provides a gentle introduction with
  30129. a welcome lack of hype. Second, the book is a thorough introduction to the C+
  30130. + programming language. There is no implicit assumption that the reader is an
  30131. advanced C programmer, but a sufficient knowledge to read simple, non-obscure
  30132. C code is a practical requirement. A reader with little knowledge of C but a
  30133. reasonable familiarity with some other procedural structured language can work
  30134. through most of the book without any problems, but in one or two places there
  30135. are short segments of code where not knowing C is a problem. The most
  30136. prominent example is a brief section where the standard macros are used for a
  30137. function with a variable number of arguments.
  30138. The principal virtue of this book is its simplicity and clarity. The
  30139. discussion of reference variables is especially clear. When there are
  30140. lingering questions, the authors nearly always provide an answer within a page
  30141. or two. Most of the time, the question is explicitly anticipated by the
  30142. authors with a brief statement that the material will be covered within a few
  30143. pages or in the next section of the book.
  30144. Examples are chosen for their usefulness in explaining concepts in C++ and not
  30145. because they are trendy or demonstrate how clever the authors are. As a
  30146. result, most of the examples demonstrate how to use C++ in a very elementary
  30147. way. The book is not a major repository of code which will be adopted by
  30148. readers and used again and again; it is a place where readers will get the
  30149. hard questions answered very clearly. Nevertheless, there are sections where
  30150. the code demonstrates and suggests useful techniques that would not be
  30151. immediately obvious to a beginning C++ programmer. Simple versions of iterator
  30152. functions, using classes to define menus, and a memory buffer class
  30153. demonstrate friends and inheritance. Function overloading and virtual
  30154. functions are discussed with very simple-minded examples using several
  30155. functions with one or two lines of code each and discussions of what overall
  30156. program behavior results and why. Although these examples are simple almost to
  30157. an extreme, the resulting clarity will serve the reader who really needs a
  30158. primer far better than flashy and complex examples that are poorly understood.
  30159. Other than a very brief mention in Chapter one, the C++ input/output stream
  30160. operators are not discussed until Chapter 10. This contrasts to the approach
  30161. in many other introductory C++ books where the stream operators are introduced
  30162. early and used intensively from then on. Each approach has advantages, but for
  30163. an introductory text on an evolutionary language there are substantial
  30164. advantages to not introducing an entire new notation from the beginning. By
  30165. deferring the discussion until near the end of the book, the myriad of
  30166. questions on how and why the operators are used as they are can be treated
  30167. naturally and without confusion.
  30168. The book makes other departures from the customary organization of
  30169. introductory books on C++. Objects and classes are introduced in Chapters two
  30170. and three instead of near the middle or end of the book. The book then
  30171. discusses new syntax and language features as they are introduced in light of
  30172. the underlying rationale of C++ which is to add class and Object-Oriented
  30173. extensions to the C language.
  30174. Two things are wrong with the book. First, the book is overpriced at $45.00.
  30175. The pricing is consistent with pricing for books that are an academic
  30176. introduction to a programming language, and indeed this book can serve that
  30177. purpose well and with no apologies, but its primary market is likely to be
  30178. broader and more diversified. A lower and more competitive price would make it
  30179. a must have. The second fault is that the book does not cover version 2 of
  30180. C++, except in scattered brief mentions throughout the text and in a short
  30181. appendix. This fault is less serious, given the introductory nature of the
  30182. book. A beginning C++ programmer can learn more than enough to graduate to
  30183. vendor manuals or to more sophisticated materials by studying this book. I
  30184. highly recommend this one despite the price disadvantage.
  30185. The Complete C++ Primer
  30186. Keith Weiskamp and
  30187. Bryan Flamig
  30188. Academic Press, 1990
  30189. $45.00, 524 pages
  30190. ISBN 0-12-742687-6
  30191.  
  30192.  
  30193.  
  30194.  
  30195.  
  30196.  
  30197.  
  30198.  
  30199.  
  30200.  
  30201.  
  30202.  
  30203.  
  30204.  
  30205.  
  30206.  
  30207.  
  30208.  
  30209.  
  30210.  
  30211.  
  30212.  
  30213.  
  30214.  
  30215.  
  30216.  
  30217.  
  30218.  
  30219.  
  30220.  
  30221.  
  30222.  
  30223.  
  30224.  
  30225.  
  30226.  
  30227.  
  30228.  
  30229.  
  30230.  
  30231. The Art of Human-Computer Interface Design
  30232.  
  30233.  
  30234. Tom Rombouts
  30235.  
  30236.  
  30237. Tom Rombouts holds a bachelor's degree in Telecommunication and works in
  30238. database software development for Ashton-Tate in Torrance, California.
  30239. Although he loved the above book, he still doesn't like to use mice. He can be
  30240. reached on USENET as tomr@ashtate. A.-T. com.
  30241.  
  30242.  
  30243. The Art of Human-Computer Interface Design makes almost no mention of the C
  30244. programming language. Yet it is also one of the most valuable books that a C
  30245. programmer, as a software professional, could possibly read.
  30246. Originally developed by the Human Interface Group at Apple Computer, this
  30247. 540-page volume is a collection of more than 50 never-before-published pieces
  30248. by a virtual Who's Who of leading user interface experts. Authorites from
  30249. related non-computer fields are also included.
  30250. The material spans the spectrum of user interface history, philosophy, theory,
  30251. and technique. Anyone who reads this book will realize that user interface is
  30252. a far deeper subject than deciding whether on-screen menu choices should use
  30253. numbers or letters.
  30254. The selections range from formal papers, to interviews, to light anecdotal
  30255. ramblings. Despite the impressive credentials of the many authors, every page
  30256. is readable and often entertaining. (A tribute to the non-jargon tone of the
  30257. book is that the otherwise common abbreviation "UI" (user interface) is never
  30258. used.)
  30259. To reduce the anthology-like nature of the book, the manuscripts were shared
  30260. and reviewed by all the contributors before publication. Thus,
  30261. cross-references to other articles in the volume appear frequently.
  30262. The book is organized into five sections of related contributions. Each
  30263. section has a brief introduction. Introductory and closing remarks for the
  30264. entire book also appear. Twenty pages of references, a contributors' gallery,
  30265. and both a subject and a name index are included.
  30266. It is impossible to do justice to such a mass of knowledge in this short
  30267. space. I hope a brief description of each major section and a selected item or
  30268. two from each will convey at least a rough sense of the book's overall tone.
  30269. The first section, "Creativity and Design," introduces the broad principles,
  30270. techniques, and problems of interface design. Some of the material is tutorial
  30271. or historical, while other parts provide a behind-the-scenes look at software
  30272. design.
  30273. From this group, Scott Kim's "Interdisciplinary Cooperation" is a remarkable
  30274. piece from which anyone in a technical field can learn. In it he illustrates
  30275. how people from different disciplines (such as a graphic designer and a
  30276. programmer) invariably have different backgrounds and values, and thus will
  30277. almost always have at least some difficulty communicating with each other. He
  30278. then outlines some specific steps to improve cooperation across such
  30279. boundaries.
  30280. The second section, "Users and Contexts," examines the range of users and
  30281. situations involved in computer interaction. Educational, recreational, and
  30282. productivity settings are all examined.
  30283. Of these, "Koko's Mac II" stands alone. This is a fascinating look at how
  30284. northern California's celebrated "talking" gorilla, Koko, actually learned to
  30285. use a specially reinforced and programmed Mac II. (The picture on page 96 of
  30286. the furry and massive Koko seated at the terminal will be instantly understood
  30287. by anyone who has ever worked in end user support!)
  30288. The next section, "Sermons," is a collection of candid, opinionated essays by
  30289. some of the most notable names in the user interface field. Works appear by
  30290. Alan Kay, Donald A. Norman, Ben Schneiderman, Jean-Louis Gassee, Timothy
  30291. Leary, Ted Nelson, and Nicholas Negroponte.
  30292. Not to be missed is Timothy Leary's piece, which opens by comparing the
  30293. effects of LSD to future software interface designs as analogous methods of
  30294. changing human consciousness. The reader can decide whether this essay
  30295. presents a brilliant vision of the future or is merely a new form of
  30296. psycho/techo-babble.
  30297. The fourth section, "Technique and Technology," discusses specific advanced
  30298. techniques such as animation, color, hyper-text, external input devices, voice
  30299. interaction, and creating the illusion of reality.
  30300. Two articles in this group, one on gestures in human communication and another
  30301. on difficulties with audio computer interaction, together demonstrate how very
  30302. different today's human-computer interaction is from normal human
  30303. conversation.
  30304. The fifth and final section, "New Directions," focuses on the future of user
  30305. interface directions with terms such as "agents," "guides," and "cyberspace."
  30306. This is perhaps the most fascinating material, because it touches on ideas
  30307. that to date have only been seen in scien