CS360 Lecture notes -- Echo

  • Jian Huang
  • CS360
  • Directory: ~huangj/cs360/notes
  • Lecture notes: http://www.cs.utk.edu/~huangj/cs360/360/notes/Echo/echo.html

    The Real Deal

    Now that you have gotten all the necessary warming up exercises, you are ready to take a look at a real world program, done by professionals. There are a lot to be learned. So take heed. Our example is the code implementing echo (see man echo).

    /*
     * Copyright (c) 1989, 1993
     *	The Regents of the University of California.  All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     * 1. Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     * 2. Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     * 3. All advertising materials mentioning features or use of this software
     *    must display the following acknowledgement:
     *	This product includes software developed by the University of
     *	California, Berkeley and its contributors.
     * 4. Neither the name of the University nor the names of its contributors
     *    may be used to endorse or promote products derived from this software
     *    without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     * SUCH DAMAGE.
     */
    
    #include < sys/cdefs.h >
    #ifndef lint
    __COPYRIGHT(
    "@(#) Copyright (c) 1989, 1993\n\
    	The Regents of the University of California.  All rights reserved.\n");
    #endif /* not lint */
    
    #ifndef lint
    #if 0
    static char sccsid[] = "@(#)echo.c	8.1 (Berkeley) 5/31/93";
    #else
    __RCSID("$NetBSD: echo.c,v 1.7 1997/07/20 06:07:03 thorpej Exp $");
    #endif
    #endif /* not lint */
    
    #include < stdio.h >
    #include < stdlib.h >
    #include < string.h >
    
    int	main __P((int, char *[]));
    
    int
    main(argc, argv)
    	int argc;
    	char *argv[];
    {
    	int nflag;
    
    	/* This utility may NOT do getopt(3) option parsing. */
    	if (*++argv && !strcmp(*argv, "-n")) {
    		++argv;
    		nflag = 1;
    	}
    	else
    		nflag = 0;
    
    	while (*argv) {
    		(void)printf("%s", *argv);
    		if (*++argv)
    			putchar(' ');
    	}
    	if (!nflag)
    		putchar('\n');
    
    	exit(0);
    }
    
    

    Note, a trick is used to deal with main. This is to make sure no matter how old a compiler is used, including those predating ANSI C'1987, this code will compile and run. This issue typically is not considered by application programers. Here __P is simply a macro, defined to be return NULL or do nothing. i.e. something like:

    #define __P(s) ()
    #define __P(s) s
    

    Of course, developers must always aim for readable code. Since __P is meant as a trick facade function prototype, __P are instead defined as the following:

    #define __P(protos) ()
    #define __P(protos) protos
    

    The next step is to go through the body of the code and see how this concise piece of code elegantly handles the task.

    {
    	int nflag;
    
    	/* This utility may NOT do getopt(3) option parsing. */
    	if (*++argv && !strcmp(*argv, "-n")) {
    		++argv;
    		nflag = 1;
    	}
    	else
    		nflag = 0;
    
    	while (*argv) {
    		(void)printf("%s", *argv);
    		if (*++argv)
    			putchar(' ');
    	}
    	if (!nflag)
    		putchar('\n');
    
    	exit(0);
    }
    

    Note how error checking is done. Echo first verifies that an argument *argv exists, before use it's content for comparison. We know that return values are a very important source of information. Usually you need to use such information. But to make sure that you are not simply missing it, make such intentional ignoring explicit, just like how the return value is cast to void.

    		(void)printf("%s", *argv);
    

    But what if printf indeed returns 0, which means a failure to write? Or putchar returns EOF due to the same reason? This scenario could happen, for instance when the output is redirected to a file and you have already run out of disk space. Then what happens?


    Non Functional Contents

    The top of the code contained the license under which the code is released. This is an important issue given today's prevalent movenent of open source development. We will however save that discussion towards the end of the semester.

    What confuses/intrigues novices are the few lines of code right underneath the license statement.

    #include < sys/cdefs.h >
    #ifndef lint
    __COPYRIGHT(
    "@(#) Copyright (c) 1989, 1993\n\
    	The Regents of the University of California.  All rights reserved.\n");
    #endif /* not lint */
    
    #ifndef lint
    #if 0
    static char sccsid[] = "@(#)echo.c	8.1 (Berkeley) 5/31/93";
    #else
    __RCSID("$NetBSD: echo.c,v 1.7 1997/07/20 06:07:03 thorpej Exp $");
    #endif
    #endif /* not lint */
    

    First let's simply things a bit. lint is a tool to check for potential problems, points of warnings, in code. It does not like certain programming behavior. However, that's not too critical, since echo is not that complicated. So, taking out those lint related items, as well as a few others, we have:

    #include < sys/cdefs.h >
    __COPYRIGHT(
    "@(#) Copyright (c) 1989, 1993\n\
    	The Regents of the University of California.  All rights reserved.\n")
    	
    __RCSID("$NetBSD: echo.c,v 1.7 1997/07/20 06:07:03 thorpej Exp $");
    

    Looking much better. Now, take a look in < sys/cdefs.h >, you will know that __COPYRIGHT() and __RCSID() are just some macros that declare a string as a global variable. Simple enough. But why?

    Here is the systems programmer's point of view again. Mechanism, not policy. So other people will use your code. How do they know which version they are using? Who has the copyright? etc., if all they have are the executable?

    You say, just run it and click on that "About" button. :) But what if the executable is to be used as a device driver of a novel storage card that must be invoked by a system call? This mess can go on and on. We don't want to define what the policy is. Then it looks awfully convenient to just put those information into the object files and executables.

    Then, you can look at them using simple commands like strings and ident, etc.


    What is this line then?

    "$NetBSD: echo.c,v 1.7 1997/07/20 06:07:03 thorpej Exp $"
    

    It's not something that you type in. Though I DID see students typing that lines like that into their code just to look cool. Young folks do have a lot of free time, it seems.

    Those are from version control tools, in particular RCS (revision control system). All software professionals use version control tools of a certain kind. RCS is the root of them all. Later tools are more function-packed and more sophisticated, however the basics are indeed exactly the same. Learning RCS is a good start. Starting from Lab3 assignments, all of your code must be maintained through RCS. You must also make your makefile capable of checking out code from RCS through an option. It should work like this:

    UNIX>make checkout
    UNIX>make all
    

    For more information, read man rcsintro and man ci and man co.


    Lastly, although we are learning about Unix programming, portability is still a great thing to take care. Whenever you include header files under < sys/... >, it implies Unix-flavor only. A more portable version of echo is provided here, as well as a somewhat modern version of that.