home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_200 / 228_01 / bdsmark.doc < prev    next >
Text File  |  1987-07-31  |  19KB  |  346 lines

  1.  
  2.  
  3.                                  BDSMARK.C
  4.  
  5.  
  6.  
  7.                                David D. Clark
  8.                             507 N. Division St.
  9.                             Bristol, IN   46507
  10.  
  11.  
  12.  
  13.         The Problem
  14.  
  15.           BD Softwares' C compiler was one of the first C compilers
  16.         for micro computers and is still one of the best.  A lot of
  17.         good public domain and commercial software (like my favorite
  18.         word processing program and another of my favorite C
  19.         compilers) has been written with it.  It is a fast
  20.         development system and produces fast and compact code.
  21.  
  22.           Unfortunatley, it is only available for CP/M systems.  If
  23.         you have a computer running a different operating system or
  24.         CPU and you want to port some of the excellent software
  25.         written in BDS C, you have to resort to a different compiler
  26.         on the new machine.  That is not the only problem however.
  27.         BDS C prior to version 1.6 is not a particularly portable
  28.         version of C because a number of the "standard" library
  29.         routines are not standard as used in "The C Programming
  30.         Language" (1) (hereafter referred to as "K&R") or C compilers
  31.         running on UNIX.  (Some of these routines do not really
  32.         exist in the UNIX C standard library but are simulations of
  33.         UNIX system calls on a CP/M system.)
  34.  
  35.           When I started working with a Zenith Z-150 running MS-DOS,
  36.         I was faced with the formidable task of porting a lot of my
  37.         favorite public domain and self-written programs, written in
  38.         BDS C, to the new system.  Too, now that version 1.6 has
  39.         arrived, with its more standard i/o library, I have a lot of
  40.         code that needs translation to the new version as well.
  41.  
  42.  
  43.         The Nonstandard "Standard" Functions
  44.  
  45.           After a bit of careful reading, I determined that there
  46.         were 9 functions in the old (pre version 1.6) BDS C standard
  47.         library that were not implemented as per K&R or, if K&R was
  48.         not clear about a particular function, the UNIX version 7 C
  49.         compiler.  Some of the differences are trivial, others are
  50.         not.  These functions, and the differences between the BDS
  51.         version and the standard version, are listed below:
  52.  
  53.         creat          The BDS C version requires only one
  54.                        parameter, the file name, while the K&R
  55.                        version requires two: the file name and a
  56.                        protection mode.  Admittedly, the protection
  57.                        mode argument is useless in a CP/M
  58.                        environment, but may be needed under a
  59.                        different operating system.  This function is
  60.                        the same in the new version of the BDS
  61.                        library.
  62.  
  63.         exit           According to K&R, exit will flush any open
  64.                        buffered files before closing them.  BDS C
  65.                        does not flush the file buffer before closing
  66.                        the file.
  67.  
  68.         fgets          Again, there is a difference in the number
  69.                        and meaning of parameters accepted by the old
  70.                        BDS C function and the K&R version.  The old
  71.                        BDS C function expects to be called with a
  72.                        pointer to the string to fill and a pointer
  73.                        to a BDS C specific file buffer associated
  74.                        with the input device.  The K&R version also
  75.                        expects to be called with a pointer to the
  76.                        string to be filled, but followed by the
  77.                        maximum number of characters to fill the
  78.                        string with and a pointer to a FILE variable
  79.                        associated with the input device.  This
  80.                        version of this function included with
  81.                        version 1.6 of BDS C is K&R compatible.
  82.  
  83.         fopen          In this case, the arguments and the value
  84.                        returned by the functions are different.  The
  85.                        old BDS library function expects to be called
  86.                        with a pointer to the name of the file and a
  87.                        pointer to the buffer variable to be
  88.                        associated with the open file.  It returns an
  89.                        integer representing the file descriptor of
  90.                        the opened file or -1 if an error occurs
  91.                        during the attempt to open the file.  The K&R
  92.                        version also expects a pointer to the name of
  93.                        the file but expects a pointer to a character
  94.                        representing the mode (read, write or append)
  95.                        in which the file is to be opened.  The
  96.                        function returns a pointer to a FILE variable
  97.                        to be associated with the open file or NULL
  98.                        in case of an error.  The new BDS C 1.6
  99.                        function is compatible.
  100.  
  101.         getc           The new version of this function differs from
  102.                        the old in that it now differentiates between
  103.                        text and binary modes.  In text mode, CP/M
  104.                        line end sequences (a carriage return/line
  105.                        feed pair) is translated to a single line
  106.                        feed character.
  107.  
  108.         getchar        The new version of this function
  109.                        distinguishes between text and binary modes
  110.                        like getc.
  111.  
  112.         putc           The new version of this function also
  113.                        differentiates between binary and text
  114.                        modes.
  115.  
  116.         puts           The difference here is small but can greatly
  117.                        affect screen displays that use the
  118.                        function.  The UNIX version of the function
  119.                        always appends a new line character to the
  120.                        end of the string it prints to the standard
  121.                        output.  The BDS function does not.
  122.  
  123.         read           In this case, one of the arguments in the BDS
  124.                        version of the function represents the number
  125.                        of 128 byte blocks to be read.  This is
  126.                        reasonable in a CP/M environment where the
  127.                        smallest unit of disk I/O is 128 bytes.  The
  128.                        K&R version of the function expects the
  129.                        corresponding argument to represent the
  130.                        number of individual bytes to be read.  The
  131.                        functions return values representing
  132.                        different things too.  The BDS function
  133.                        returns the number of blocks read while the
  134.                        K&R version returns the number of bytes.
  135.                        This function has not changed in the new
  136.                        version of the BDS library.
  137.  
  138.         tolower        This function is somewhat ambiguously defined
  139.                        in K&R. The UNIX version (a macro actually)
  140.                        does the conversion regardless of whether of
  141.                        not the argument was upper case to begin
  142.                        with.  The BDS function checks the argument
  143.                        to determine if it is upper case before
  144.                        performing the conversion.  Most other
  145.                        microcomputer versions of C do the same check
  146.                        before conversion.
  147.  
  148.         toupper        The same problem occurs with this function as
  149.                        with tolower.  UNIX does not check the case
  150.                        of the argument before converting while BDS C
  151.                        does.  This seems like a very innocuous
  152.                        difference, but it is the one that most often
  153.                        caused me trouble.  Again, most microcomputer
  154.                        versions of C now do the case checking before
  155.                        conversion
  156.  
  157.         write          Once more, there is a difference in the
  158.                        parameters expected by the two versions of
  159.                        the function.  The difference is the same as
  160.                        that with read.  The K&R version expects the
  161.                        number of bytes to write while the BDS
  162.                        version expects the number of 128 byte blocks
  163.                        to transfer.  The values returned by the
  164.                        functions are different too.  The K&R
  165.                        function returns the number of bytes actually
  166.                        written while the BDS version returns the
  167.                        number of blocks.  This function is the same
  168.                        in the new version of the BDS library.
  169.  
  170.           Changing the calls when switching compilers is not really
  171.         too difficult.  The read and write functions sometimes cause
  172.         problems if numeric calculations are based on the values
  173.         they return.  The real problem is finding all of the calls
  174.         to this set of functions in a long program and taking
  175.         corrective action.  When I first started porting some
  176.         lengthy programs, attempting to find every single instance
  177.         of calls to these functions just about drove me up the
  178.         walls.  In my desire to finish some of the conversions
  179.         before the turn of the century with my normal cheerful
  180.         demeanor still intact, I decided to let the computer help
  181.         me.
  182.  
  183.  
  184.         The Program
  185.  
  186.           Listing 1 shows my solution to the problem.  Bdsmark.c
  187.         reads a BDS C program as input and writes the program to its
  188.         output.  Along the way, it examines each legal C identifier
  189.         existing outside of a comment or constant string.  If the
  190.         identifier matches one of those in a list of nonstandard
  191.         function names, the identifier is "marked" in the output
  192.         file.  The marking consists of preceeding and suceeding
  193.         double carat marks ("^^").  This marking not only makes the
  194.         function names easy to spot, it also makes the program
  195.         uncompilable.  This prevents compilation and subsequent
  196.         subtle execution errors in programs where some of the names
  197.         might have been missed by the programmer.  The program does
  198.         not do any checking for legal C syntax.  It assumes that the
  199.         input file contains a legal, compilable C program.  If the
  200.         program contains syntax errors of various sorts, it is
  201.         possible that the program will be fatal to bdsmark.
  202.  
  203.           The program is invoked from the command line by typing the
  204.         program name followed two file names.  The first file name
  205.         should be the file containing the C language source file to
  206.         mark.  The second file name should be the name of a file to
  207.         write the marked version of the program to.  If a file with
  208.         the same name already exists, it will be overwritten.  The
  209.         main function attempts to open the input and output files
  210.         specified on the command line.  If successful, it passes
  211.         control to the function scanner to handle just about
  212.         everything else.  If the requested files cannot be opened,
  213.         an error message is printed and the program terminates.  If
  214.         the number of command line arguments is incorrect, a short
  215.         usage message is printed and the program terminates.
  216.  
  217.           The function scanner does most of the real work of the
  218.         program.  It simply reads characters from the input file,
  219.         checks for some special types of characters, and writes
  220.         characters to the output file.  Scanner looks for four types
  221.         of C program constructs: comments, string constants,
  222.         character constants and identifiers.  Checking for comments
  223.         and string and character constants is only needed if you
  224.         want to avoid marking function names which appear in
  225.         comments or string constants.
  226.  
  227.           If a potential comment is detected, by reading a '/'
  228.         character, control is passed to the function commenter to
  229.         handle the rest of the comment.  The commenter function
  230.         first checks for the '*' after the slash to indicate that a
  231.         comment has really been detected.  If not, control is
  232.         returned to scanner.  If a comment is started, commenter
  233.         passes characters from the input to the output file until a
  234.         matching end-of-comment token ("*/") is detected.  After the
  235.         comment has been completely scanned, control is returned to
  236.         scanner.  Commenter will accept UNIX style nested comments
  237.         of the form:
  238.  
  239.              /* printf("The number is %d/n", i);   /* a comment */
  240.  
  241.           Once the initial open-comment token has been detected, the
  242.         function will continue to pass characters through until a
  243.         close-comment token is detected.  As such, it will not
  244.         handle correctly comments of the form allowed by some
  245.         compilers:
  246.  
  247.              /* printf("The number is %d/n", i);  /* a comment */ */
  248.  
  249.           To avoid marking and altering function names that appear
  250.         in string constants, scanner must be able to recognize and
  251.         pass string constants unchanged.  This is not quite as
  252.         straightforward as it seems.  The tricky part is to catch
  253.         double quote characters embedded in the string by use of the
  254.         escape sequence '\"'.  That part of the program looks for a
  255.         backslash character and, if one is detected, it reads the
  256.         next character and writes it without regard for what it is.
  257.         Otherwise, the program just looks for the terminal double
  258.         quote of the string.
  259.  
  260.           One of the consequences of requiring the program to handle
  261.         string constants correctly is that it must handle character
  262.         constants as well.  Otherwise, a double quote used in a
  263.         character constant would incorrectly trigger the part of the
  264.         program that handles string constants.  Again, escape
  265.         sequences within the character constant must be recognized
  266.         and handled correctly.  Character constants may therefore
  267.         contain from one to four characters (in the case of an octal
  268.         constant) between the single quote delimiters.  The code in
  269.         the listing looks for and recognizes these different
  270.         possibilities.
  271.  
  272.           The source code for bdsmark is actually a fairly good test
  273.         file to run the program on.  Since it must be able to
  274.         recognize the special cases, the source contains many of
  275.         those special cases itself.
  276.  
  277.           Most of the processing takes place when a legal C
  278.         identifier is detected.  If the function iscfsym determines
  279.         that a character is a legal one to start a C identifier
  280.         with, that token is copied into the variable "token".
  281.         Copying stops when the function iscsym determines that the
  282.         current scan character is not a valid component of a C
  283.         identifier or if the token becomes longer than TOKLEN - 1
  284.         (since none of the function names we are interested in are
  285.         that long).  In this program, the functions iscfsym and
  286.         iscsym could be replaced by the functions isalpha and
  287.         isalnum since none of the nonstandard function names start
  288.         with an underscore.  I used the two longer functions in case
  289.         I make changes to the array of function names some time in
  290.         the future.
  291.  
  292.           Once an identifier has been read into "token", the
  293.         function tsttoken is called to see if it matches one of the
  294.         function names in the initialized external variable nonstd.
  295.         Nonstd is an array of pointers to characters, just like the
  296.         argv argument to the main function, each of which points to
  297.         an initialized array of characters containing the name of
  298.         one of the nonstandard functions.  The names are arranged in
  299.         alphabetical order.  Since there are only nine names,
  300.         tsttoken does a simple linear search of the array.  If
  301.         tsttoken finds a match, scanner writes a marked version of
  302.         the identifier to the output file, otherwise, the token is
  303.         written unchanged.
  304.  
  305.           You may notice in several places in the program that local
  306.         variables are declared with the static storage class.  For
  307.         the compilers I use, this produces smaller and faster code.
  308.         Others may prefer to use auto or register variables.
  309.         Another seeming peculiarity in the code is the use of
  310.         sequences of fputs and putc functions where a single printf
  311.         would seem more logical.  I chose the longer sequence of
  312.         calls to individual functions exactly to avoid using a
  313.         printf.  Using printf would increase the size of the program
  314.         by 5K to 10K, depending on the compiler, because most of the
  315.         floating point and long integer library routines would be
  316.         pulled in as well.  As is, the program compiles to a code
  317.         file of from 4K to 10K, depending on the compiler.
  318.  
  319.           The program can be compiled "as is" on a number of
  320.         compilers including Eco-C88 under MS-DOS and Aztec C II
  321.         under CP/M.  If you remove the preprocessor directive to
  322.         include the file "ctype.h", the program can be compiled by
  323.         Q/C and Eco-C for the Z80 under CP/M.  Changes to make the
  324.         program compatible with other compilers should be easy.
  325.  
  326.  
  327.         Conclusion
  328.  
  329.           Bdsmark.c is a simple program that handles a tedious job.
  330.         By marking all occurrences of calls to BDS C library
  331.         functions that are not implemented in a standard manner, the
  332.         program can assist in porting some of the large number of
  333.         programs written in BDS C to other operating systems and
  334.         processors.  Since the marked programs are uncompilable,
  335.         there is no chance of subtle bugs sneaking into a program
  336.         just because an occurence of one of the nonstandard
  337.         functions slipped past the programmer.  The programmer must
  338.         at least look at the marked funtions before the program can
  339.         be compiled.  The program itself is fairly portable.  It
  340.         should be possible to use it with a variety of C compilers
  341.         with minimal changes.
  342.  
  343.         ----------
  344.         1. Brian W. Kernighan and Dennis M. Ritchie, "The C
  345.         Programming Language", Prentice-Hall, 1978
  346.